Snap for 5735642 from 2bff65eed14b618c746b2df5e437a4211ed1be8a to sdk-release

Change-Id: If5ef62d08c1031e41c8475026084bb9459e301c5
diff --git a/.gitignore b/.gitignore
index cbd8ca8..fabb38f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,10 +30,6 @@
 
 gen-external-apklibs
 
-bazel-bin
-bazel-genfiles
-bazel-out
-bazel-testlogs
-bazel-dagger
+/bazel-*
 
 *.pyc
diff --git a/.travis.yml b/.travis.yml
index 439bcec..9015f2e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: android
 
 os: linux
-dist: precise
+dist: trusty
 sudo: required
 addons:
   apt:
@@ -53,7 +53,7 @@
     # permission to push to the repo.
     - secure: "UpTUhCQzAGbr5JetRg2GZxp/dPDep/7Il3yGeyDECopciWdx41OPk/QNqAXBhNtKuEaMVsmASyoteuhgaTryQdV4qUIGVOMhES6kbOlYy3nwK44VdsNeeepwVospyDyZbxMtXq5LuHWuTADmAl1mdjNPNoziXc523zjnUzUx/EQ="
     - JDK_FOR_PUBLISHING: *jdk_for_publishing
-    - BAZEL_VERSION="0.19.2"
+    - BAZEL_VERSION="0.24.1"
 
 after_success:
   - util/generate-latest-docs.sh
diff --git a/Android.bp b/Android.bp
index b8ba1ab..a0f4055 100644
--- a/Android.bp
+++ b/Android.bp
@@ -88,7 +88,6 @@
     jars: ["lib/javax.inject-1.jar"],
 }
 
-
 java_import_host {
     name: "dagger2-bootstrap-compiler-jar",
     jars: ["java/dagger/internal/codegen/bootstrap_compiler_deploy.jar"],
@@ -148,22 +147,18 @@
     name: "dagger2-compiler",
     processor_class: "dagger.internal.codegen.ComponentProcessor",
     generates_api: true,
-
-    javacflags: [
-        // Required for use of javax.annotation.Generated per http://b/62050818
-        "-J--add-modules=java.xml.ws.annotation",
-    ],
-
     use_tools_jar: true,
 
     srcs: [
         "java/dagger/internal/codegen/**/*.java",
+        "java/dagger/internal/codegen/**/*.proto",
 
         "java/dagger/model/*.java",
         "java/dagger/spi/*.java",
     ],
 
     exclude_srcs: [
+        "java/dagger/internal/codegen/BindingGraphStatisticsCollector.java",
         "java/dagger/internal/codegen/DaggerKythePlugin.java",
     ],
 
@@ -199,6 +194,11 @@
         "dagger2-bootstrap-compiler",
     ],
 
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src/"],
+    },
+
     java_version: "1.8",
 }
 
diff --git a/BUILD b/BUILD
index 651372a..8becb3e 100644
--- a/BUILD
+++ b/BUILD
@@ -63,6 +63,9 @@
         "//java/dagger/internal/codegen:processor",
         "//java/dagger/internal/codegen:validation",
         "//java/dagger/internal/codegen:writing",
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/internal/codegen/serialization",
         "//java/dagger/model:internal-proxies",
         "//java/dagger/errorprone",
         "@com_google_auto_auto_common//jar",
@@ -80,6 +83,9 @@
         "//java/dagger/internal/codegen:libprocessor-src.jar",
         "//java/dagger/internal/codegen:libvalidation-src.jar",
         "//java/dagger/internal/codegen:libwriting-src.jar",
+        "//java/dagger/internal/codegen/javapoet:libjavapoet-src.jar",
+        "//java/dagger/internal/codegen/langmodel:liblangmodel-src.jar",
+        # TODO(ronshapiro): is there a generated src.jar for protos in Bazel?
         "//java/dagger/errorprone:liberrorprone-src.jar",
     ],
 )
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 79d185a..45f15c4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,3 +23,47 @@
 information on using pull requests.
 
 [GitHub Help]: https://help.github.com/articles/about-pull-requests/
+
+## Building Dagger
+
+Dagger is built with [`bazel`](https://bazel.build).
+
+### Building Dagger from the command line
+
+*   [Install Bazel](https://docs.bazel.build/versions/master/install.html)
+*   Build the Dagger project with `bazel build <target>`
+    *   Learn more about Bazel targets [here][bazel targets].
+    *   If you see an error similar to `ERROR: missing input file
+        '@androidsdk//:build-tools/26.0.2/aapt'`, install the missing build
+        tools version with the android `sdkmanager` tool.
+*   Run tests with `bazel test <target>`, or `bazel test //...` to run all
+    tests
+*   You can install the Dagger libraries in your **local maven repository** by
+    running the `./util/install-local-snapshot.sh` script.
+    *   It will build the libraries and install them with a `LOCAL-SNAPSHOT`
+        version.
+
+[bazel targets]: https://docs.bazel.build/versions/master/build-ref.html
+
+### Importing the Dagger project in IntelliJ/Android Studio
+
+*   Visit `Preferences > Plugins` in the IDE menu.
+    *   Search for `bazel` and install the plugin.
+    *   If no result shows up, click on `Search in repositories`, search for
+        `bazel` and install the plugin.
+*   Select `Import Bazel Project`.
+*   Input the path to the Dagger project under `workspace`, click `Next`.
+*   Select `Generate from BUILD file`, type `BUILD` in the `Build file` input,
+    click `Next`.
+*   [Android Studio only] In the `Project View` form, uncomment one of the
+    `android_sdk_platform` lines. Pick one that you have installed, then click
+    `Finish`.
+*   If you get an error on Bazel sync, `Cannot run program "bazel"`, then:
+    *   In the command line, run `where bazel` and copy the output  (e.g.
+        `/usr/local/bin/bazel`)
+    *   In Android Studio, go to `Preferences > Bazel Settings` and replace
+        `Bazel binary location` with what you just copied.
+*   Note that the first sync can take a long time. When build files are changed,
+    you can run partial syncs (which should be faster) from the file menu.
+*   [Android Studio only] To view the Dagger project structure, open the
+    `Project` view and switch the top selector from `Android` to `Project`.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..59b9e25
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "dagger2"
+description:
+    "A fast dependency injector for Android and Java."
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://dagger.dev"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/google/dagger"
+  }
+  version: "dagger-2.23.1"
+  last_upgrade_date { year: 2019 month: 6 day: 14 }
+  license_type: PERMISSIVE
+}
diff --git a/OWNERS b/OWNERS
index 22cc91a..87a5dbe 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1 @@
-paulduffin@google.com
-nfuller@google.com
+include platform/libcore:/OWNERS
diff --git a/README.md b/README.md
index 13523c0..a9ebc26 100644
--- a/README.md
+++ b/README.md
@@ -180,12 +180,8 @@
     * [Dagger development Javadocs][latestapi] (from the `master` branch
       on GitHub)
     * [Google's Dagger project site on GitHub][project]
-    * <a href="https://plus.google.com/118328287768685565185"
-         rel="publisher">Google+ Dagger Project Page</a>
-    * [Google+ Dagger Users Community][community]
   * 1.x (square/dagger)
     * [Square's original Dagger project site on GitHub][square]
-    * [Square Open Source Community][squarecommunity]
 
 
 If you do not use maven, gradle, ivy, or other build systems that consume
@@ -198,9 +194,7 @@
 
 ## Building Dagger
 
-Dagger is built with [`bazel`]. The tests can be run with `bazel test //...`.
-`util/install-local-snapshot.sh` will build all of the Dagger libraries and
-install a copy in your local maven repository with the version `LOCAL-SNAPSHOT`.
+See [the CONTRIBUTING.md docs][Building Dagger].
 
 ## License
 
@@ -218,18 +212,17 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 
-[20api]: https://google.github.io/dagger/api/2.0/
+[20api]: https://dagger.dev/api/2.0/
 [`bazel`]: https://bazel.build
 [bazel-external-deps]: https://docs.bazel.build/versions/master/external.html#depending-on-other-bazel-projects
-[community]: https://plus.google.com/communities/111933036769103367883
+[Building Dagger]: CONTRIBUTING.md#building-dagger
 [dagger-snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/dagger/
 [databinding]: https://developer.android.com/topic/libraries/data-binding/
 [gaktalk]: https://www.youtube.com/watch?v=oK_XtfXPkqw
-[latestapi]: https://google.github.io/dagger/api/latest/
+[latestapi]: https://dagger.dev/api/latest/
 [mavenbadge-svg]: https://maven-badges.herokuapp.com/maven-central/com.google.dagger/dagger/badge.svg
 [mavencentral]: https://search.maven.org/artifact/com.google.dagger/dagger
 [project]: http://github.com/google/dagger/
 [proposal]: https://github.com/square/dagger/issues/366
 [square]: http://github.com/square/dagger/
-[squarecommunity]: https://plus.google.com/communities/109244258569782858265
-[website]: https://google.github.io/dagger
+[website]: https://dagger.dev
diff --git a/WORKSPACE b/WORKSPACE
index afdcae6..8758b38 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -16,10 +16,19 @@
 
 http_archive(
     name = "google_bazel_common",
-    strip_prefix = "bazel-common-1c225e62390566a9e88916471948ddd56e5f111c",
-    urls = ["https://github.com/google/bazel-common/archive/1c225e62390566a9e88916471948ddd56e5f111c.zip"],
+    strip_prefix = "bazel-common-26011657fee96a949c66500b1662c4c7288a4968",
+    urls = ["https://github.com/google/bazel-common/archive/26011657fee96a949c66500b1662c4c7288a4968.zip"],
 )
 
 load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules")
 
 google_common_workspace_rules()
+
+# This fixes an issue with protobuf starting to use zlib by default in 3.7.0.
+# TODO(ronshapiro): Figure out if this is in fact necessary, or if proto can depend on the
+# @bazel_tools library directly. See discussion in
+# https://github.com/protocolbuffers/protobuf/pull/5389#issuecomment-481785716
+bind(
+    name = "zlib",
+    actual = "@bazel_tools//third_party/zlib",
+)
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
index 4b19fc7..21aeb46 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -1,15 +1,64 @@
 #!/bin/bash
 
+declare -A INNER
+declare -A PARAMETER
+declare -A IMPORT
+
 ANNOTATIONS=(
     org.checkerframework.checker.nullness.compatqual.NullableDecl
+    net.ltgt.gradle.incap.IncrementalAnnotationProcessor
 )
 
+PARAMETER["net.ltgt.gradle.incap.IncrementalAnnotationProcessor"]="IncrementalAnnotationProcessorType"
+IMPORT["net.ltgt.gradle.incap.IncrementalAnnotationProcessor"]="net.ltgt.gradle.incap.IncrementalAnnotationProcessorType"
+
 for a in ${ANNOTATIONS[@]}; do
     package=${a%.*}
     class=${a##*.}
     dir=$(dirname $0)/src/${package//.//}
     file=${class}.java
+    inner=${INNER[$a]}
+    parameter=${PARAMETER[$a]}
+    import=
+
+    if [ -n "${parameter}" ]; then
+	parameter="${parameter} value();"
+    fi
+
+    for i in ${IMPORT[$a]}; do
+	import="${import}import ${i};"
+    done
 
     mkdir -p ${dir}
-    sed -e"s/__PACKAGE__/${package}/" -e"s/__CLASS__/${class}/" tmpl.java > ${dir}/${file}
+    sed -e"s/__PACKAGE__/${package}/" \
+	-e"s/__CLASS__/${class}/" \
+	-e"s/__INNER__/${inner}/" \
+	-e"s/__PARAMETER__/${parameter}/" \
+	-e"s/__IMPORT__/${import}/" \
+	$(dirname $0)/tmpl.java > ${dir}/${file}
+    google-java-format -i ${dir}/${file}
 done
+
+f=$(dirname $0)/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
+cat > ${f} <<EOF
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+public enum IncrementalAnnotationProcessorType {
+  DYNAMIC
+}
+EOF
diff --git a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java
new file mode 100644
index 0000000..aa1ce76
--- /dev/null
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+  ElementType.ANNOTATION_TYPE,
+  ElementType.CONSTRUCTOR,
+  ElementType.FIELD,
+  ElementType.LOCAL_VARIABLE,
+  ElementType.METHOD,
+  ElementType.PACKAGE,
+  ElementType.PARAMETER,
+  ElementType.TYPE,
+  ElementType.TYPE_PARAMETER,
+  ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface IncrementalAnnotationProcessor {
+
+  IncrementalAnnotationProcessorType value();
+}
diff --git a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
new file mode 100644
index 0000000..83e3590
--- /dev/null
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.ltgt.gradle.incap;
+public enum IncrementalAnnotationProcessorType {
+  DYNAMIC
+}
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
index 15e3f9b..2d39b0f 100644
--- a/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
+++ b/android-annotation-stubs/src/org/checkerframework/checker/nullness/compatqual/NullableDecl.java
@@ -25,15 +25,16 @@
  * in the Android platform source tree. */
 
 @Target({
-    ElementType.ANNOTATION_TYPE,
-    ElementType.CONSTRUCTOR,
-    ElementType.FIELD,
-    ElementType.LOCAL_VARIABLE,
-    ElementType.METHOD,
-    ElementType.PACKAGE,
-    ElementType.PARAMETER,
-    ElementType.TYPE,
-    ElementType.TYPE_PARAMETER,
-    ElementType.TYPE_USE})
+  ElementType.ANNOTATION_TYPE,
+  ElementType.CONSTRUCTOR,
+  ElementType.FIELD,
+  ElementType.LOCAL_VARIABLE,
+  ElementType.METHOD,
+  ElementType.PACKAGE,
+  ElementType.PARAMETER,
+  ElementType.TYPE,
+  ElementType.TYPE_PARAMETER,
+  ElementType.TYPE_USE
+})
 @Retention(RetentionPolicy.SOURCE)
 public @interface NullableDecl {}
diff --git a/android-annotation-stubs/tmpl.java b/android-annotation-stubs/tmpl.java
index 814c143..c4df609 100644
--- a/android-annotation-stubs/tmpl.java
+++ b/android-annotation-stubs/tmpl.java
@@ -20,20 +20,24 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+__IMPORT__
 
 /* This is an annotation stub to avoid dependencies on annotations that aren't
  * in the Android platform source tree. */
 
 @Target({
-    ElementType.ANNOTATION_TYPE,
-    ElementType.CONSTRUCTOR,
-    ElementType.FIELD,
-    ElementType.LOCAL_VARIABLE,
-    ElementType.METHOD,
-    ElementType.PACKAGE,
-    ElementType.PARAMETER,
-    ElementType.TYPE,
-    ElementType.TYPE_PARAMETER,
-    ElementType.TYPE_USE})
+  ElementType.ANNOTATION_TYPE,
+  ElementType.CONSTRUCTOR,
+  ElementType.FIELD,
+  ElementType.LOCAL_VARIABLE,
+  ElementType.METHOD,
+  ElementType.PACKAGE,
+  ElementType.PARAMETER,
+  ElementType.TYPE,
+  ElementType.TYPE_PARAMETER,
+  ElementType.TYPE_USE})
 @Retention(RetentionPolicy.SOURCE)
-public @interface __CLASS__ {}
+public @interface __CLASS__ {
+  __INNER__
+  __PARAMETER__
+}
diff --git a/java/dagger/BUILD b/java/dagger/BUILD
index 5f5cfbf..3485431 100644
--- a/java/dagger/BUILD
+++ b/java/dagger/BUILD
@@ -30,7 +30,9 @@
     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"],
-    deps = ["@google_bazel_common//third_party/java/jsr330_inject"],
+    deps = [
+        "@google_bazel_common//third_party/java/jsr330_inject",
+    ],
 )
 
 pom_file(
diff --git a/java/dagger/MapKey.java b/java/dagger/MapKey.java
index 89307be..46dbf93 100644
--- a/java/dagger/MapKey.java
+++ b/java/dagger/MapKey.java
@@ -100,7 +100,7 @@
  * <p>(Note that there must be a class {@code MyMapKeyImpl} that implements {@code MyMapKey} in
  * order to call {@link Map#get(Object)} on the provided map.)
  *
- * @see <a href="https://google.github.io/dagger/multibindings#map-multibindings">Map multibinding</a>
+ * @see <a href="https://dagger.dev/multibindings#map-multibindings">Map multibinding</a>
  */
 @Documented
 @Target(ANNOTATION_TYPE)
diff --git a/java/dagger/Reusable.java b/java/dagger/Reusable.java
index f2419d5..2cb68aa 100644
--- a/java/dagger/Reusable.java
+++ b/java/dagger/Reusable.java
@@ -29,7 +29,7 @@
  * <p>{@code @Reusable} is useful when you want to limit the number of provisions of a type, but
  * there is no specific lifetime over which there must be only one instance.
  *
- * @see <a href="https://google.github.io/dagger/users-guide#reusable-scope">Reusable Scope</a>
+ * @see <a href="https://dagger.dev/users-guide#reusable-scope">Reusable Scope</a>
  */
 @Documented
 @Beta
diff --git a/java/dagger/android/AndroidInjection.java b/java/dagger/android/AndroidInjection.java
index 102a429..2f4fb4c 100644
--- a/java/dagger/android/AndroidInjection.java
+++ b/java/dagger/android/AndroidInjection.java
@@ -1,4 +1,4 @@
-/*
+  /*
  * Copyright (C) 2017 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,79 +39,99 @@
    * otherwise throws an {@link IllegalArgumentException}.
    *
    * @throws RuntimeException if the {@link Application} doesn't implement {@link
-   *     HasActivityInjector}.
+   *     HasAndroidInjector} or {@link HasActivityInjector}.
    */
   public static void inject(Activity activity) {
     checkNotNull(activity, "activity");
     Application application = activity.getApplication();
-    if (!(application instanceof HasActivityInjector)) {
+    AndroidInjector<? super Activity> injector;
+    if (application instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) application).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+    } else if (application instanceof HasActivityInjector) {
+      injector = ((HasActivityInjector) application).activityInjector();
+      checkNotNull(injector, "%s.activityInjector() returned null", application.getClass());
+    } else {
       throw new RuntimeException(
           String.format(
-              "%s does not implement %s",
+              "%s does not implement %s or %s",
               application.getClass().getCanonicalName(),
+              HasAndroidInjector.class.getCanonicalName(),
               HasActivityInjector.class.getCanonicalName()));
     }
 
-    AndroidInjector<Activity> activityInjector =
-        ((HasActivityInjector) application).activityInjector();
-    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
-
-    activityInjector.inject(activity);
+    injector.inject(activity);
   }
 
   /**
    * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
    * otherwise throws an {@link IllegalArgumentException}.
    *
-   * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to
-   * use to inject {@code fragment}:
+   * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
+   * inject {@code fragment}:
    *
    * <ol>
-   *   <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link
-   *       HasFragmentInjector}, and if none do
+   *   <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
+   *       HasAndroidInjector} or {@link HasFragmentInjector}, and if none do
    *   <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
-   *       {@link HasFragmentInjector}, and if not
-   *   <li>Uses the {@link android.app.Application} if it implements {@link HasFragmentInjector}.
+   *       {@link HasAndroidInjector} or {@link HasFragmentInjector}, and if not
+   *   <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
+   *       {@link HasFragmentInjector}.
    * </ol>
    *
-   * If none of them implement {@link HasFragmentInjector}, a {@link IllegalArgumentException} is
-   * thrown.
+   * If none of them implement {@link HasAndroidInjector} or {@link HasFragmentInjector}, a {@link
+   * IllegalArgumentException} is thrown.
    *
    * @throws IllegalArgumentException if no parent fragment, activity, or application implements
-   *     {@link HasFragmentInjector}.
+   *     {@link HasAndroidInjector} or {@link HasFragmentInjector}.
    */
   public static void inject(Fragment fragment) {
     checkNotNull(fragment, "fragment");
-    HasFragmentInjector hasFragmentInjector = findHasFragmentInjector(fragment);
+
+    Object hasInjector = findHasFragmentInjector(fragment);
+    AndroidInjector<? super Fragment> injector;
+    if (hasInjector instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) hasInjector).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
+    } else if (hasInjector instanceof HasFragmentInjector) {
+      injector = ((HasFragmentInjector) hasInjector).fragmentInjector();
+      checkNotNull(injector, "%s.fragmentInjector() returned null", hasInjector.getClass());
+    } else {
+      throw new RuntimeException(
+          String.format(
+              "%s does not implement %s or %s",
+              hasInjector.getClass().getCanonicalName(),
+              HasAndroidInjector.class.getCanonicalName(),
+              HasFragmentInjector.class.getCanonicalName()));
+    }
+
     if (Log.isLoggable(TAG, DEBUG)) {
       Log.d(
           TAG,
           String.format(
               "An injector for %s was found in %s",
               fragment.getClass().getCanonicalName(),
-              hasFragmentInjector.getClass().getCanonicalName()));
+              hasInjector.getClass().getCanonicalName()));
     }
 
-    AndroidInjector<Fragment> fragmentInjector = hasFragmentInjector.fragmentInjector();
-    checkNotNull(
-        fragmentInjector, "%s.fragmentInjector() returned null", hasFragmentInjector.getClass());
-
-    fragmentInjector.inject(fragment);
+    injector.inject(fragment);
   }
 
-  private static HasFragmentInjector findHasFragmentInjector(Fragment fragment) {
+  private static Object findHasFragmentInjector(Fragment fragment) {
     Fragment parentFragment = fragment;
     while ((parentFragment = parentFragment.getParentFragment()) != null) {
-      if (parentFragment instanceof HasFragmentInjector) {
-        return (HasFragmentInjector) parentFragment;
+      if (parentFragment instanceof HasAndroidInjector
+          || parentFragment instanceof HasFragmentInjector) {
+        return parentFragment;
       }
     }
     Activity activity = fragment.getActivity();
-    if (activity instanceof HasFragmentInjector) {
-      return (HasFragmentInjector) activity;
+    if (activity instanceof HasAndroidInjector || activity instanceof HasFragmentInjector) {
+      return activity;
     }
-    if (activity.getApplication() instanceof HasFragmentInjector) {
-      return (HasFragmentInjector) activity.getApplication();
+    Application application = activity.getApplication();
+    if (application instanceof HasAndroidInjector || application instanceof HasFragmentInjector) {
+      return application;
     }
     throw new IllegalArgumentException(
         String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
@@ -122,23 +142,28 @@
    * otherwise throws an {@link IllegalArgumentException}.
    *
    * @throws RuntimeException if the {@link Application} doesn't implement {@link
-   *     HasServiceInjector}.
+   *     HasAndroidInjector} or {@link HasServiceInjector}.
    */
   public static void inject(Service service) {
     checkNotNull(service, "service");
     Application application = service.getApplication();
-    if (!(application instanceof HasServiceInjector)) {
+    AndroidInjector<? super Service> injector;
+    if (application instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) application).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+    } else if (application instanceof HasServiceInjector) {
+      injector = ((HasServiceInjector) application).serviceInjector();
+      checkNotNull(injector, "%s.serviceInjector() returned null", application.getClass());
+    } else {
       throw new RuntimeException(
           String.format(
-              "%s does not implement %s",
+              "%s does not implement %s or %s",
               application.getClass().getCanonicalName(),
+              HasAndroidInjector.class.getCanonicalName(),
               HasServiceInjector.class.getCanonicalName()));
     }
 
-    AndroidInjector<Service> serviceInjector = ((HasServiceInjector) application).serviceInjector();
-    checkNotNull(serviceInjector, "%s.serviceInjector() returned null", application.getClass());
-
-    serviceInjector.inject(service);
+    injector.inject(service);
   }
 
   /**
@@ -146,28 +171,32 @@
    * be found, otherwise throws an {@link IllegalArgumentException}.
    *
    * @throws RuntimeException if the {@link Application} from {@link
-   *     Context#getApplicationContext()} doesn't implement {@link HasBroadcastReceiverInjector}.
+   *     Context#getApplicationContext()} doesn't implement {@link HasAndroidInjector} or {@link
+   *     HasBroadcastReceiverInjector}.
    */
   public static void inject(BroadcastReceiver broadcastReceiver, Context context) {
     checkNotNull(broadcastReceiver, "broadcastReceiver");
     checkNotNull(context, "context");
+
     Application application = (Application) context.getApplicationContext();
-    if (!(application instanceof HasBroadcastReceiverInjector)) {
+    AndroidInjector<? super BroadcastReceiver> injector;
+    if (application instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) application).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+    } else if (application instanceof HasBroadcastReceiverInjector) {
+      injector = ((HasBroadcastReceiverInjector) application).broadcastReceiverInjector();
+      checkNotNull(
+          injector, "%s.broadcastReceiverInjector() returned null", application.getClass());
+    } else {
       throw new RuntimeException(
           String.format(
-              "%s does not implement %s",
+              "%s does not implement %s or %s",
               application.getClass().getCanonicalName(),
+              HasAndroidInjector.class.getCanonicalName(),
               HasBroadcastReceiverInjector.class.getCanonicalName()));
     }
 
-    AndroidInjector<BroadcastReceiver> broadcastReceiverInjector =
-        ((HasBroadcastReceiverInjector) application).broadcastReceiverInjector();
-    checkNotNull(
-        broadcastReceiverInjector,
-        "%s.broadcastReceiverInjector() returned null",
-        application.getClass());
-
-    broadcastReceiverInjector.inject(broadcastReceiver);
+    injector.inject(broadcastReceiver);
   }
 
   /**
@@ -175,27 +204,29 @@
    * found, otherwise throws an {@link IllegalArgumentException}.
    *
    * @throws RuntimeException if the {@link Application} doesn't implement {@link
-   *     HasContentProviderInjector}.
+   *     HasAndroidInjector} or {@link HasContentProviderInjector}.
    */
   public static void inject(ContentProvider contentProvider) {
     checkNotNull(contentProvider, "contentProvider");
     Application application = (Application) contentProvider.getContext().getApplicationContext();
-    if (!(application instanceof HasContentProviderInjector)) {
+
+    AndroidInjector<? super ContentProvider> injector;
+    if (application instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) application).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
+    } else if (application instanceof HasContentProviderInjector) {
+      injector = ((HasContentProviderInjector) application).contentProviderInjector();
+      checkNotNull(injector, "%s.contentProviderInjector() returned null", application.getClass());
+    } else {
       throw new RuntimeException(
           String.format(
-              "%s does not implement %s",
+              "%s does not implement %s or %s",
               application.getClass().getCanonicalName(),
-              HasContentProviderInjector.class.getCanonicalName()));
+              HasAndroidInjector.class.getCanonicalName(),
+              HasBroadcastReceiverInjector.class.getCanonicalName()));
     }
 
-    AndroidInjector<ContentProvider> contentProviderInjector =
-        ((HasContentProviderInjector) application).contentProviderInjector();
-    checkNotNull(
-        contentProviderInjector,
-        "%s.contentProviderInjector() returned null",
-        application.getClass());
-
-    contentProviderInjector.inject(contentProvider);
+    injector.inject(contentProvider);
   }
 
   private AndroidInjection() {}
diff --git a/java/dagger/android/ContributesAndroidInjector.java b/java/dagger/android/ContributesAndroidInjector.java
index 76492d1..5aa9312 100644
--- a/java/dagger/android/ContributesAndroidInjector.java
+++ b/java/dagger/android/ContributesAndroidInjector.java
@@ -32,7 +32,7 @@
  * a concrete Android framework type (e.g. {@code FooActivity}, {@code BarFragment}, {@code
  * MyService}, etc). The method should have no parameters.
  *
- * <p>For more information, see <a href="https://google.github.io/dagger/android">the docs</a>
+ * <p>For more information, see <a href="https://dagger.dev/android">the docs</a>
  */
 @Documented
 @Retention(RUNTIME)
diff --git a/java/dagger/android/DaggerActivity.java b/java/dagger/android/DaggerActivity.java
index 1fd9b9d..43708f3 100644
--- a/java/dagger/android/DaggerActivity.java
+++ b/java/dagger/android/DaggerActivity.java
@@ -28,9 +28,9 @@
  * inject {@link Fragment}s attached to it.
  */
 @Beta
-public abstract class DaggerActivity extends Activity implements HasFragmentInjector {
+public abstract class DaggerActivity extends Activity implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> fragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -39,7 +39,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> fragmentInjector() {
-    return fragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/DaggerApplication.java b/java/dagger/android/DaggerApplication.java
index ca9ea43..d09050b 100644
--- a/java/dagger/android/DaggerApplication.java
+++ b/java/dagger/android/DaggerApplication.java
@@ -16,36 +16,21 @@
 
 package dagger.android;
 
-import android.app.Activity;
 import android.app.Application;
-import android.app.Fragment;
-import android.app.Service;
-import android.content.BroadcastReceiver;
 import android.content.ContentProvider;
 import com.google.errorprone.annotations.ForOverride;
 import dagger.internal.Beta;
 import javax.inject.Inject;
 
 /**
- * An {@link Application} that injects its members and can be used to inject {@link Activity}s,
- * {@link Fragment}s, {@link Service}s, {@link BroadcastReceiver}s and {@link ContentProvider}s
- * attached to it. Injection is performed in {@link #onCreate()} or the first call to {@link
- * AndroidInjection#inject(ContentProvider)}, whichever happens first.
+ * An {@link Application} that injects its members and can be used to inject objects that the
+ * Android framework instantiates, such as Activitys, Fragments, or Services. Injection is performed
+ * in {@link #onCreate()} or the first call to {@link AndroidInjection#inject(ContentProvider)},
+ * whichever happens first.
  */
 @Beta
-public abstract class DaggerApplication extends Application
-    implements HasActivityInjector,
-        HasFragmentInjector,
-        HasServiceInjector,
-        HasBroadcastReceiverInjector,
-        HasContentProviderInjector {
-
-  @Inject DispatchingAndroidInjector<Activity> activityInjector;
-  @Inject DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;
-  @Inject DispatchingAndroidInjector<Fragment> fragmentInjector;
-  @Inject DispatchingAndroidInjector<Service> serviceInjector;
-  @Inject DispatchingAndroidInjector<ContentProvider> contentProviderInjector;
-  private volatile boolean needToInject = true;
+public abstract class DaggerApplication extends Application implements HasAndroidInjector {
+  @Inject volatile DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onCreate() {
@@ -69,14 +54,14 @@
    * allowing for a partially-constructed instance to escape.
    */
   private void injectIfNecessary() {
-    if (needToInject) {
+    if (androidInjector == null) {
       synchronized (this) {
-        if (needToInject) {
+        if (androidInjector == null) {
           @SuppressWarnings("unchecked")
           AndroidInjector<DaggerApplication> applicationInjector =
               (AndroidInjector<DaggerApplication>) applicationInjector();
           applicationInjector.inject(this);
-          if (needToInject) {
+          if (androidInjector == null) {
             throw new IllegalStateException(
                 "The AndroidInjector returned from applicationInjector() did not inject the "
                     + "DaggerApplication");
@@ -86,37 +71,12 @@
     }
   }
 
-  @Inject
-  void setInjected() {
-    needToInject = false;
-  }
-
   @Override
-  public DispatchingAndroidInjector<Activity> activityInjector() {
-    return activityInjector;
-  }
-
-  @Override
-  public DispatchingAndroidInjector<Fragment> fragmentInjector() {
-    return fragmentInjector;
-  }
-
-  @Override
-  public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
-    return broadcastReceiverInjector;
-  }
-
-  @Override
-  public DispatchingAndroidInjector<Service> serviceInjector() {
-    return serviceInjector;
-  }
-
-  // injectIfNecessary is called here but not on the other *Injector() methods because it is the
-  // only one that should be called (in AndroidInjection.inject(ContentProvider)) before
-  // Application.onCreate()
-  @Override
-  public AndroidInjector<ContentProvider> contentProviderInjector() {
+  public AndroidInjector<Object> androidInjector() {
+    // injectIfNecessary should already be called unless we are about to inject a ContentProvider,
+    // which can happen before Application.onCreate()
     injectIfNecessary();
-    return contentProviderInjector;
+
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/DaggerDialogFragment.java b/java/dagger/android/DaggerDialogFragment.java
index 00e65f2..3cbc0f1 100644
--- a/java/dagger/android/DaggerDialogFragment.java
+++ b/java/dagger/android/DaggerDialogFragment.java
@@ -33,9 +33,9 @@
  */
 @Deprecated
 @Beta
-public abstract class DaggerDialogFragment extends DialogFragment implements HasFragmentInjector {
+public abstract class DaggerDialogFragment extends DialogFragment implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onAttach(Context context) {
@@ -44,7 +44,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> fragmentInjector() {
-    return childFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/DaggerFragment.java b/java/dagger/android/DaggerFragment.java
index c95c457..187820b 100644
--- a/java/dagger/android/DaggerFragment.java
+++ b/java/dagger/android/DaggerFragment.java
@@ -32,9 +32,9 @@
  */
 @Beta
 @Deprecated
-public abstract class DaggerFragment extends Fragment implements HasFragmentInjector {
+public abstract class DaggerFragment extends Fragment implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onAttach(Context context) {
@@ -43,7 +43,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> fragmentInjector() {
-    return childFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/HasAndroidInjector.java b/java/dagger/android/HasAndroidInjector.java
new file mode 100644
index 0000000..3b49718
--- /dev/null
+++ b/java/dagger/android/HasAndroidInjector.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import dagger.internal.Beta;
+
+/** Provides an {@link AndroidInjector}. */
+@Beta
+public interface HasAndroidInjector {
+  /** Returns an {@link AndroidInjector}. */
+  AndroidInjector<Object> androidInjector();
+}
diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java
index 7c5ae07..3ec6613 100644
--- a/java/dagger/android/processor/AndroidInjectorDescriptor.java
+++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java
@@ -43,9 +43,7 @@
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
 import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-import javax.lang.model.util.Types;
 import javax.tools.Diagnostic.Kind;
 
 /**
@@ -85,13 +83,9 @@
   }
 
   static final class Validator {
-    private final Types types;
-    private final Elements elements;
     private final Messager messager;
 
-    Validator(Types types, Elements elements, Messager messager) {
-      this.types = types;
-      this.elements = elements;
+    Validator(Messager messager) {
       this.messager = messager;
     }
 
diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java
index 4cdcd2a..f6e808a 100644
--- a/java/dagger/android/processor/AndroidMapKeyValidator.java
+++ b/java/dagger/android/processor/AndroidMapKeyValidator.java
@@ -131,7 +131,7 @@
       messager.printMessage(
           Kind.ERROR,
           String.format(
-              "%s should bind %s, not %s. See https://google.github.io/dagger/android",
+              "%s should bind %s, not %s. See https://dagger.dev/android",
               method, requiredReturnType, returnType),
           method);
     }
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
index c468170..5c17341 100644
--- a/java/dagger/android/processor/AndroidProcessor.java
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -18,6 +18,7 @@
 
 import static javax.tools.Diagnostic.Kind.ERROR;
 import static javax.tools.StandardLocation.CLASS_OUTPUT;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
 
 import com.google.auto.common.BasicAnnotationProcessor;
 import com.google.auto.service.AutoService;
@@ -36,6 +37,7 @@
 import javax.lang.model.SourceVersion;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
 
 /**
  * An {@linkplain javax.annotation.processing.Processor annotation processor} to verify usage of
@@ -51,6 +53,7 @@
  * R8 in <a href="https://r8.googlesource.com/r8/+/389123dfcc11e6dda0eec31ab62e1b7eb0da80d2">May
  * 2018</a>.
  */
+@IncrementalAnnotationProcessor(ISOLATING)
 @AutoService(Processor.class)
 public final class AndroidProcessor extends BasicAnnotationProcessor {
   private static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
@@ -66,7 +69,7 @@
     return ImmutableList.of(
         new AndroidMapKeyValidator(elements, types, messager),
         new ContributesAndroidInjectorGenerator(
-            new AndroidInjectorDescriptor.Validator(types, elements, messager),
+            new AndroidInjectorDescriptor.Validator(messager),
             useStringKeys(),
             filer,
             elements,
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
index 00c1021..5754143 100644
--- a/java/dagger/android/processor/BUILD
+++ b/java/dagger/android/processor/BUILD
@@ -22,8 +22,7 @@
     "DOCLINT_HTML_AND_SYNTAX",
     "DOCLINT_REFERENCES",
 )
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
-load("//tools:simple_jar.bzl", "simple_jar")
+load("//tools:maven.bzl", "POM_VERSION", "pom_file")
 
 filegroup(
     name = "srcs",
@@ -34,13 +33,13 @@
     name = "processor",
     srcs = [":srcs"],
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
-    resource_jars = [":processor_manifest_files.jar"],
     tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
     deps = [
         "@google_bazel_common//third_party/java/guava",
         "@google_bazel_common//third_party/java/auto:service",
         "@google_bazel_common//third_party/java/auto:value",
         "@google_bazel_common//third_party/java/auto:common",
+        "@google_bazel_common//third_party/java/incap",
         "@google_bazel_common//third_party/java/javapoet",
         "@google_bazel_common//third_party/java/google_java_format",
         "//java/dagger:core",
@@ -65,11 +64,6 @@
     visibility = ["//visibility:private"],
 )
 
-simple_jar(
-    name = "processor_manifest_files",
-    srcs = glob(["META-INF/**"]),
-)
-
 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 bae454b..5c99fd4 100644
--- a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
+++ b/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
@@ -35,7 +35,6 @@
 import com.google.common.collect.SetMultimap;
 import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.JavaFile;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.ParameterizedTypeName;
@@ -171,7 +170,7 @@
       ClassName subcomponentFactoryName) {
     AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(Subcomponent.class);
     for (ClassName module : descriptor.modules()) {
-      subcomponentAnnotation.addMember("modules", CodeBlock.of("$T.class", module));
+      subcomponentAnnotation.addMember("modules", "$T.class", module);
     }
 
     return interfaceBuilder(subcomponentName)
diff --git a/java/dagger/android/processor/META-INF/gradle/incremental.annotation.processors b/java/dagger/android/processor/META-INF/gradle/incremental.annotation.processors
deleted file mode 100644
index 22b8a95..0000000
--- a/java/dagger/android/processor/META-INF/gradle/incremental.annotation.processors
+++ /dev/null
@@ -1 +0,0 @@
-dagger.android.processor.AndroidProcessor,isolating
diff --git a/java/dagger/android/support/AndroidSupportInjection.java b/java/dagger/android/support/AndroidSupportInjection.java
index f5e206e..1624345 100644
--- a/java/dagger/android/support/AndroidSupportInjection.java
+++ b/java/dagger/android/support/AndroidSupportInjection.java
@@ -20,9 +20,11 @@
 import static dagger.internal.Preconditions.checkNotNull;
 
 import android.app.Activity;
+import android.app.Application;
 import android.support.v4.app.Fragment;
 import android.util.Log;
 import dagger.android.AndroidInjector;
+import dagger.android.HasAndroidInjector;
 import dagger.internal.Beta;
 
 /** Injects core Android types from support libraries. */
@@ -31,62 +33,75 @@
   private static final String TAG = "dagger.android.support";
 
   /**
-   * Injects {@code fragment} if an associated {@link dagger.android.AndroidInjector} implementation
-   * can be found, otherwise throws an {@link IllegalArgumentException}.
+   * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
+   * otherwise throws an {@link IllegalArgumentException}.
    *
-   * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to
-   * use to inject {@code fragment}:
+   * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
+   * inject {@code fragment}:
    *
    * <ol>
-   *   <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link
-   *       HasSupportFragmentInjector}, and if none do
+   *   <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
+   *       HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if none do
    *   <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
-   *       {@link HasSupportFragmentInjector}, and if not
-   *   <li>Uses the {@link android.app.Application} if it implements {@link
-   *       HasSupportFragmentInjector}.
+   *       {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if not
+   *   <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
+   *       {@link HasSupportFragmentInjector}.
    * </ol>
    *
-   * If none of them implement {@link HasSupportFragmentInjector}, a {@link
-   * IllegalArgumentException} is thrown.
+   * If none of them implement {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, a
+   * {@link IllegalArgumentException} is thrown.
    *
    * @throws IllegalArgumentException if no parent fragment, activity, or application implements
-   *     {@link HasSupportFragmentInjector}.
+   *     {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}.
    */
   public static void inject(Fragment fragment) {
     checkNotNull(fragment, "fragment");
-    HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector(fragment);
+
+    Object hasInjector = findHasSupportFragmentInjector(fragment);
+    AndroidInjector<? super Fragment> injector;
+    if (hasInjector instanceof HasAndroidInjector) {
+      injector = ((HasAndroidInjector) hasInjector).androidInjector();
+      checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
+    } else if (hasInjector instanceof HasSupportFragmentInjector) {
+      injector = ((HasSupportFragmentInjector) hasInjector).supportFragmentInjector();
+      checkNotNull(injector, "%s.supportFragmentInjector() returned null", hasInjector.getClass());
+    } else {
+      throw new RuntimeException(
+          String.format(
+              "%s does not implement %s or %s",
+              hasInjector.getClass().getCanonicalName(),
+              HasAndroidInjector.class.getCanonicalName(),
+              HasSupportFragmentInjector.class.getCanonicalName()));
+    }
+
     if (Log.isLoggable(TAG, DEBUG)) {
       Log.d(
           TAG,
           String.format(
               "An injector for %s was found in %s",
               fragment.getClass().getCanonicalName(),
-              hasSupportFragmentInjector.getClass().getCanonicalName()));
+              hasInjector.getClass().getCanonicalName()));
     }
 
-    AndroidInjector<Fragment> fragmentInjector =
-        hasSupportFragmentInjector.supportFragmentInjector();
-    checkNotNull(
-        fragmentInjector,
-        "%s.supportFragmentInjector() returned null",
-        hasSupportFragmentInjector.getClass());
-
-    fragmentInjector.inject(fragment);
+    injector.inject(fragment);
   }
 
-  private static HasSupportFragmentInjector findHasFragmentInjector(Fragment fragment) {
+  private static Object findHasSupportFragmentInjector(Fragment fragment) {
     Fragment parentFragment = fragment;
     while ((parentFragment = parentFragment.getParentFragment()) != null) {
-      if (parentFragment instanceof HasSupportFragmentInjector) {
-        return (HasSupportFragmentInjector) parentFragment;
+      if (parentFragment instanceof HasAndroidInjector
+          || parentFragment instanceof HasSupportFragmentInjector) {
+        return parentFragment;
       }
     }
     Activity activity = fragment.getActivity();
-    if (activity instanceof HasSupportFragmentInjector) {
-      return (HasSupportFragmentInjector) activity;
+    if (activity instanceof HasAndroidInjector || activity instanceof HasSupportFragmentInjector) {
+      return activity;
     }
-    if (activity.getApplication() instanceof HasSupportFragmentInjector) {
-      return (HasSupportFragmentInjector) activity.getApplication();
+    Application application = activity.getApplication();
+    if (application instanceof HasAndroidInjector
+        || application instanceof HasSupportFragmentInjector) {
+      return application;
     }
     throw new IllegalArgumentException(
         String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
diff --git a/java/dagger/android/support/DaggerAppCompatActivity.java b/java/dagger/android/support/DaggerAppCompatActivity.java
index 4621e9a..ccc4faa 100644
--- a/java/dagger/android/support/DaggerAppCompatActivity.java
+++ b/java/dagger/android/support/DaggerAppCompatActivity.java
@@ -18,12 +18,11 @@
 
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
 import android.support.v7.app.AppCompatActivity;
 import dagger.android.AndroidInjection;
 import dagger.android.AndroidInjector;
 import dagger.android.DispatchingAndroidInjector;
-import dagger.android.HasFragmentInjector;
+import dagger.android.HasAndroidInjector;
 import dagger.internal.Beta;
 import javax.inject.Inject;
 
@@ -33,10 +32,9 @@
  */
 @Beta
 public abstract class DaggerAppCompatActivity extends AppCompatActivity
-    implements HasFragmentInjector, HasSupportFragmentInjector {
+    implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
-  @Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -45,12 +43,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> supportFragmentInjector() {
-    return supportFragmentInjector;
-  }
-
-  @Override
-  public AndroidInjector<android.app.Fragment> fragmentInjector() {
-    return frameworkFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/support/DaggerAppCompatDialogFragment.java b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
index 6592c23..1efaeec 100644
--- a/java/dagger/android/support/DaggerAppCompatDialogFragment.java
+++ b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
@@ -21,6 +21,7 @@
 import android.support.v7.app.AppCompatDialogFragment;
 import dagger.android.AndroidInjector;
 import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
 import dagger.internal.Beta;
 import javax.inject.Inject;
 
@@ -31,9 +32,9 @@
  */
 @Beta
 public abstract class DaggerAppCompatDialogFragment extends AppCompatDialogFragment
-    implements HasSupportFragmentInjector {
+    implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onAttach(Context context) {
@@ -42,7 +43,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> supportFragmentInjector() {
-    return childFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/support/DaggerApplication.java b/java/dagger/android/support/DaggerApplication.java
index 294d2aa..1cb3bd8 100644
--- a/java/dagger/android/support/DaggerApplication.java
+++ b/java/dagger/android/support/DaggerApplication.java
@@ -16,27 +16,15 @@
 
 package dagger.android.support;
 
-import android.support.v4.app.Fragment;
 import dagger.android.AndroidInjector;
-import dagger.android.DispatchingAndroidInjector;
-import javax.inject.Inject;
 
 /**
- * An {@link android.app.Application} that injects its members and can be used to inject {@link
- * android.app.Activity}s, {@linkplain android.app.Fragment framework fragments}, {@linkplain
- * Fragment support fragments}, {@link android.app.Service}s, {@link
- * android.content.BroadcastReceiver}s, and {@link android.content.ContentProvider}s attached to it.
+ * An {@link Application} that injects its members and can be used to inject classes that the
+ * Android framework instantiates. Injection is performed in {@link #onCreate()} or the first call
+ * to {@link AndroidInjection#inject(ContentProvider)}, whichever happens first.
  */
-public abstract class DaggerApplication extends dagger.android.DaggerApplication
-    implements HasSupportFragmentInjector {
-
-  @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
-
+// TODO(ronshapiro): deprecate and remove this class
+public abstract class DaggerApplication extends dagger.android.DaggerApplication {
   @Override
   protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();
-
-  @Override
-  public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
-    return supportFragmentInjector;
-  }
 }
diff --git a/java/dagger/android/support/DaggerDialogFragment.java b/java/dagger/android/support/DaggerDialogFragment.java
index 18feb6e..69b90bc 100644
--- a/java/dagger/android/support/DaggerDialogFragment.java
+++ b/java/dagger/android/support/DaggerDialogFragment.java
@@ -21,6 +21,7 @@
 import android.support.v4.app.Fragment;
 import dagger.android.AndroidInjector;
 import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
 import dagger.internal.Beta;
 import javax.inject.Inject;
 
@@ -30,10 +31,9 @@
  * its members will be injected again.
  */
 @Beta
-public abstract class DaggerDialogFragment extends DialogFragment
-    implements HasSupportFragmentInjector {
+public abstract class DaggerDialogFragment extends DialogFragment implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onAttach(Context context) {
@@ -42,7 +42,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> supportFragmentInjector() {
-    return childFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/android/support/DaggerFragment.java b/java/dagger/android/support/DaggerFragment.java
index c7335a8..332cdaa 100644
--- a/java/dagger/android/support/DaggerFragment.java
+++ b/java/dagger/android/support/DaggerFragment.java
@@ -20,6 +20,7 @@
 import android.support.v4.app.Fragment;
 import dagger.android.AndroidInjector;
 import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasAndroidInjector;
 import dagger.internal.Beta;
 import javax.inject.Inject;
 
@@ -29,9 +30,9 @@
  * members will be injected again.
  */
 @Beta
-public abstract class DaggerFragment extends Fragment implements HasSupportFragmentInjector {
+public abstract class DaggerFragment extends Fragment implements HasAndroidInjector {
 
-  @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
+  @Inject DispatchingAndroidInjector<Object> androidInjector;
 
   @Override
   public void onAttach(Context context) {
@@ -40,7 +41,7 @@
   }
 
   @Override
-  public AndroidInjector<Fragment> supportFragmentInjector() {
-    return childFragmentInjector;
+  public AndroidInjector<Object> androidInjector() {
+    return androidInjector;
   }
 }
diff --git a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
index c2d9024..e98fe9b 100644
--- a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
+++ b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
@@ -58,13 +58,12 @@
   @Override
   public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
     if (MODULE_CLASS_LITERAL.matches(tree, state)) {
-      return buildDescription(tree)
-          .addFix(
-              SuggestedFix.builder()
-                  .replace(tree, "AndroidInjectionModule.class")
-                  .addImport("dagger.android.AndroidInjectionModule")
-                  .build())
-          .build();
+      return describeMatch(
+          tree,
+          SuggestedFix.builder()
+              .replace(tree, "AndroidInjectionModule.class")
+              .addImport("dagger.android.AndroidInjectionModule")
+              .build());
     }
     return Description.NO_MATCH;
   }
diff --git a/java/dagger/grpc/server/CallScoped.java b/java/dagger/grpc/server/CallScoped.java
index a477f9d..4b9d14f 100644
--- a/java/dagger/grpc/server/CallScoped.java
+++ b/java/dagger/grpc/server/CallScoped.java
@@ -16,12 +16,14 @@
 
 package dagger.grpc.server;
 
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
 import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
 import javax.inject.Scope;
 
-/**
- * A scope that lasts as long as a single gRPC {@link io.grpc.ServerCall}.
- */
+/** A scope that lasts as long as a single gRPC {@link io.grpc.ServerCall}. */
+@Retention(RUNTIME)
 @Scope
 @Documented
 public @interface CallScoped {}
diff --git a/java/dagger/grpc/server/README.md b/java/dagger/grpc/server/README.md
index c8d027c..5fe8d5c 100644
--- a/java/dagger/grpc/server/README.md
+++ b/java/dagger/grpc/server/README.md
@@ -1,10 +1,10 @@
 # Dagger-gRPC on the Server
 
 This package contains the public types used to create gRPC server applications
-using https://google.github.io/dagger.
+using https://dagger.dev.
 
 It is maintained by the Dagger team.
 
 It is in development, and is planned for open-source release as part of Dagger.
 
-See user documentation at https://google.github.io/dagger/grpc.
+See user documentation at https://dagger.dev/grpc.
diff --git a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
index 58495e2..e361fdb 100644
--- a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
+++ b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
@@ -40,7 +40,7 @@
 /**
  * Generates code from types annotated with {@link GrpcService @GrpcService}.
  *
- * @see <a href="https://google.github.io/dagger/grpc">https://google.github.io/dagger/grpc</a>
+ * @see <a href="https://dagger.dev/grpc">https://dagger.dev/grpc</a>
  */
 @AutoService(Processor.class)
 public class GrpcServiceProcessor extends BasicAnnotationProcessor implements ProcessingStep {
diff --git a/java/dagger/internal/ComponentDefinitionType.java b/java/dagger/internal/ComponentDefinitionType.java
new file mode 100644
index 0000000..1ab8b13
--- /dev/null
+++ b/java/dagger/internal/ComponentDefinitionType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.lang.annotation.Target;
+
+/** Specifies the user-defined component that is being implemented by the annotated class. */
+@Target(TYPE)
+public @interface ComponentDefinitionType {
+  Class<?> value();
+}
diff --git a/java/dagger/internal/ConfigureInitializationParameters.java b/java/dagger/internal/ConfigureInitializationParameters.java
new file mode 100644
index 0000000..1ca0fbb
--- /dev/null
+++ b/java/dagger/internal/ConfigureInitializationParameters.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a {@code configureInitialization()} method with {@code ComponentRequirement}s that it
+ * accepts as parameters.
+ */
+@Target(METHOD)
+public @interface ConfigureInitializationParameters {
+  /**
+   * The list of parameters.
+   *
+   * Each value is a {@link dagger.internal.codegen.serialization.ComponentRequirementProto}
+   * serialized in Base64.
+   */
+  String[] value() default {};
+}
diff --git a/java/dagger/internal/ModifiableBinding.java b/java/dagger/internal/ModifiableBinding.java
new file mode 100644
index 0000000..1e658e4
--- /dev/null
+++ b/java/dagger/internal/ModifiableBinding.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Target;
+
+/** Annotates methods that implement bindings that may be modified by subclass implementations. */
+@Target(METHOD)
+public @interface ModifiableBinding {
+  /** {@code ModifiableBindingType} of the binding. */
+  // TODO(ronshapiro): should this be a shared enum with dagger.internal.codegen?
+  String modifiableBindingType();
+
+  /** A {@link dagger.internal.codegen.serialization.BindingRequestProto} serialized in Base64. */
+  String bindingRequest();
+
+  /**
+   * For a multibinding, the keys of all contributions it depends on in this implementation.
+   */
+  String[] multibindingContributions() default {};
+}
diff --git a/java/dagger/internal/ModifiableModule.java b/java/dagger/internal/ModifiableModule.java
new file mode 100644
index 0000000..983321e
--- /dev/null
+++ b/java/dagger/internal/ModifiableModule.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/**
+ * Annotates methods that return {@linkplain dagger.Module modules} that may be modified by subclass
+ * implementations.
+ */
+public @interface ModifiableModule {
+  /** The serialized {@code ComponentRequirement} of this method's module. */
+  String value();
+}
diff --git a/java/dagger/internal/codegen/AnnotationCreatorGenerator.java b/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
index c38b747..273f552 100644
--- a/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
+++ b/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
@@ -21,7 +21,7 @@
 import static com.squareup.javapoet.TypeSpec.classBuilder;
 import static dagger.internal.codegen.AnnotationExpression.createMethodName;
 import static dagger.internal.codegen.AnnotationExpression.getAnnotationCreatorClassName;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
@@ -36,6 +36,7 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.LinkedHashSet;
 import java.util.Optional;
 import java.util.Set;
diff --git a/java/dagger/internal/codegen/AnnotationExpression.java b/java/dagger/internal/codegen/AnnotationExpression.java
index 9072fe2..8d729e2 100644
--- a/java/dagger/internal/codegen/AnnotationExpression.java
+++ b/java/dagger/internal/codegen/AnnotationExpression.java
@@ -17,8 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
 import static dagger.internal.codegen.SourceFiles.classFileName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
 import static java.util.stream.Collectors.toList;
 
 import com.google.auto.common.MoreElements;
diff --git a/java/dagger/internal/codegen/AnnotationProtoConverter.java b/java/dagger/internal/codegen/AnnotationProtoConverter.java
new file mode 100644
index 0000000..282d8ca
--- /dev/null
+++ b/java/dagger/internal/codegen/AnnotationProtoConverter.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Maps.transformValues;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static javax.lang.model.util.ElementFilter.fieldsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.serialization.AnnotationProto;
+import dagger.internal.codegen.serialization.AnnotationValueProto;
+import java.util.Collections;
+import java.util.List;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Converts {@link AnnotationMirror}s to {@link AnnotationProto}s and vice-versa. */
+final class AnnotationProtoConverter {
+  private final TypeProtoConverter typeProtoConverter;
+
+  @Inject
+  AnnotationProtoConverter(TypeProtoConverter typeProtoConverter) {
+    this.typeProtoConverter = typeProtoConverter;
+  }
+
+  /** Translates an {@link AnnotationMirror} to a proto representation. */
+  static AnnotationProto toProto(AnnotationMirror annotationMirror) {
+    AnnotationProto.Builder builder = AnnotationProto.newBuilder();
+    builder.setAnnotationType(TypeProtoConverter.toProto(annotationMirror.getAnnotationType()));
+    getAnnotationValuesWithDefaults(annotationMirror)
+        .forEach(
+            (attribute, value) ->
+                builder.putAllValues(
+                    Collections.singletonMap(
+                        attribute.getSimpleName().toString(), annotationValueProto(value))));
+    return builder.build();
+  }
+
+  /** Creates an {@link AnnotationMirror} from its proto representation. */
+  AnnotationMirror fromProto(AnnotationProto annotation) {
+    return SimpleAnnotationMirror.of(
+        MoreTypes.asTypeElement(typeProtoConverter.fromProto(annotation.getAnnotationType())),
+        transformValues(annotation.getValues(), AnnotationValueFromProto::new));
+  }
+
+  private static final AnnotationValueVisitor<
+          AnnotationValueProto.Builder, AnnotationValueProto.Builder>
+      ANNOTATION_VALUE_TO_PROTO =
+          new SimpleAnnotationValueVisitor8<
+              AnnotationValueProto.Builder, AnnotationValueProto.Builder>() {
+            @Override
+            public AnnotationValueProto.Builder visitAnnotation(
+                AnnotationMirror nestedAnnotation, AnnotationValueProto.Builder builder) {
+              return builder
+                  .setNestedAnnotation(toProto(nestedAnnotation))
+                  .setKind(AnnotationValueProto.Kind.ANNOTATION);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitBoolean(
+                boolean b, AnnotationValueProto.Builder builder) {
+              return builder.setBooleanValue(b).setKind(AnnotationValueProto.Kind.BOOLEAN);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitChar(
+                char c, AnnotationValueProto.Builder builder) {
+              return builder
+                  .setStringValue(String.valueOf(c))
+                  .setKind(AnnotationValueProto.Kind.CHAR);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitByte(
+                byte b, AnnotationValueProto.Builder builder) {
+              return builder.setIntValue(b).setKind(AnnotationValueProto.Kind.BYTE);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitShort(
+                short s, AnnotationValueProto.Builder builder) {
+              return builder.setIntValue(s).setKind(AnnotationValueProto.Kind.SHORT);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitInt(
+                int i, AnnotationValueProto.Builder builder) {
+              return builder.setIntValue(i).setKind(AnnotationValueProto.Kind.INT);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitFloat(
+                float f, AnnotationValueProto.Builder builder) {
+              return builder.setFloatValue(f).setKind(AnnotationValueProto.Kind.FLOAT);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitLong(
+                long l, AnnotationValueProto.Builder builder) {
+              return builder.setLongValue(l).setKind(AnnotationValueProto.Kind.LONG);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitDouble(
+                double d, AnnotationValueProto.Builder builder) {
+              return builder.setDoubleValue(d).setKind(AnnotationValueProto.Kind.DOUBLE);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitString(
+                String s, AnnotationValueProto.Builder builder) {
+              return builder.setStringValue(s).setKind(AnnotationValueProto.Kind.STRING);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitType(
+                TypeMirror t, AnnotationValueProto.Builder builder) {
+              return builder
+                  .setClassLiteral(TypeProtoConverter.toProto(t))
+                  .setKind(AnnotationValueProto.Kind.CLASS_LITERAL);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitEnumConstant(
+                VariableElement c, AnnotationValueProto.Builder builder) {
+              return builder
+                  .setEnumType(TypeProtoConverter.toProto(c.asType()))
+                  .setEnumName(c.getSimpleName().toString())
+                  .setKind(AnnotationValueProto.Kind.ENUM);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitArray(
+                List<? extends AnnotationValue> values, AnnotationValueProto.Builder builder) {
+              values.forEach(value -> builder.addArrayValues(annotationValueProto(value)));
+              return builder.setKind(AnnotationValueProto.Kind.ARRAY);
+            }
+
+            @Override
+            public AnnotationValueProto.Builder visitUnknown(
+                AnnotationValue av, AnnotationValueProto.Builder builder) {
+              throw new UnsupportedOperationException(av.toString());
+            }
+          };
+
+  /** Translates an {@link AnnotationValue} to a proto representation. */
+  private static AnnotationValueProto annotationValueProto(AnnotationValue annotationValue) {
+    return annotationValue
+        .accept(ANNOTATION_VALUE_TO_PROTO, AnnotationValueProto.newBuilder())
+        .build();
+  }
+
+  private class AnnotationValueFromProto implements AnnotationValue {
+    private final AnnotationValueProto proto;
+
+    AnnotationValueFromProto(AnnotationValueProto proto) {
+      this.proto = proto;
+    }
+
+    @Override
+    public Object getValue() {
+      switch (proto.getKind()) {
+        case BOOLEAN:
+          return proto.getBooleanValue();
+        case BYTE:
+          return (byte) proto.getIntValue();
+        case SHORT:
+          return (short) proto.getIntValue();
+        case CHAR:
+          return getCharValue();
+        case INT:
+          return proto.getIntValue();
+        case FLOAT:
+          return proto.getFloatValue();
+        case LONG:
+          return proto.getLongValue();
+        case DOUBLE:
+          return proto.getDoubleValue();
+        case STRING:
+          return proto.getStringValue();
+        case CLASS_LITERAL:
+          return typeProtoConverter.fromProto(proto.getClassLiteral());
+        case ENUM:
+          return getEnumConstant();
+        case ANNOTATION:
+          return fromProto(proto.getNestedAnnotation());
+        case ARRAY:
+          return getArrayValues();
+        case UNKNOWN:
+        case UNRECOGNIZED:
+          // fall through
+      }
+      throw new AssertionError(proto);
+    }
+
+    @Override
+    public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P passedValue) {
+      switch (proto.getKind()) {
+        case BOOLEAN:
+          return visitor.visitBoolean(proto.getBooleanValue(), passedValue);
+        case BYTE:
+          return visitor.visitByte((byte) proto.getIntValue(), passedValue);
+        case SHORT:
+          return visitor.visitShort((short) proto.getIntValue(), passedValue);
+        case CHAR:
+          return visitor.visitChar(getCharValue(), passedValue);
+        case INT:
+          return visitor.visitInt(proto.getIntValue(), passedValue);
+        case FLOAT:
+          return visitor.visitFloat(proto.getFloatValue(), passedValue);
+        case LONG:
+          return visitor.visitLong(proto.getLongValue(), passedValue);
+        case DOUBLE:
+          return visitor.visitDouble(proto.getDoubleValue(), passedValue);
+        case STRING:
+          return visitor.visitString((String) getValue(), passedValue);
+        case CLASS_LITERAL:
+          return visitor.visitType((TypeMirror) getValue(), passedValue);
+        case ENUM:
+          return visitor.visitEnumConstant((VariableElement) getValue(), passedValue);
+        case ANNOTATION:
+          return visitor.visitAnnotation((AnnotationMirror) getValue(), passedValue);
+        case ARRAY:
+          return visitor.visitArray(getArrayValues(), passedValue);
+        case UNKNOWN:
+        case UNRECOGNIZED:
+          // fall through
+      }
+      throw new AssertionError(proto);
+    }
+
+    private char getCharValue() {
+      checkState(proto.getKind().equals(AnnotationValueProto.Kind.CHAR));
+      return proto.getStringValue().charAt(0);
+    }
+
+    private VariableElement getEnumConstant() {
+      checkState(proto.getKind().equals(AnnotationValueProto.Kind.ENUM));
+      TypeMirror enumType = typeProtoConverter.fromProto(proto.getEnumType());
+      return fieldsIn(MoreTypes.asTypeElement(enumType).getEnclosedElements()).stream()
+          .filter(value -> value.getSimpleName().contentEquals(proto.getEnumName()))
+          .findFirst()
+          .get();
+    }
+
+    private ImmutableList<AnnotationValue> getArrayValues() {
+      checkState(proto.getKind().equals(AnnotationValueProto.Kind.ARRAY));
+      return proto.getArrayValuesList().stream()
+          .map(AnnotationValueFromProto::new)
+          .collect(toImmutableList());
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java b/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
index 1e66e59..fc30eaa 100644
--- a/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
@@ -17,12 +17,13 @@
 package dagger.internal.codegen;
 
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.CodeBlocks.anonymousProvider;
+import static dagger.internal.codegen.javapoet.CodeBlocks.anonymousProvider;
 import static dagger.model.RequestKind.INSTANCE;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.Expression;
 
 /**
  * A {@link javax.inject.Provider} creation expression for an anonymous inner class whose
diff --git a/java/dagger/internal/codegen/AnyBindingMethodValidator.java b/java/dagger/internal/codegen/AnyBindingMethodValidator.java
index 23393b6..bad9636 100644
--- a/java/dagger/internal/codegen/AnyBindingMethodValidator.java
+++ b/java/dagger/internal/codegen/AnyBindingMethodValidator.java
@@ -18,9 +18,9 @@
 
 import static com.google.auto.common.MoreElements.isAnnotationPresent;
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.collect.ImmutableMap;
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index e7b6b60..824a72e 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -19,18 +19,19 @@
 
 load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
 load("//tools:maven.bzl", "POM_VERSION", "pom_file")
-load("//tools:simple_jar.bzl", "simple_jar")
 
 EXPERIMENTAL_VISUALIZER_SRCS = ["BindingNetworkVisualizer.java"]
 
-KYTHE_SRCS = [
-    "DaggerKythePlugin.java",
-    "JavacPluginModule.java",
-]
+JAVAC_PLUGIN_MODULE_SRCS = ["JavacPluginModule.java"]
+
+KYTHE_SRCS = ["DaggerKythePlugin.java"]
+
+STATISTICS_COLLECTOR_SRCS = ["BindingGraphStatisticsCollector.java"]
 
 CODEGEN_SRCS = glob(
     ["*.java"],
-    exclude = EXPERIMENTAL_VISUALIZER_SRCS + KYTHE_SRCS,
+    exclude = EXPERIMENTAL_VISUALIZER_SRCS + KYTHE_SRCS + STATISTICS_COLLECTOR_SRCS +
+              JAVAC_PLUGIN_MODULE_SRCS,
 )
 
 CODEGEN_PLUGINS = [":bootstrap_compiler_plugin"]
@@ -47,6 +48,7 @@
     "@google_bazel_common//third_party/java/jsr250_annotations",
     "@google_bazel_common//third_party/java/jsr330_inject",
     "//java/dagger:core",
+    "//java/dagger/internal/codegen/serialization",
     "//java/dagger/producers",
     "//java/dagger/model",
     "//java/dagger/spi",
@@ -78,23 +80,20 @@
 java_library(
     name = "base",
     srcs = [
-        "Accessibility.java",
-        "AnnotationSpecs.java",
+        "AnnotationProtoConverter.java",
         "ClearableCache.java",
-        "CodeBlocks.java",
         "CompilerOptions.java",
         "ComponentAnnotation.java",
         "ContributionType.java",
-        "DaggerElements.java",
         "DaggerStatistics.java",
         "DaggerStatisticsCollectingProcessingStep.java",
         "DaggerStatisticsCollector.java",
         "DaggerStatisticsRecorder.java",
-        "DaggerTypes.java",
         "DiagnosticFormatting.java",
-        "Expression.java",
+        "ElementFormatter.java",
         "FeatureStatus.java",
         "Formatter.java",
+        "ForwardingCompilerOptions.java",
         "FrameworkTypes.java",
         "InjectionAnnotations.java",
         "Keys.java",
@@ -105,6 +104,7 @@
         "MoreAnnotationValues.java",
         "MultibindingAnnotations.java",
         "OptionalType.java",
+        "ProcessingEnvironmentCompilerOptions.java",
         "ProcessingOptions.java",
         "RequestKinds.java",
         "Scopes.java",
@@ -114,8 +114,7 @@
         "SourceFileGenerationException.java",  # Used in :writing and :processor
         "SourceFileGenerator.java",  # Needed by InjectBindingRegistry in :binding and also :writing
         "TypeCheckingProcessingStep.java",
-        "TypeNames.java",
-        "TypeSpecs.java",
+        "TypeProtoConverter.java",
         "UniqueNameSet.java",
         "Util.java",
         "ValidationType.java",
@@ -123,7 +122,10 @@
     ],
     plugins = CODEGEN_PLUGINS,
     tags = ["maven:merged"],
-    deps = CODEGEN_DEPS,
+    deps = CODEGEN_DEPS + [
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
+    ],
 )
 
 # Classes that help to build a model of the binding graph
@@ -165,6 +167,7 @@
         "FrameworkType.java",
         "FrameworkTypeMapper.java",
         "InjectBindingRegistry.java",
+        "InjectionSiteFactory.java",
         "KeyFactory.java",
         "KeyVariableNamer.java",  # needs ConfigurationAnnotations, SourceFiles
         "MapKeys.java",
@@ -184,7 +187,11 @@
     ],
     plugins = CODEGEN_PLUGINS,
     tags = ["maven:merged"],
-    deps = CODEGEN_DEPS + [":base"],
+    deps = CODEGEN_DEPS + [
+        ":base",
+        "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/internal/codegen/javapoet",
+    ],
 )
 
 # Code related to validating the user-written Dagger code
@@ -192,10 +199,14 @@
     name = "validation",
     srcs = [
         "AnyBindingMethodValidator.java",
+        "BindingElementValidator.java",
         "BindingGraphPlugins.java",
         "BindingGraphValidator.java",
         "BindingMethodProcessingStep.java",
         "BindingMethodValidator.java",
+        "BindsInstanceElementValidator.java",
+        "BindsInstanceMethodValidator.java",
+        "BindsInstanceParameterValidator.java",
         "BindsInstanceProcessingStep.java",
         "BindsMethodValidator.java",
         "BindsOptionalOfMethodValidator.java",
@@ -221,6 +232,7 @@
     deps = CODEGEN_DEPS + [
         ":base",
         ":binding",
+        "//java/dagger/internal/codegen/langmodel",
     ],
 )
 
@@ -244,6 +256,7 @@
         ":base",
         ":binding",
         ":validation",
+        "//java/dagger/internal/codegen/langmodel",
     ],
 )
 
@@ -333,14 +346,11 @@
     deps = CODEGEN_DEPS + [
         ":base",
         ":binding",
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
     ],
 )
 
-simple_jar(
-    name = "processor_manifest_files",
-    srcs = glob(["META-INF/**"]),
-)
-
 # The processor's "main", if you will
 java_library(
     name = "processor",
@@ -355,6 +365,7 @@
         "ComponentProcessingStep.java",
         "ComponentProcessor.java",
         "CurrentImplementationSubcomponent.java",
+        "DeserializedComponentImplementationBuilder.java",
         "GenerationOptionsModule.java",
         "InjectBindingRegistryImpl.java",
         "InjectBindingRegistryModule.java",
@@ -369,7 +380,6 @@
         "TopLevelImplementationComponent.java",
     ],
     plugins = CODEGEN_PLUGINS,
-    resource_jars = [":processor_manifest_files.jar"],
     tags = ["maven_coordinates=com.google.dagger:dagger-compiler:" + POM_VERSION],
     deps = CODEGEN_DEPS + [
         ":base",
@@ -377,6 +387,9 @@
         ":binding_graph_validation",
         ":writing",
         ":validation",
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
+        "@google_bazel_common//third_party/java/incap",
     ],
 )
 
@@ -391,6 +404,23 @@
         ":binding_graph_validation",
         ":writing",
         ":validation",
+        "//java/dagger/internal/codegen/serialization",
+        "//java/dagger/internal/codegen/javapoet",
+    ],
+)
+
+java_library(
+    name = "javac-plugin-module",
+    srcs = JAVAC_PLUGIN_MODULE_SRCS,
+    plugins = [":component-codegen"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":base",
+        ":binding",
+        ":javac",
+        ":processor",
+        "//java/dagger:core",
+        "//java/dagger/internal/codegen/langmodel",
     ],
 )
 
@@ -401,18 +431,26 @@
     deps = [
         ":base",
         ":binding",
+        ":javac",
+        ":javac-plugin-module",
         ":kythe_plugin",
         ":processor",
         "//java/dagger:core",
+        "//java/dagger/internal/codegen/langmodel",
         "//java/dagger/model",
         "//java/dagger/producers",
-        "@bazel_tools//third_party/java/jdk/langtools:javac",
         "@google_bazel_common//third_party/java/auto:common",
         "@google_bazel_common//third_party/java/auto:service",
         "@google_bazel_common//third_party/java/guava",
     ],
 )
 
+# Replacement for @bazel_tools//third_party/java/jdk/langtools:javac, which seems to have gone away?
+java_import(
+    name = "javac",
+    jars = ["@bazel_tools//third_party/java/jdk/langtools:javac_jar"],
+)
+
 # A _deploy.jar consisting of the java_librarys in https://github.com/kythe/kythe needed to build a
 # Kythe plugin
 # TODO(ronshapiro): replace this with a http_archive of the next release in
@@ -433,7 +471,6 @@
     name = "bootstrap_compiler_plugin",
     generates_api = 1,
     processor_class = "dagger.internal.codegen.ComponentProcessor",
-    visibility = ["//visibility:private"],
     deps = [":bootstrap_compiler"],
 )
 
@@ -452,7 +489,11 @@
     srcs = CODEGEN_SRCS,
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     plugins = CODEGEN_PLUGINS,
-    deps = CODEGEN_DEPS,
+    deps = CODEGEN_DEPS + [
+        "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/internal/codegen/javapoet",
+        "@google_bazel_common//third_party/java/incap",
+    ],
 )
 
 java_plugin(
@@ -468,3 +509,19 @@
     ],
     deps = [":processor"],
 )
+
+java_library(
+    name = "statistics",
+    srcs = STATISTICS_COLLECTOR_SRCS,
+    plugins = [":component-codegen"],
+    deps = [
+        ":base",
+        ":binding",
+        ":javac",
+        ":javac-plugin-module",
+        ":processor",
+        "//java/dagger:core",
+        "//java/dagger/model",
+        "@google_bazel_common//third_party/java/error_prone:check_api",
+    ],
+)
diff --git a/java/dagger/internal/codegen/Binding.java b/java/dagger/internal/codegen/Binding.java
index 46e8b75..c0f6b71 100644
--- a/java/dagger/internal/codegen/Binding.java
+++ b/java/dagger/internal/codegen/Binding.java
@@ -32,6 +32,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingKind;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
@@ -82,6 +83,11 @@
   /** The {@link BindingType} of this binding. */
   abstract BindingType bindingType();
 
+  /** The {@link FrameworkType} of this binding. */
+  final FrameworkType frameworkType() {
+    return FrameworkType.forBindingType(bindingType());
+  }
+
   /**
    * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding as
    * defined by the user-defined injection sites.
@@ -112,7 +118,7 @@
    * union of {@link #explicitDependencies()} and {@link #implicitDependencies()}. This returns an
    * unmodifiable set.
    */
-  ImmutableSet<DependencyRequest> dependencies() {
+  final ImmutableSet<DependencyRequest> dependencies() {
     return dependencies.get();
   }
 
@@ -158,7 +164,7 @@
   /* TODO(dpb): The stable-order postcondition is actually hard to verify in code for two equal
    * instances of Binding, because it really depends on the order of the binding's dependencies,
    * and two equal instances of Binding may have the same dependencies in a different order. */
-  ImmutableList<FrameworkDependency> frameworkDependencies() {
+  final ImmutableList<FrameworkDependency> frameworkDependencies() {
     return frameworkDependencies.get();
   }
 
@@ -206,7 +212,7 @@
    * multiple times if the {@linkplain Binding#unresolved() unresolved} binding requires it. If that
    * distinction is not important, the entries can be merged into a single mapping.
    */
-  ImmutableList<DependencyAssociation> dependencyAssociations() {
+  final ImmutableList<DependencyAssociation> dependencyAssociations() {
     return dependencyAssociations.get();
   }
 
@@ -230,7 +236,8 @@
    * Returns the mapping from each {@linkplain #dependencies dependency} to its associated {@link
    * FrameworkDependency}.
    */
-  ImmutableMap<DependencyRequest, FrameworkDependency> dependenciesToFrameworkDependenciesMap() {
+  final ImmutableMap<DependencyRequest, FrameworkDependency>
+      dependenciesToFrameworkDependenciesMap() {
     return frameworkDependenciesMap.get();
   }
 
diff --git a/java/dagger/internal/codegen/BindingDeclaration.java b/java/dagger/internal/codegen/BindingDeclaration.java
index d7aa1fb..c9520cd 100644
--- a/java/dagger/internal/codegen/BindingDeclaration.java
+++ b/java/dagger/internal/codegen/BindingDeclaration.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen;
 
+import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.model.BindingKind;
 import dagger.model.Key;
 import java.util.Optional;
@@ -48,7 +49,7 @@
    * The type enclosing the {@link #bindingElement()}, or {@link Optional#empty()} if {@link
    * #bindingElement()} is empty.
    */
-  Optional<TypeElement> bindingTypeElement() {
+  final Optional<TypeElement> bindingTypeElement() {
     return bindingElement().map(DaggerElements::closestEnclosingTypeElement);
   }
   
diff --git a/java/dagger/internal/codegen/BindingDeclarationFormatter.java b/java/dagger/internal/codegen/BindingDeclarationFormatter.java
index 1ba5d96..d850165 100644
--- a/java/dagger/internal/codegen/BindingDeclarationFormatter.java
+++ b/java/dagger/internal/codegen/BindingDeclarationFormatter.java
@@ -17,8 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.common.collect.Sets.immutableEnumSet;
-import static dagger.internal.codegen.DaggerElements.elementToString;
 import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
 import static javax.lang.model.element.ElementKind.PARAMETER;
 import static javax.lang.model.type.TypeKind.DECLARED;
 import static javax.lang.model.type.TypeKind.EXECUTABLE;
diff --git a/java/dagger/internal/codegen/BindingElementValidator.java b/java/dagger/internal/codegen/BindingElementValidator.java
new file mode 100644
index 0000000..0051912
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingElementValidator.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
+import static dagger.internal.codegen.MapKeys.getMapKeys;
+import static dagger.internal.codegen.Scopes.scopesOf;
+import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.lang.model.type.TypeKind.ARRAY;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.MapKey;
+import dagger.Provides;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.producers.Produces;
+import java.lang.annotation.Annotation;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for elements that represent binding declarations. */
+abstract class BindingElementValidator<E extends Element> {
+  private final Class<? extends Annotation> bindingAnnotation;
+  private final AllowsMultibindings allowsMultibindings;
+  private final AllowsScoping allowsScoping;
+  private final Map<E, ValidationReport<E>> cache = new HashMap<>();
+
+  /**
+   * Creates a validator object.
+   *
+   * @param bindingAnnotation the annotation on an element that identifies it as a binding element
+   */
+  protected BindingElementValidator(
+      Class<? extends Annotation> bindingAnnotation,
+      AllowsMultibindings allowsMultibindings,
+      AllowsScoping allowsScoping) {
+    this.bindingAnnotation = bindingAnnotation;
+    this.allowsMultibindings = allowsMultibindings;
+    this.allowsScoping = allowsScoping;
+  }
+
+  /** Returns a {@link ValidationReport} for {@code element}. */
+  final ValidationReport<E> validate(E element) {
+    return reentrantComputeIfAbsent(cache, element, this::validateUncached);
+  }
+
+  private ValidationReport<E> validateUncached(E element) {
+    return elementValidator(element).validate();
+  }
+
+  /**
+   * Returns an error message of the form "&lt;{@link #bindingElements()}&gt; <i>rule</i>", where
+   * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
+   * and the other arguments.
+   */
+  @FormatMethod
+  protected final String bindingElements(String ruleFormat, Object... args) {
+    return new Formatter().format("%s ", bindingElements()).format(ruleFormat, args).toString();
+  }
+
+  /**
+   * The kind of elements that this validator validates. Should be plural. Used for error reporting.
+   */
+  protected abstract String bindingElements();
+
+  /** The verb describing the {@link ElementValidator#bindingElementType()} in error messages. */
+  // TODO(ronshapiro,dpb): improve the name of this method and it's documentation.
+  protected abstract String bindingElementTypeVerb();
+
+  /** The error message when a binding element has a bad type. */
+  protected String badTypeMessage() {
+    return bindingElements(
+        "must %s a primitive, an array, a type variable, or a declared type",
+        bindingElementTypeVerb());
+  }
+
+  /**
+   * The error message when a the type for a binding element with {@link
+   * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
+   */
+  protected String elementsIntoSetNotASetMessage() {
+    return bindingElements(
+        "annotated with @ElementsIntoSet must %s a Set", bindingElementTypeVerb());
+  }
+
+  /**
+   * The error message when a the type for a binding element with {@link
+   * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
+   */
+  protected String elementsIntoSetRawSetMessage() {
+    return bindingElements(
+        "annotated with @ElementsIntoSet cannot %s a raw Set", bindingElementTypeVerb());
+  }
+
+  /*** Returns an {@link ElementValidator} for validating the given {@code element}. */
+  protected abstract ElementValidator elementValidator(E element);
+
+  /** Validator for a single binding element. */
+  protected abstract class ElementValidator {
+    protected final E element;
+    protected final ValidationReport.Builder<E> report;
+
+    protected ElementValidator(E element) {
+      this.element = element;
+      this.report = ValidationReport.about(element);
+    }
+
+    /** Checks the element for validity. */
+    private ValidationReport<E> validate() {
+      checkType();
+      checkQualifiers();
+      checkMapKeys();
+      checkMultibindings();
+      checkScopes();
+      checkAdditionalProperties();
+      return report.build();
+    }
+
+    /** Check any additional properties of the element. Does nothing by default. */
+    protected void checkAdditionalProperties() {}
+
+    /**
+     * The type declared by this binding element. This may differ from a binding's {@link
+     * Key#type()}, for example in multibindings. An {@link Optional#empty()} return value indicates
+     * that the contributed type is ambiguous or missing, i.e. a {@code @BindsInstance} method with
+     * zero or many parameters.
+     */
+    // TODO(dpb): should this be an ImmutableList<TypeMirror>, with this class checking the size?
+    protected abstract Optional<TypeMirror> bindingElementType();
+
+    /**
+     * Adds an error if the {@link #bindingElementType() binding element type} is not appropriate.
+     *
+     * <p>Adds an error if the type is not a primitive, array, declared type, or type variable.
+     *
+     * <p>If the binding is not a multibinding contribution, adds an error if the type is a
+     * framework type.
+     *
+     * <p>If the element has {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES}, adds an
+     * error if the type is not a {@code Set<T>} for some {@code T}
+     */
+    protected void checkType() {
+      switch (ContributionType.fromBindingElement(element)) {
+        case UNIQUE:
+          /* Validate that a unique binding is not attempting to bind a framework type. This
+           * validation is only appropriate for unique bindings because multibindings may collect
+           * framework types.  E.g. Set<Provider<Foo>> is perfectly reasonable. */
+          checkFrameworkType();
+          // fall through
+
+        case SET:
+        case MAP:
+          bindingElementType().ifPresent(type -> checkKeyType(type));
+          break;
+
+        case SET_VALUES:
+          checkSetValuesType();
+      }
+    }
+
+    /**
+     * Adds an error if {@code keyType} is not a primitive, declared type, array, or type variable.
+     */
+    protected void checkKeyType(TypeMirror keyType) {
+      TypeKind kind = keyType.getKind();
+      if (kind.equals(VOID)) {
+        report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
+      } else if (!(kind.isPrimitive()
+          || kind.equals(DECLARED)
+          || kind.equals(ARRAY)
+          || kind.equals(TYPEVAR))) {
+        report.addError(badTypeMessage());
+      }
+    }
+
+    /**
+     * Adds an error if the type for an element with {@link ElementsIntoSet @ElementsIntoSet} or
+     * {@code SET_VALUES} is not a a {@code Set<T>} for a reasonable {@code T}.
+     */
+    // TODO(gak): should we allow "covariant return" for set values?
+    protected void checkSetValuesType() {
+      bindingElementType().ifPresent(keyType -> checkSetValuesType(keyType));
+    }
+
+    /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
+    protected final void checkSetValuesType(TypeMirror type) {
+      if (!SetType.isSet(type)) {
+        report.addError(elementsIntoSetNotASetMessage());
+      } else {
+        SetType setType = SetType.from(type);
+        if (setType.isRawType()) {
+          report.addError(elementsIntoSetRawSetMessage());
+        } else {
+          checkKeyType(setType.elementType());
+        }
+      }
+    }
+
+    /**
+     * Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation.
+     */
+    private void checkQualifiers() {
+      ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(element);
+      if (qualifiers.size() > 1) {
+        for (AnnotationMirror qualifier : qualifiers) {
+          report.addError(
+              bindingElements("may not use more than one @Qualifier"),
+              element,
+              qualifier);
+        }
+      }
+    }
+
+    /**
+     * Adds an error if an {@link IntoMap @IntoMap} element doesn't have exactly one {@link
+     * MapKey @MapKey} annotation, or if an element that is {@link IntoMap @IntoMap} has any.
+     */
+    private void checkMapKeys() {
+      if (!allowsMultibindings.allowsMultibindings()) {
+        return;
+      }
+      ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(element);
+      if (ContributionType.fromBindingElement(element).equals(ContributionType.MAP)) {
+        switch (mapKeys.size()) {
+          case 0:
+            report.addError(bindingElements("of type map must declare a map key"));
+            break;
+          case 1:
+            break;
+          default:
+            report.addError(bindingElements("may not have more than one map key"));
+            break;
+        }
+      } else if (!mapKeys.isEmpty()) {
+        report.addError(bindingElements("of non map type cannot declare a map key"));
+      }
+    }
+
+    /**
+     * Adds errors if:
+     *
+     * <ul>
+     *   <li>the element doesn't allow {@linkplain MultibindingAnnotations multibinding annotations}
+     *       and has any
+     *   <li>the element does allow them but has more than one
+     *   <li>the element has a multibinding annotation and its {@link Provides} or {@link Produces}
+     *       annotation has a {@code type} parameter.
+     * </ul>
+     */
+    private void checkMultibindings() {
+      ImmutableSet<AnnotationMirror> multibindingAnnotations =
+          MultibindingAnnotations.forElement(element);
+
+      switch (allowsMultibindings) {
+        case NO_MULTIBINDINGS:
+          for (AnnotationMirror annotation : multibindingAnnotations) {
+            report.addError(
+                bindingElements("cannot have multibinding annotations"),
+                element,
+                annotation);
+          }
+          break;
+
+        case ALLOWS_MULTIBINDINGS:
+          if (multibindingAnnotations.size() > 1) {
+            for (AnnotationMirror annotation : multibindingAnnotations) {
+              report.addError(
+                  bindingElements("cannot have more than one multibinding annotation"),
+                  element,
+                  annotation);
+            }
+          }
+          break;
+      }
+
+      // TODO(ronshapiro): move this into ProvidesMethodValidator
+      if (bindingAnnotation.equals(Provides.class)) {
+        AnnotationMirror bindingAnnotationMirror =
+            getAnnotationMirror(element, bindingAnnotation).get();
+        boolean usesProvidesType = false;
+        for (ExecutableElement member : bindingAnnotationMirror.getElementValues().keySet()) {
+          usesProvidesType |= member.getSimpleName().contentEquals("type");
+        }
+        if (usesProvidesType && !multibindingAnnotations.isEmpty()) {
+          report.addError(
+              "@Provides.type cannot be used with multibinding annotations", element);
+        }
+      }
+    }
+
+    /**
+     * Adds an error if the element has a scope but doesn't allow scoping, or if it has more than
+     * one {@linkplain Scope scope} annotation.
+     */
+    private void checkScopes() {
+      ImmutableSet<Scope> scopes = scopesOf(element);
+      String error = null;
+      switch (allowsScoping) {
+        case ALLOWS_SCOPING:
+          if (scopes.size() <= 1) {
+            return;
+          }
+          error = bindingElements("cannot use more than one @Scope");
+          break;
+        case NO_SCOPING:
+          error = bindingElements("cannot be scoped");
+          break;
+      }
+      verifyNotNull(error);
+      for (Scope scope : scopes) {
+        report.addError(error, element, scope.scopeAnnotation());
+      }
+    }
+
+    /**
+     * Adds an error if the {@link #bindingElementType() type} is a {@linkplain FrameworkTypes
+     * framework type}.
+     */
+    private void checkFrameworkType() {
+      if (bindingElementType().filter(FrameworkTypes::isFrameworkType).isPresent()) {
+        report.addError(bindingElements("must not %s framework types", bindingElementTypeVerb()));
+      }
+    }
+  }
+
+  /** Whether to check multibinding annotations. */
+  enum AllowsMultibindings {
+    /**
+     * This element disallows multibinding annotations, so don't bother checking for their validity.
+     * {@link MultibindingAnnotationsProcessingStep} will add errors if the element has any
+     * multibinding annotations.
+     */
+    NO_MULTIBINDINGS,
+
+    /** This element allows multibinding annotations, so validate them. */
+    ALLOWS_MULTIBINDINGS,
+    ;
+
+    private boolean allowsMultibindings() {
+      return this == ALLOWS_MULTIBINDINGS;
+    }
+  }
+
+  /** How to check scoping annotations. */
+  enum AllowsScoping {
+    /** This element disallows scoping, so check that no scope annotations are present. */
+    NO_SCOPING,
+
+    /** This element allows scoping, so validate that there's at most one scope annotation. */
+    ALLOWS_SCOPING,
+    ;
+  }
+}
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 9ac6edc..65200f7 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -16,12 +16,14 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.TypeMirror;
 
 /** A factory of code expressions used to access a single request for a binding in a component. */
diff --git a/java/dagger/internal/codegen/BindingFactory.java b/java/dagger/internal/codegen/BindingFactory.java
index 5405bfd..564b412 100644
--- a/java/dagger/internal/codegen/BindingFactory.java
+++ b/java/dagger/internal/codegen/BindingFactory.java
@@ -26,7 +26,6 @@
 import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
 import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
 import static dagger.internal.codegen.ContributionBinding.bindingKindForMultibindingKey;
-import static dagger.internal.codegen.DaggerElements.DECLARATION_ORDER;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
 import static dagger.internal.codegen.MapKeys.getMapKey;
@@ -46,49 +45,40 @@
 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 javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
 
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.SetMultimap;
 import dagger.Module;
 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
 import dagger.internal.codegen.ProductionBinding.ProductionKind;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import dagger.producers.Produced;
 import dagger.producers.Producer;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.BiFunction;
 import javax.inject.Inject;
 import javax.inject.Provider;
 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.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.ExecutableType;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor6;
 
 /** A factory for {@link Binding} objects. */
 final class BindingFactory {
   private final DaggerTypes types;
   private final KeyFactory keyFactory;
   private final DependencyRequestFactory dependencyRequestFactory;
+  private final InjectionSiteFactory injectionSiteFactory;
   private final DaggerElements elements;
 
   @Inject
@@ -96,11 +86,13 @@
       DaggerTypes types,
       DaggerElements elements,
       KeyFactory keyFactory,
-      DependencyRequestFactory dependencyRequestFactory) {
+      DependencyRequestFactory dependencyRequestFactory,
+      InjectionSiteFactory injectionSiteFactory) {
     this.types = types;
     this.elements = elements;
     this.keyFactory = keyFactory;
     this.dependencyRequestFactory = dependencyRequestFactory;
+    this.injectionSiteFactory = injectionSiteFactory;
   }
 
   /**
@@ -144,7 +136,7 @@
             .bindingElement(constructorElement)
             .key(key)
             .provisionDependencies(provisionDependencies)
-            .injectionSites(getInjectionSites(constructedType))
+            .injectionSites(injectionSiteFactory.getInjectionSites(constructedType))
             .kind(INJECTION)
             .scope(uniqueScopeOf(constructorElement.getEnclosingElement()));
 
@@ -214,7 +206,7 @@
       builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement())));
     }
     return builder
-        .contributionType(ContributionType.fromBindingMethod(method))
+        .contributionType(ContributionType.fromBindingElement(method))
         .bindingElement(method)
         .contributingModule(contributedBy)
         .key(key)
@@ -493,7 +485,8 @@
           types.erasure(declaredType));
       declaredType = resolved;
     }
-    ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType);
+    ImmutableSortedSet<InjectionSite> injectionSites =
+        injectionSiteFactory.getInjectionSites(declaredType);
     ImmutableSet<DependencyRequest> dependencies =
         injectionSites
             .stream()
@@ -512,115 +505,4 @@
             : Optional.empty(),
         injectionSites);
   }
-
-  private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
-      new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>(Optional.empty()) {
-        @Override
-        public Optional<InjectionSite> visitExecutableAsMethod(
-            ExecutableElement e, DeclaredType type) {
-          return Optional.of(injectionSiteForInjectMethod(e, type));
-        }
-
-        @Override
-        public Optional<InjectionSite> visitVariableAsField(VariableElement e, DeclaredType type) {
-          return (isAnnotationPresent(e, Inject.class)
-                  && !e.getModifiers().contains(PRIVATE)
-                  && !e.getModifiers().contains(STATIC))
-              ? Optional.of(injectionSiteForInjectField(e, type))
-              : Optional.empty();
-        }
-      };
-
-  private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
-    Set<InjectionSite> injectionSites = new HashSet<>();
-    List<TypeElement> ancestors = new ArrayList<>();
-    SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
-    for (Optional<DeclaredType> currentType = Optional.of(declaredType);
-        currentType.isPresent();
-        currentType = types.nonObjectSuperclass(currentType.get())) {
-      DeclaredType type = currentType.get();
-      ancestors.add(MoreElements.asType(type.asElement()));
-      for (Element enclosedElement : type.asElement().getEnclosedElements()) {
-        Optional<InjectionSite> maybeInjectionSite =
-            injectionSiteVisitor.visit(enclosedElement, type);
-        if (maybeInjectionSite.isPresent()) {
-          InjectionSite injectionSite = maybeInjectionSite.get();
-          if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
-            injectionSites.add(injectionSite);
-          }
-          if (injectionSite.kind().equals(InjectionSite.Kind.METHOD)) {
-            ExecutableElement injectionSiteMethod =
-                MoreElements.asExecutable(injectionSite.element());
-            overriddenMethodMap.put(
-                injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
-          }
-        }
-      }
-    }
-    return ImmutableSortedSet.copyOf(
-        // supertypes before subtypes
-        Comparator.comparing(
-                (InjectionSite injectionSite) ->
-                    ancestors.indexOf(injectionSite.element().getEnclosingElement()))
-            .reversed()
-            // fields before methods
-            .thenComparing(injectionSite -> injectionSite.element().getKind())
-            // then sort by whichever element comes first in the parent
-            // this isn't necessary, but makes the processor nice and predictable
-            .thenComparing(InjectionSite::element, DECLARATION_ORDER),
-        injectionSites);
-  }
-
-  private boolean shouldBeInjected(
-      Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
-    if (!isAnnotationPresent(injectionSite, Inject.class)
-        || injectionSite.getModifiers().contains(PRIVATE)
-        || injectionSite.getModifiers().contains(STATIC)) {
-      return false;
-    }
-
-    if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
-      return true;
-    }
-
-    // For each method with the same name belonging to any descendant class, return false if any
-    // method has already overridden the injectionSite method. To decrease the number of methods
-    // that are checked, we store the already injected methods in a SetMultimap and only
-    // check the methods with the same name.
-    ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
-    TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
-    for (ExecutableElement method :
-        overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
-      if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private InjectionSite injectionSiteForInjectMethod(
-      ExecutableElement methodElement, DeclaredType containingType) {
-    checkNotNull(methodElement);
-    checkArgument(methodElement.getKind().equals(ElementKind.METHOD));
-    ExecutableType resolved =
-        MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement));
-    return new AutoValue_MembersInjectionBinding_InjectionSite(
-        InjectionSite.Kind.METHOD,
-        methodElement,
-        dependencyRequestFactory.forRequiredResolvedVariables(
-            methodElement.getParameters(), resolved.getParameterTypes()));
-  }
-
-  private InjectionSite injectionSiteForInjectField(
-      VariableElement fieldElement, DeclaredType containingType) {
-    checkNotNull(fieldElement);
-    checkArgument(fieldElement.getKind().equals(ElementKind.FIELD));
-    checkArgument(isAnnotationPresent(fieldElement, Inject.class));
-    TypeMirror resolved = types.asMemberOf(containingType, fieldElement);
-    return new AutoValue_MembersInjectionBinding_InjectionSite(
-        InjectionSite.Kind.FIELD,
-        fieldElement,
-        ImmutableSet.of(
-            dependencyRequestFactory.forRequiredResolvedVariable(fieldElement, resolved)));
-  }
 }
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index a02cee2..2f548b2 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -35,8 +35,10 @@
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
@@ -201,19 +203,19 @@
 
   static BindingGraph create(
       ComponentDescriptor componentDescriptor,
-      ImmutableMap<Key, ResolvedBindings> resolvedContributionBindingsMap,
-      ImmutableMap<Key, ResolvedBindings> resolvedMembersInjectionBindings,
-      ImmutableList<BindingGraph> subgraphs,
-      ImmutableSet<ModuleDescriptor> ownedModules,
+      Map<Key, ResolvedBindings> resolvedContributionBindingsMap,
+      Map<Key, ResolvedBindings> resolvedMembersInjectionBindings,
+      List<BindingGraph> subgraphs,
+      Set<ModuleDescriptor> ownedModules,
       Optional<ExecutableElement> factoryMethod,
       boolean isFullBindingGraph) {
     checkForDuplicates(subgraphs);
     return new AutoValue_BindingGraph(
         componentDescriptor,
-        resolvedContributionBindingsMap,
-        resolvedMembersInjectionBindings,
-        subgraphs,
-        ownedModules,
+        ImmutableMap.copyOf(resolvedContributionBindingsMap),
+        ImmutableMap.copyOf(resolvedMembersInjectionBindings),
+        ImmutableList.copyOf(subgraphs),
+        ImmutableSet.copyOf(ownedModules),
         factoryMethod,
         isFullBindingGraph);
   }
diff --git a/java/dagger/internal/codegen/BindingGraphConverter.java b/java/dagger/internal/codegen/BindingGraphConverter.java
index 28cf9a1..a2cc799 100644
--- a/java/dagger/internal/codegen/BindingGraphConverter.java
+++ b/java/dagger/internal/codegen/BindingGraphConverter.java
@@ -19,10 +19,9 @@
 import static com.google.auto.common.MoreTypes.asTypeElement;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.DaggerGraphs.unreachableNodes;
-import static dagger.internal.codegen.DaggerStreams.presentValues;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.graph.MutableNetwork;
@@ -34,6 +33,7 @@
 import dagger.model.BindingGraph.MissingBinding;
 import dagger.model.BindingGraph.Node;
 import dagger.model.BindingGraphProxies;
+import dagger.model.ComponentPath;
 import dagger.model.DependencyRequest;
 import javax.inject.Inject;
 import javax.lang.model.element.ExecutableElement;
@@ -42,15 +42,11 @@
 
 /** Converts {@link dagger.internal.codegen.BindingGraph}s to {@link dagger.model.BindingGraph}s. */
 final class BindingGraphConverter {
-
   private final BindingDeclarationFormatter bindingDeclarationFormatter;
-  private final CompilerOptions compilerOptions;
 
   @Inject
-  BindingGraphConverter(
-      BindingDeclarationFormatter bindingDeclarationFormatter, CompilerOptions compilerOptions) {
+  BindingGraphConverter(BindingDeclarationFormatter bindingDeclarationFormatter) {
     this.bindingDeclarationFormatter = bindingDeclarationFormatter;
-    this.compilerOptions = compilerOptions;
   }
 
   /**
@@ -83,15 +79,20 @@
   }
 
   private final class Traverser extends ComponentTreeTraverser {
-
     private final MutableNetwork<Node, Edge> network =
         NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
+    private final boolean isRootSubcomponent;
+    private final boolean isFullBindingGraph;
 
+    private final ComponentPath rootComponentPath;
     private ComponentNode parentComponent;
     private ComponentNode currentComponent;
 
     Traverser(BindingGraph graph) {
-      super(graph, compilerOptions);
+      super(graph);
+      rootComponentPath = ComponentPath.create(ImmutableList.of(graph.componentTypeElement()));
+      isRootSubcomponent = graph.componentDescriptor().isSubcomponent();
+      isFullBindingGraph = graph.isFullBindingGraph();
     }
 
     @Override
@@ -103,7 +104,6 @@
       network.addNode(currentComponent);
 
       for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
-        ImmutableSet<TypeElement> declaringModules = subcomponentDeclaringModules(resolvedBindings);
         for (BindingNode binding : bindingNodes(resolvedBindings)) {
           addBinding(binding);
           if (binding.kind().equals(SUBCOMPONENT_CREATOR)
@@ -111,7 +111,8 @@
             network.addEdge(
                 binding,
                 subcomponentNode(binding.key().type(), graph),
-                new SubcomponentCreatorBindingEdgeImpl(declaringModules));
+                new SubcomponentCreatorBindingEdgeImpl(
+                    resolvedBindings.subcomponentDeclarations()));
           }
         }
       }
@@ -219,7 +220,12 @@
     }
 
     private MissingBinding missingBindingNode(ResolvedBindings dependencies) {
-      return BindingGraphProxies.missingBindingNode(componentPath(), dependencies.key());
+      // TODO(b/117833324): Revisit whether limiting missing binding nodes to the root component is
+      // necessary to limit the amount of missing binding nodes in the network, or if perhaps *all*
+      // missing binding nodes should be structured this way.
+      return BindingGraphProxies.missingBindingNode(
+          isRootSubcomponent && !isFullBindingGraph ? rootComponentPath : componentPath(),
+          dependencies.key());
     }
 
     private ComponentNode subcomponentNode(TypeMirror subcomponentBuilderType, BindingGraph graph) {
@@ -229,15 +235,5 @@
       return ComponentNodeImpl.create(
           componentPath().childPath(subcomponent.typeElement()), subcomponent);
     }
-
-    private ImmutableSet<TypeElement> subcomponentDeclaringModules(
-        ResolvedBindings resolvedBindings) {
-      return resolvedBindings
-          .subcomponentDeclarations()
-          .stream()
-          .map(SubcomponentDeclaration::contributingModule)
-          .flatMap(presentValues())
-          .collect(toImmutableSet());
-    }
   }
 }
diff --git a/java/dagger/internal/codegen/BindingGraphFactory.java b/java/dagger/internal/codegen/BindingGraphFactory.java
index 311f9dd..d96da8a 100644
--- a/java/dagger/internal/codegen/BindingGraphFactory.java
+++ b/java/dagger/internal/codegen/BindingGraphFactory.java
@@ -40,11 +40,11 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
 import dagger.MembersInjector;
 import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.Scope;
@@ -903,17 +903,11 @@
      * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
      * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
      */
-    ImmutableMap<Key, ResolvedBindings> getResolvedContributionBindings() {
-      ImmutableMap.Builder<Key, ResolvedBindings> builder = ImmutableMap.builder();
-      builder.putAll(resolvedContributionBindings);
-      if (parentResolver.isPresent()) {
-        ImmutableMap<Key, ResolvedBindings> parentBindings =
-            parentResolver.get().getResolvedContributionBindings();
-        Map<Key, ResolvedBindings> bindingsResolvedInParent =
-            Maps.difference(parentBindings, resolvedContributionBindings).entriesOnlyOnLeft();
-        builder.putAll(bindingsResolvedInParent);
-      }
-      return builder.build();
+    Map<Key, ResolvedBindings> getResolvedContributionBindings() {
+      Map<Key, ResolvedBindings> bindings = new LinkedHashMap<>();
+      parentResolver.ifPresent(parent -> bindings.putAll(parent.getResolvedContributionBindings()));
+      bindings.putAll(resolvedContributionBindings);
+      return bindings;
     }
 
     /**
diff --git a/java/dagger/internal/codegen/BindingGraphPlugins.java b/java/dagger/internal/codegen/BindingGraphPlugins.java
index ae4ef96..e2c3812 100644
--- a/java/dagger/internal/codegen/BindingGraphPlugins.java
+++ b/java/dagger/internal/codegen/BindingGraphPlugins.java
@@ -21,6 +21,8 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.spi.BindingGraphPlugin;
 import java.util.Map;
 import java.util.Set;
diff --git a/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java b/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
new file mode 100644
index 0000000..129647f
--- /dev/null
+++ b/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.errorprone.util.ASTHelpers.getSymbol;
+import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotation;
+
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.sun.source.tree.ClassTree;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.util.Context;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.model.BindingGraph;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A {@link BugChecker} that collects statistics derived from a {@link BindingGraph}. */
+public abstract class BindingGraphStatisticsCollector extends BugChecker
+    implements ClassTreeMatcher {
+  private BindingGraphConverter bindingGraphConverter;
+  private BindingGraphFactory bindingGraphFactory;
+  private ComponentDescriptorFactory componentDescriptorFactory;
+  private boolean isInjected;
+
+  @Singleton
+  @Component(modules = JavacPluginModule.class)
+  interface Injector {
+    void inject(BindingGraphStatisticsCollector collector);
+
+    @Component.Factory
+    interface Factory {
+      Injector create(@BindsInstance Context context);
+    }
+  }
+
+  // BugCheckers must have no-arg constructors, so we'll use method injection instead.
+  @Inject
+  void inject(
+      BindingGraphConverter bindingGraphConverter,
+      BindingGraphFactory bindingGraphFactory,
+      ComponentDescriptorFactory componentDescriptorFactory) {
+    this.bindingGraphConverter = bindingGraphConverter;
+    this.bindingGraphFactory = bindingGraphFactory;
+    this.componentDescriptorFactory = componentDescriptorFactory;
+  }
+
+  @Override
+  public final Description matchClass(ClassTree tree, VisitorState state) {
+    injectIfNecessary(state.context);
+
+    ClassSymbol symbol = getSymbol(tree);
+    rootComponentAnnotation(symbol)
+        .map(annotation -> createBindingGraph(symbol))
+        .ifPresent(graph -> visitBindingGraph(graph, state));
+
+    return Description.NO_MATCH;
+  }
+
+  private BindingGraph createBindingGraph(ClassSymbol component) {
+    return bindingGraphConverter.convert(
+        bindingGraphFactory.create(
+            componentDescriptorFactory.rootComponentDescriptor(component), false));
+  }
+
+  /** Visits a {@link BindingGraph} and emits stats to a {@link VisitorState}. */
+  protected abstract void visitBindingGraph(BindingGraph graph, VisitorState state);
+
+  private void injectIfNecessary(Context context) {
+    if (isInjected) {
+      return;
+    }
+    DaggerBindingGraphStatisticsCollector_Injector.factory().create(context).inject(this);
+    isInjected = true;
+  }
+}
diff --git a/java/dagger/internal/codegen/BindingMethodValidator.java b/java/dagger/internal/codegen/BindingMethodValidator.java
index 4b82979..21c05cc 100644
--- a/java/dagger/internal/codegen/BindingMethodValidator.java
+++ b/java/dagger/internal/codegen/BindingMethodValidator.java
@@ -16,43 +16,23 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.Verify.verifyNotNull;
-import static dagger.internal.codegen.DaggerElements.getAnnotationMirror;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.MapKeys.getMapKeys;
-import static dagger.internal.codegen.Scopes.scopesOf;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 import static java.util.stream.Collectors.joining;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.type.TypeKind.ARRAY;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.TYPEVAR;
-import static javax.lang.model.type.TypeKind.VOID;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.errorprone.annotations.FormatMethod;
-import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper;
-import dagger.MapKey;
-import dagger.Provides;
-import dagger.model.Scope;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.producers.Produces;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.lang.annotation.Annotation;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.Map;
-import javax.inject.Qualifier;
-import javax.lang.model.element.AnnotationMirror;
+import java.util.Optional;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 
 /** A validator for methods that represent binding declarations. */
-abstract class BindingMethodValidator {
+abstract class BindingMethodValidator extends BindingElementValidator<ExecutableElement> {
 
   private final DaggerElements elements;
   private final DaggerTypes types;
@@ -61,9 +41,6 @@
   private final ImmutableSet<? extends Class<? extends Annotation>> enclosingElementAnnotations;
   private final Abstractness abstractness;
   private final ExceptionSuperclass exceptionSuperclass;
-  private final Map<ExecutableElement, ValidationReport<ExecutableElement>> cache = new HashMap<>();
-  private final AllowsMultibindings allowsMultibindings;
-  private final AllowsScoping allowsScoping;
 
   /**
    * Creates a validator object.
@@ -111,6 +88,7 @@
       ExceptionSuperclass exceptionSuperclass,
       AllowsMultibindings allowsMultibindings,
       AllowsScoping allowsScoping) {
+    super(methodAnnotation, allowsMultibindings, allowsScoping);
     this.elements = elements;
     this.types = types;
     this.methodAnnotation = methodAnnotation;
@@ -118,8 +96,6 @@
     this.dependencyRequestValidator = dependencyRequestValidator;
     this.abstractness = abstractness;
     this.exceptionSuperclass = exceptionSuperclass;
-    this.allowsMultibindings = allowsMultibindings;
-    this.allowsScoping = allowsScoping;
   }
 
   /** The annotation that identifies binding methods validated by this object. */
@@ -134,314 +110,115 @@
    */
   @FormatMethod
   protected final String bindingMethods(String ruleFormat, Object... args) {
-    return new Formatter()
-        .format("@%s methods ", methodAnnotation.getSimpleName())
-        .format(ruleFormat, args)
-        .toString();
+    return bindingElements(ruleFormat, args);
   }
 
-  /** Returns a {@link ValidationReport} for {@code method}. */
-  final ValidationReport<ExecutableElement> validate(ExecutableElement method) {
-    return reentrantComputeIfAbsent(cache, method, this::validateUncached);
+  @Override
+  protected final String bindingElements() {
+    return String.format("@%s methods", methodAnnotation.getSimpleName());
   }
 
-  private ValidationReport<ExecutableElement> validateUncached(ExecutableElement m) {
-    ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(m);
-    checkMethod(report);
-    return report.build();
+  @Override
+  protected final String bindingElementTypeVerb() {
+    return "return";
   }
 
-  /** Checks the method for validity. Adds errors to {@code builder}. */
-  @OverridingMethodsMustInvokeSuper
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    checkEnclosingElement(builder);
-    checkTypeParameters(builder);
-    checkNotPrivate(builder);
-    checkAbstractness(builder);
-    checkReturnType(builder);
-    checkThrows(builder);
-    checkQualifiers(builder);
-    checkMapKeys(builder);
-    checkMultibindings(builder);
-    checkScopes(builder);
-    checkParameters(builder);
-  }
-
-  /**
-   * Adds an error if the method is not declared in a class or interface annotated with one of the
-   * {@link #enclosingElementAnnotations}.
-   */
-  private void checkEnclosingElement(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!isAnyAnnotationPresent(
-        builder.getSubject().getEnclosingElement(), enclosingElementAnnotations)) {
-      builder.addError(
-          bindingMethods(
-              "can only be present within a @%s",
-              enclosingElementAnnotations
-                  .stream()
-                  .map(Class::getSimpleName)
-                  .collect(joining(" or @"))));
+  /** Abstract validator for individual binding method elements. */
+  protected abstract class MethodValidator extends ElementValidator {
+    protected MethodValidator(ExecutableElement element) {
+      super(element);
     }
-  }
 
-  /** Adds an error if the method is generic. */
-  private void checkTypeParameters(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!builder.getSubject().getTypeParameters().isEmpty()) {
-      builder.addError(bindingMethods("may not have type parameters"));
+    @Override
+    protected final Optional<TypeMirror> bindingElementType() {
+      return Optional.of(element.getReturnType());
     }
-  }
 
-  /** Adds an error if the method is private. */
-  private void checkNotPrivate(ValidationReport.Builder<ExecutableElement> builder) {
-    if (builder.getSubject().getModifiers().contains(PRIVATE)) {
-      builder.addError(bindingMethods("cannot be private"));
+    @Override
+    protected final void checkAdditionalProperties() {
+      checkEnclosingElement();
+      checkTypeParameters();
+      checkNotPrivate();
+      checkAbstractness();
+      checkThrows();
+      checkParameters();
+      checkAdditionalMethodProperties();
     }
-  }
 
-  /** Adds an error if the method is abstract but must not be, or is not and must be. */
-  private void checkAbstractness(ValidationReport.Builder<ExecutableElement> builder) {
-    boolean isAbstract = builder.getSubject().getModifiers().contains(ABSTRACT);
-    switch (abstractness) {
-      case MUST_BE_ABSTRACT:
-        if (!isAbstract) {
-          builder.addError(bindingMethods("must be abstract"));
-        }
-        break;
+    /** Checks additional properties of the binding method. */
+    protected void checkAdditionalMethodProperties() {}
 
-      case MUST_BE_CONCRETE:
-        if (isAbstract) {
-          builder.addError(bindingMethods("cannot be abstract"));
-        }
-        break;
-
-      default:
-        throw new AssertionError();
-    }
-  }
-
-  /**
-   * Adds an error if the return type is not appropriate for the method.
-   *
-   * <p>Adds an error if the method doesn't return a primitive, array, declared type, or type
-   * variable.
-   *
-   * <p>If the method is not a multibinding contribution, adds an error if it returns a framework
-   * type.
-   *
-   * <p>If the method is a {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES}
-   * contribution, adds an error if the method doesn't return a {@code Set<T>} for some {@code T}
-   */
-  protected void checkReturnType(ValidationReport.Builder<ExecutableElement> builder) {
-    switch (ContributionType.fromBindingMethod(builder.getSubject())) {
-      case UNIQUE:
-        /* Validate that a unique binding is not attempting to bind a framework type. This
-         * validation is only appropriate for unique bindings because multibindings may collect
-         * framework types.  E.g. Set<Provider<Foo>> is perfectly reasonable. */
-        checkFrameworkType(builder);
-        // fall through
-
-      case SET:
-      case MAP:
-        checkKeyType(builder, builder.getSubject().getReturnType());
-        break;
-
-      case SET_VALUES:
-        checkSetValuesType(builder);
-        break;
-
-      default:
-        throw new AssertionError();
-    }
-  }
-
-  /**
-   * Adds an error if {@code keyType} is not a primitive, declared type, array, or type variable.
-   */
-  protected void checkKeyType(
-      ValidationReport.Builder<ExecutableElement> builder, TypeMirror keyType) {
-    TypeKind kind = keyType.getKind();
-    if (kind.equals(VOID)) {
-      builder.addError(bindingMethods("must return a value (not void)"));
-    } else if (!(kind.isPrimitive()
-        || kind.equals(DECLARED)
-        || kind.equals(ARRAY)
-        || kind.equals(TYPEVAR))) {
-      builder.addError(badReturnTypeMessage());
-    }
-  }
-
-  /** The error message when a non-{@code void} binding method returns a bad type. */
-  protected String badReturnTypeMessage() {
-    return bindingMethods("must return a primitive, an array, a type variable, or a declared type");
-  }
-
-  /**
-   * Adds an error if an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method
-   * doesn't return a {@code Set<T>} for a reasonable {@code T}.
-   */
-  // TODO(gak): should we allow "covariant return" for set values?
-  protected void checkSetValuesType(ValidationReport.Builder<ExecutableElement> builder) {
-    checkSetValuesType(builder, builder.getSubject().getReturnType());
-  }
-
-  /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
-  protected final void checkSetValuesType(
-      ValidationReport.Builder<ExecutableElement> builder, TypeMirror type) {
-    if (!SetType.isSet(type)) {
-      builder.addError(badSetValuesTypeMessage());
-    } else {
-      SetType setType = SetType.from(type);
-      if (setType.isRawType()) {
-        builder.addError(bindingMethods("annotated with @ElementsIntoSet cannot return a raw Set"));
-      } else {
-        checkKeyType(builder, setType.elementType());
+    /**
+     * Adds an error if the method is not declared in a class or interface annotated with one of the
+     * {@link #enclosingElementAnnotations}.
+     */
+    private void checkEnclosingElement() {
+      if (!isAnyAnnotationPresent(
+          element.getEnclosingElement(), enclosingElementAnnotations)) {
+        report.addError(
+            bindingMethods(
+                "can only be present within a @%s",
+                enclosingElementAnnotations.stream()
+                    .map(Class::getSimpleName)
+                    .collect(joining(" or @"))));
       }
     }
-  }
 
-  /**
-   * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
-   * subtype of {@link Exception}.
-   */
-  private void checkThrows(ValidationReport.Builder<ExecutableElement> builder) {
-    exceptionSuperclass.checkThrows(this, builder);
-  }
-
-  /** Adds an error if the method has more than one {@linkplain Qualifier qualifier} annotation. */
-  protected void checkQualifiers(ValidationReport.Builder<ExecutableElement> builder) {
-    ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(builder.getSubject());
-    if (qualifiers.size() > 1) {
-      for (AnnotationMirror qualifier : qualifiers) {
-        builder.addError(
-            bindingMethods("may not use more than one @Qualifier"),
-            builder.getSubject(),
-            qualifier);
+    /** Adds an error if the method is generic. */
+    private void checkTypeParameters() {
+      if (!element.getTypeParameters().isEmpty()) {
+        report.addError(bindingMethods("may not have type parameters"));
       }
     }
-  }
 
-  /**
-   * Adds an error if an {@link IntoMap @IntoMap} or {@code MAP} method doesn't have exactly one
-   * {@link MapKey @MapKey} annotation, or if a method that is neither {@link IntoMap @IntoMap} nor
-   * {@code MAP} has any.
-   */
-  protected void checkMapKeys(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!allowsMultibindings.allowsMultibindings()) {
-      return;
-    }
-    ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(builder.getSubject());
-    if (ContributionType.fromBindingMethod(builder.getSubject()).equals(ContributionType.MAP)) {
-      switch (mapKeys.size()) {
-        case 0:
-          builder.addError(bindingMethods("of type map must declare a map key"));
-          break;
-        case 1:
-          break;
-        default:
-          builder.addError(bindingMethods("may not have more than one map key"));
-          break;
+    /** Adds an error if the method is private. */
+    private void checkNotPrivate() {
+      if (element.getModifiers().contains(PRIVATE)) {
+        report.addError(bindingMethods("cannot be private"));
       }
-    } else if (!mapKeys.isEmpty()) {
-      builder.addError(bindingMethods("of non map type cannot declare a map key"));
     }
-  }
 
-  /**
-   * Adds errors if the method doesn't allow {@linkplain MultibindingAnnotations multibinding
-   * annotations} and has any, or if it does allow them but has more than one, or if it has a
-   * multibinding annotation and its {@link Provides} or {@link Produces} annotation has a {@code
-   * type} parameter.
-   */
-  protected void checkMultibindings(ValidationReport.Builder<ExecutableElement> builder) {
-    ImmutableSet<AnnotationMirror> multibindingAnnotations =
-        MultibindingAnnotations.forMethod(builder.getSubject());
-
-    switch (allowsMultibindings) {
-      case NO_MULTIBINDINGS:
-        for (AnnotationMirror annotation : multibindingAnnotations) {
-          builder.addError(
-              bindingMethods("cannot have multibinding annotations"),
-              builder.getSubject(),
-              annotation);
-        }
-        break;
-
-      case ALLOWS_MULTIBINDINGS:
-        if (multibindingAnnotations.size() > 1) {
-          for (AnnotationMirror annotation : multibindingAnnotations) {
-            builder.addError(
-                bindingMethods("cannot have more than one multibinding annotation"),
-                builder.getSubject(),
-                annotation);
+    /** Adds an error if the method is abstract but must not be, or is not and must be. */
+    private void checkAbstractness() {
+      boolean isAbstract = element.getModifiers().contains(ABSTRACT);
+      switch (abstractness) {
+        case MUST_BE_ABSTRACT:
+          if (!isAbstract) {
+            report.addError(bindingMethods("must be abstract"));
           }
-        }
-        break;
+          break;
+
+        case MUST_BE_CONCRETE:
+          if (isAbstract) {
+            report.addError(bindingMethods("cannot be abstract"));
+          }
+      }
     }
 
-    AnnotationMirror bindingAnnotationMirror =
-        getAnnotationMirror(builder.getSubject(), methodAnnotation).get();
-    boolean usesProvidesType = false;
-    for (ExecutableElement member : bindingAnnotationMirror.getElementValues().keySet()) {
-      usesProvidesType |= member.getSimpleName().contentEquals("type");
+    /**
+     * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
+     * subtype of {@link Exception}.
+     */
+    private void checkThrows() {
+      exceptionSuperclass.checkThrows(BindingMethodValidator.this, element, report);
     }
-    if (usesProvidesType && !multibindingAnnotations.isEmpty()) {
-      builder.addError(
-          "@Provides.type cannot be used with multibinding annotations", builder.getSubject());
-    }
-  }
 
-  /** Adds an error if the method has more than one {@linkplain Scope scope} annotation. */
-  private void checkScopes(ValidationReport.Builder<ExecutableElement> builder) {
-    ImmutableSet<Scope> scopes = scopesOf(builder.getSubject());
-    String error = null;
-    switch (allowsScoping) {
-      case ALLOWS_SCOPING:
-        if (scopes.size() <= 1) {
-          return;
-        }
-        error = bindingMethods("cannot use more than one @Scope");
-        break;
-      case NO_SCOPING:
-        error = bindingMethods("cannot be scoped");
-        break;
+    /** Adds errors for the method parameters. */
+    protected void checkParameters() {
+      for (VariableElement parameter : element.getParameters()) {
+        checkParameter(parameter);
+      }
     }
-    verifyNotNull(error);
-    for (Scope scope : scopes) {
-      builder.addError(error, builder.getSubject(), scope.scopeAnnotation());
+
+    /**
+     * 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());
     }
   }
 
-  /** Adds errors for the method parameters. */
-  protected void checkParameters(ValidationReport.Builder<ExecutableElement> builder) {
-    for (VariableElement parameter : builder.getSubject().getParameters()) {
-      checkParameter(builder, parameter);
-    }
-  }
-
-  /**
-   * Adds errors for a method parameter. This implementation reports an error if the parameter has
-   * more than one qualifier.
-   */
-  protected void checkParameter(
-      ValidationReport.Builder<ExecutableElement> builder, VariableElement parameter) {
-    dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.asType());
-  }
-
-  /** Adds an error if the method returns a {@linkplain FrameworkTypes framework type}. */
-  protected void checkFrameworkType(ValidationReport.Builder<ExecutableElement> builder) {
-    if (FrameworkTypes.isFrameworkType(builder.getSubject().getReturnType())) {
-      builder.addError(bindingMethods("must not return framework types"));
-    }
-  }
-
-  /**
-   * The error message when an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method
-   * returns a bad type.
-   */
-  protected String badSetValuesTypeMessage() {
-    return bindingMethods("annotated with @ElementsIntoSet must return a Set");
-  }
-
   /** An abstract/concrete restriction on methods. */
   protected enum Abstractness {
     MUST_BE_ABSTRACT,
@@ -462,9 +239,11 @@
 
       @Override
       protected void checkThrows(
-          BindingMethodValidator validator, ValidationReport.Builder<ExecutableElement> builder) {
-        if (!builder.getSubject().getThrownTypes().isEmpty()) {
-          builder.addError(validator.bindingMethods("may not throw"));
+          BindingMethodValidator validator,
+          ExecutableElement element,
+          ValidationReport.Builder<ExecutableElement> report) {
+        if (!element.getThrownTypes().isEmpty()) {
+          report.addError(validator.bindingMethods("may not throw"));
           return;
         }
       }
@@ -505,13 +284,15 @@
      * <p>This method is overridden in {@link #NO_EXCEPTIONS}.
      */
     protected void checkThrows(
-        BindingMethodValidator validator, ValidationReport.Builder<ExecutableElement> builder) {
+        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 : builder.getSubject().getThrownTypes()) {
+      for (TypeMirror thrownType : element.getThrownTypes()) {
         if (!validator.types.isSubtype(thrownType, exceptionSupertype)
             && !validator.types.isSubtype(thrownType, errorType)) {
-          builder.addError(errorMessage(validator));
+          report.addError(errorMessage(validator));
           break;
         }
       }
@@ -519,32 +300,4 @@
 
     protected abstract String errorMessage(BindingMethodValidator validator);
   }
-
-  /** Whether to check multibinding annotations. */
-  protected enum AllowsMultibindings {
-    /**
-     * This method disallows multibinding annotations, so don't bother checking for their validity.
-     * {@link MultibindingAnnotationsProcessingStep} will add errors if the method has any
-     * multibinding annotations.
-     */
-    NO_MULTIBINDINGS,
-
-    /** This method allows multibinding annotations, so validate them. */
-    ALLOWS_MULTIBINDINGS,
-    ;
-
-    private boolean allowsMultibindings() {
-      return this == ALLOWS_MULTIBINDINGS;
-    }
-  }
-
-  /** How to check scoping annotations. */
-  protected enum AllowsScoping {
-    /** This method disallows scope annotations, so check that none are present. */
-    NO_SCOPING,
-
-    /** This method allows scoping, so validate that there's at most one. */
-    ALLOWS_SCOPING,
-    ;
-  }
 }
diff --git a/java/dagger/internal/codegen/BindingRequest.java b/java/dagger/internal/codegen/BindingRequest.java
index 432e76f..27067aa 100644
--- a/java/dagger/internal/codegen/BindingRequest.java
+++ b/java/dagger/internal/codegen/BindingRequest.java
@@ -19,6 +19,10 @@
 import static dagger.internal.codegen.RequestKinds.requestType;
 
 import com.google.auto.value.AutoValue;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.BindingRequestProto;
+import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
+import dagger.internal.codegen.serialization.RequestKindWrapper;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -105,4 +109,17 @@
     }
     return frameworkType().get().equals(FrameworkType.PRODUCER_NODE);
   }
+
+  /** Creates a proto representation of this binding request. */
+  BindingRequestProto toProto() {
+    BindingRequestProto.Builder builder =
+        BindingRequestProto.newBuilder().setKey(KeyFactory.toProto(key()));
+    if (frameworkType().isPresent()) {
+      builder.setFrameworkType(
+          FrameworkTypeWrapper.FrameworkType.valueOf(frameworkType().get().name()));
+    } else {
+      builder.setRequestKind(RequestKindWrapper.RequestKind.valueOf(requestKind().get().name()));
+    }
+    return builder.build();
+  }
 }
diff --git a/java/dagger/internal/codegen/BindsInstanceElementValidator.java b/java/dagger/internal/codegen/BindsInstanceElementValidator.java
new file mode 100644
index 0000000..9249c8e
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceElementValidator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import dagger.BindsInstance;
+import javax.lang.model.element.Element;
+
+abstract class BindsInstanceElementValidator<E extends Element> extends BindingElementValidator<E> {
+  BindsInstanceElementValidator() {
+    super(BindsInstance.class, AllowsMultibindings.NO_MULTIBINDINGS, AllowsScoping.NO_SCOPING);
+  }
+
+  @Override
+  protected final String bindingElements() {
+    // Even though @BindsInstance may be placed on methods, the subject of errors is the
+    // parameter
+    return "@BindsInstance parameters";
+  }
+
+  @Override
+  protected final String bindingElementTypeVerb() {
+    return "be";
+  }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceMethodValidator.java b/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
new file mode 100644
index 0000000..1a491c7
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
+import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreElements;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceMethodValidator extends BindsInstanceElementValidator<ExecutableElement> {
+  @Inject
+  BindsInstanceMethodValidator() {}
+
+  @Override
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
+  }
+
+  private class Validator extends ElementValidator {
+    Validator(ExecutableElement element) {
+      super(element);
+    }
+
+    @Override
+    protected void checkAdditionalProperties() {
+      if (!element.getModifiers().contains(ABSTRACT)) {
+        report.addError("@BindsInstance methods must be abstract");
+      }
+      if (element.getParameters().size() != 1) {
+        report.addError(
+            "@BindsInstance methods should have exactly one parameter for the bound type");
+      }
+      TypeElement enclosingType = MoreElements.asType(element.getEnclosingElement());
+      moduleAnnotation(enclosingType)
+          .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
+      anyComponentAnnotation(enclosingType)
+          .ifPresent(
+              componentAnnotation ->
+                  report.addError(
+                      String.format(
+                          "@BindsInstance methods should not be included in @%1$ss. "
+                              + "Did you mean to put it in a @%1$s.Builder?",
+                          componentAnnotation.simpleName())));
+    }
+
+    @Override
+    protected Optional<TypeMirror> bindingElementType() {
+      List<? extends VariableElement> parameters =
+          MoreElements.asExecutable(element).getParameters();
+      return parameters.size() == 1
+          ? Optional.of(getOnlyElement(parameters).asType())
+          : Optional.empty();
+    }
+  }
+
+  private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
+    return String.format(
+        "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
+        moduleAnnotation.annotationClass().getSimpleName());
+  }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceParameterValidator.java b/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
new file mode 100644
index 0000000..b2dc8d8
--- /dev/null
+++ b/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.element.ElementKind.METHOD;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+
+import com.google.auto.common.MoreElements;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceParameterValidator extends BindsInstanceElementValidator<VariableElement> {
+  @Inject
+  BindsInstanceParameterValidator() {}
+
+  @Override
+  protected ElementValidator elementValidator(VariableElement element) {
+    return new Validator(element);
+  }
+
+  private class Validator extends ElementValidator {
+    Validator(VariableElement element) {
+      super(element);
+    }
+
+    @Override
+    protected void checkAdditionalProperties() {
+      Element enclosing = element.getEnclosingElement();
+      if (!enclosing.getKind().equals(METHOD)) {
+        report.addError(
+            "@BindsInstance should only be applied to methods or parameters of methods");
+        return;
+      }
+
+      ExecutableElement method = MoreElements.asExecutable(enclosing);
+      if (!method.getModifiers().contains(ABSTRACT)) {
+        report.addError("@BindsInstance parameters may only be used in abstract methods");
+      }
+
+      TypeKind returnKind = method.getReturnType().getKind();
+      if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
+        report.addError(
+            "@BindsInstance parameters may not be used in methods with a void, array or primitive "
+                + "return type");
+      }
+    }
+
+    @Override
+    protected Optional<TypeMirror> bindingElementType() {
+      return Optional.of(element.asType());
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/BindsInstanceProcessingStep.java b/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
index 6ee2485..4c222a9 100644
--- a/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
+++ b/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
@@ -16,14 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static javax.lang.model.element.ElementKind.METHOD;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.TYPEVAR;
-
 import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableSet;
 import dagger.BindsInstance;
@@ -32,21 +24,24 @@
 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;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
 
 /**
  * Processing step that validates that the {@code BindsInstance} annotation is applied to the
  * correct elements.
  */
 final class BindsInstanceProcessingStep extends TypeCheckingProcessingStep<Element> {
+  private final BindsInstanceMethodValidator methodValidator;
+  private final BindsInstanceParameterValidator parameterValidator;
   private final Messager messager;
 
   @Inject
-  BindsInstanceProcessingStep(Messager messager) {
+  BindsInstanceProcessingStep(
+      BindsInstanceMethodValidator methodValidator,
+      BindsInstanceParameterValidator parameterValidator,
+      Messager messager) {
     super(element -> element);
+    this.methodValidator = methodValidator;
+    this.parameterValidator = parameterValidator;
     this.messager = messager;
   }
 
@@ -57,82 +52,15 @@
 
   @Override
   protected void process(Element element, ImmutableSet<Class<? extends Annotation>> annotations) {
-    ValidationReport.Builder<Element> report = ValidationReport.about(element);
-
     switch (element.getKind()) {
-      case METHOD:
-        ExecutableElement method = MoreElements.asExecutable(element);
-        validateBindsInstanceMethod(method, report);
-        break;
       case PARAMETER:
-        VariableElement parameter = MoreElements.asVariable(element);
-        validateBindsInstanceParameterType(parameter, report);
-        validateBindsInstanceParameterEnclosingMethod(parameter, report);
+        parameterValidator.validate(MoreElements.asVariable(element)).printMessagesTo(messager);
+        break;
+      case METHOD:
+        methodValidator.validate(MoreElements.asExecutable(element)).printMessagesTo(messager);
         break;
       default:
-        // Shouldn't be possible given the target elements @BindsInstance allows.
-        throw new AssertionError();
-    }
-
-    report.build().printMessagesTo(messager);
-  }
-
-  private void validateBindsInstanceMethod(
-      ExecutableElement method, ValidationReport.Builder<Element> report) {
-    if (!method.getModifiers().contains(ABSTRACT)) {
-      report.addError("@BindsInstance methods must be abstract");
-    }
-    if (method.getParameters().size() != 1) {
-      report.addError(
-          "@BindsInstance methods should have exactly one parameter for the bound type");
-    } else {
-      validateBindsInstanceParameterType(getOnlyElement(method.getParameters()), report);
-    }
-    TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
-    moduleAnnotation(enclosingType)
-        .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
-    anyComponentAnnotation(enclosingType)
-        .ifPresent(
-            componentAnnotation ->
-                report.addError(
-                    String.format(
-                        "@BindsInstance methods should not be included in @%1$ss. "
-                            + "Did you mean to put it in a @%1$s.Builder?",
-                        componentAnnotation.simpleName())));
-  }
-
-  private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
-    return String.format(
-        "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
-        moduleAnnotation.annotationClass().getSimpleName());
-  }
-
-  private void validateBindsInstanceParameterType(
-      VariableElement parameter, ValidationReport.Builder<Element> report) {
-    if (FrameworkTypes.isFrameworkType(parameter.asType())) {
-      report.addError("@BindsInstance parameters may not be framework types", parameter);
-    }
-  }
-
-  private void validateBindsInstanceParameterEnclosingMethod(
-      VariableElement parameter, ValidationReport.Builder<Element> report) {
-    Element enclosing = parameter.getEnclosingElement();
-    if (!enclosing.getKind().equals(METHOD)) {
-      report.addError(
-          "@BindsInstance should only be applied to methods or parameters of methods");
-      return;
-    }
-
-    ExecutableElement method = MoreElements.asExecutable(enclosing);
-    if (!method.getModifiers().contains(ABSTRACT)) {
-      report.addError("@BindsInstance parameters may only be used in abstract methods");
-    }
-
-    TypeKind returnKind = method.getReturnType().getKind();
-    if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
-      report.addError(
-          "@BindsInstance parameters may not be used in methods with a void, array or "
-              + "primitive return type");
+        throw new AssertionError(element);
     }
   }
 }
diff --git a/java/dagger/internal/codegen/BindsMethodValidator.java b/java/dagger/internal/codegen/BindsMethodValidator.java
index 1e017bc..e198c3a 100644
--- a/java/dagger/internal/codegen/BindsMethodValidator.java
+++ b/java/dagger/internal/codegen/BindsMethodValidator.java
@@ -16,24 +16,24 @@
 
 package dagger.internal.codegen;
 
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsScoping.ALLOWS_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
 
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import dagger.Binds;
 import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.producers.ProducerModule;
 import javax.inject.Inject;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeMirror;
 
-/**
- * A validator for {@link Binds} methods.
- */
+/** A validator for {@link Binds} methods. */
 final class BindsMethodValidator extends BindingMethodValidator {
   private final DaggerTypes types;
   private final BindsTypeChecker bindsTypeChecker;
@@ -58,48 +58,50 @@
   }
 
   @Override
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    super.checkMethod(builder);
-    checkParameters(builder);
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
   }
 
-  @Override
-  protected void checkParameters(ValidationReport.Builder<ExecutableElement> builder) {
-    ExecutableElement method = builder.getSubject();
-    if (method.getParameters().size() != 1) {
-      builder.addError(
-          bindingMethods(
-              "must have exactly one parameter, whose type is assignable to the return type"));
-    } else {
-      super.checkParameters(builder);
-    }
-  }
-
-  @Override
-  protected void checkParameter(
-      ValidationReport.Builder<ExecutableElement> builder, VariableElement parameter) {
-    super.checkParameter(builder, parameter);
-    ExecutableElement method = builder.getSubject();
-    TypeMirror leftHandSide = boxIfNecessary(method.getReturnType());
-    TypeMirror rightHandSide = parameter.asType();
-    ContributionType contributionType = ContributionType.fromBindingMethod(method);
-    if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
-      builder.addError(
-          "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
+  private class Validator extends MethodValidator {
+    Validator(ExecutableElement element) {
+      super(element);
     }
 
-    if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
-      // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
-      // right-hand-side might not be assignable to the left-hand-side, but still compatible with
-      // Set.addAll(Collection<? extends E>)
-      builder.addError("@Binds methods' parameter type must be assignable to the return type");
+    @Override
+    protected void checkParameters() {
+      if (element.getParameters().size() != 1) {
+        report.addError(
+            bindingMethods(
+                "must have exactly one parameter, whose type is assignable to the return type"));
+      } else {
+        super.checkParameters();
+      }
     }
-  }
 
-  private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
-    if (maybePrimitive.getKind().isPrimitive()) {
-      return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
+    @Override
+    protected void checkParameter(VariableElement parameter) {
+      super.checkParameter(parameter);
+      TypeMirror leftHandSide = boxIfNecessary(element.getReturnType());
+      TypeMirror rightHandSide = parameter.asType();
+      ContributionType contributionType = ContributionType.fromBindingElement(element);
+      if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
+        report.addError(
+            "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
+      }
+
+      if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
+        // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
+        // right-hand-side might not be assignable to the left-hand-side, but still compatible with
+        // Set.addAll(Collection<? extends E>)
+        report.addError("@Binds methods' parameter type must be assignable to the return type");
+      }
     }
-    return maybePrimitive;
+
+    private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
+      if (maybePrimitive.getKind().isPrimitive()) {
+        return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
+      }
+      return maybePrimitive;
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java b/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
index e8088d9..e1c9d73 100644
--- a/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
+++ b/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
@@ -16,9 +16,9 @@
 
 package dagger.internal.codegen;
 
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsMultibindings.NO_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
 import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
 import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
@@ -29,6 +29,8 @@
 import com.google.common.collect.ImmutableSet;
 import dagger.BindsOptionalOf;
 import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.producers.ProducerModule;
 import javax.inject.Inject;
 import javax.lang.model.element.ExecutableElement;
@@ -58,29 +60,33 @@
   }
 
   @Override
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    super.checkMethod(builder);
-    checkParameters(builder);
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
   }
 
-  @Override
-  protected void checkKeyType(
-      ValidationReport.Builder<ExecutableElement> builder, TypeMirror keyType) {
-    super.checkKeyType(builder, keyType);
-    if (isValidImplicitProvisionKey(
-            getQualifiers(builder.getSubject()).stream().findFirst(), keyType, types)
-        && !injectedConstructors(MoreElements.asType(MoreTypes.asDeclared(keyType).asElement()))
-            .isEmpty()) {
-      builder.addError(
-          "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
-              + "annotated constructor because those are always present");
+  private class Validator extends MethodValidator {
+    Validator(ExecutableElement element) {
+      super(element);
     }
-  }
 
-  @Override
-  protected void checkParameters(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!builder.getSubject().getParameters().isEmpty()) {
-      builder.addError("@BindsOptionalOf methods cannot have parameters");
+    @Override
+    protected void checkKeyType(TypeMirror keyType) {
+      super.checkKeyType(keyType);
+      if (isValidImplicitProvisionKey(
+              getQualifiers(element).stream().findFirst(), keyType, types)
+          && !injectedConstructors(MoreElements.asType(MoreTypes.asDeclared(keyType).asElement()))
+              .isEmpty()) {
+        report.addError(
+            "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
+                + "annotated constructor because those are always present");
+      }
+    }
+
+    @Override
+    protected void checkParameters() {
+      if (!element.getParameters().isEmpty()) {
+        report.addError("@BindsOptionalOf methods cannot have parameters");
+      }
     }
   }
 }
diff --git a/java/dagger/internal/codegen/BindsTypeChecker.java b/java/dagger/internal/codegen/BindsTypeChecker.java
index c00aa1b..acecc9e 100644
--- a/java/dagger/internal/codegen/BindsTypeChecker.java
+++ b/java/dagger/internal/codegen/BindsTypeChecker.java
@@ -21,6 +21,8 @@
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Map;
 import java.util.Set;
 import javax.inject.Inject;
@@ -68,9 +70,8 @@
         DeclaredType parameterizedMapType =
             types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
         return methodParameterTypes(parameterizedMapType, "put").get(1);
-      default:
-        throw new AssertionError("Unknown contribution type: " + contributionType);
     }
+    throw new AssertionError("Unknown contribution type: " + contributionType);
   }
 
   private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
diff --git a/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
index 8a81017..a5e0219 100644
--- a/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
+++ b/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
@@ -16,7 +16,7 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.elementToString;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
 
 import dagger.model.BindingGraph.ChildFactoryMethodEdge;
 import javax.lang.model.element.ExecutableElement;
diff --git a/java/dagger/internal/codegen/CompilerOptions.java b/java/dagger/internal/codegen/CompilerOptions.java
index 85b276b..bc3cbf8 100644
--- a/java/dagger/internal/codegen/CompilerOptions.java
+++ b/java/dagger/internal/codegen/CompilerOptions.java
@@ -16,39 +16,12 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.concat;
-import static com.google.common.collect.Sets.immutableEnumSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.FeatureStatus.DISABLED;
-import static dagger.internal.codegen.FeatureStatus.ENABLED;
-import static dagger.internal.codegen.ValidationType.ERROR;
-import static dagger.internal.codegen.ValidationType.NONE;
-import static dagger.internal.codegen.ValidationType.WARNING;
-import static java.util.EnumSet.allOf;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.google.errorprone.annotations.CheckReturnValue;
 import com.squareup.javapoet.AnnotationSpec;
 import dagger.internal.GenerationOptions;
-import dagger.producers.Produces;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.stream.Stream;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.Processor;
+import javax.lang.model.element.TypeElement;
 import javax.tools.Diagnostic;
 
 /** A collection of options that dictate how the compiler will run. */
-@AutoValue
 abstract class CompilerOptions {
   abstract boolean usesProducers();
 
@@ -68,7 +41,7 @@
 
   abstract Diagnostic.Kind nullableValidationKind();
 
-  boolean doCheckForNulls() {
+  final boolean doCheckForNulls() {
     return nullableValidationKind().equals(Diagnostic.Kind.ERROR);
   }
 
@@ -93,360 +66,54 @@
 
   abstract boolean aheadOfTimeSubcomponents();
 
+  /**
+   * Enables a testing configuration where all superclass {@link ComponentImplementation}s are
+   * derived from their serialized forms.
+   */
+  abstract boolean forceUseSerializedComponentImplementations();
+
+  /**
+   * If {@code true}, in {@link #aheadOfTimeSubcomponents()} mode, Dagger will emit metadata
+   * annotations to deserialize aspects of the {@link ComponentImplementation}.
+   *
+   * This should only be disabled in compile-testing tests that want to ignore the annotations when
+   * asserting on generated source.
+   */
+  abstract boolean emitModifiableMetadataAnnotations();
+
   abstract boolean useGradleIncrementalProcessing();
 
-  abstract ValidationType moduleBindingValidationType();
+  /**
+   * Returns the validation that should be done for the full binding graph for the element.
+   *
+   * @throws IllegalArgumentException if {@code element} is not a module or (sub)component
+   */
+  abstract ValidationType fullBindingGraphValidationType(TypeElement element);
 
   abstract Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind();
 
   abstract ValidationType explicitBindingConflictsWithInjectValidationType();
 
-  static Builder builder() {
-    return new AutoValue_CompilerOptions.Builder()
-        .headerCompilation(false)
-        .useGradleIncrementalProcessing(false);
-  }
-
-  static CompilerOptions create(ProcessingEnvironment processingEnv) {
-    Builder builder = new AutoValue_CompilerOptions.Builder();
-    for (Option option : concat(allOf(Feature.class), allOf(Validation.class))) {
-      option.set(builder, processingEnv);
-    }
-    return builder.build();
-  }
-
-  abstract Builder toBuilder();
-
   /**
    * Creates a new {@link CompilerOptions} from the serialized {@link GenerationOptions} of a base
    * component implementation.
    */
-  CompilerOptions withGenerationOptions(GenerationOptions generationOptions) {
-    return toBuilder().fastInit(generationOptions.fastInit()).build();
+  final CompilerOptions withGenerationOptions(GenerationOptions generationOptions) {
+    return new ForwardingCompilerOptions(this) {
+      @Override
+      public boolean fastInit() {
+        return generationOptions.fastInit();
+      }
+    };
   }
 
   /**
-   * Returns an {@link GenerationOptions} annotation that serializes any options for this
-   * compilation that should be reused in future compilations.
+   * Returns a {@link GenerationOptions} annotation that serializes any options for this compilation
+   * that should be reused in future compilations.
    */
-  AnnotationSpec toGenerationOptionsAnnotation() {
+  final AnnotationSpec toGenerationOptionsAnnotation() {
     return AnnotationSpec.builder(GenerationOptions.class)
         .addMember("fastInit", "$L", fastInit())
         .build();
   }
-
-  @AutoValue.Builder
-  @CanIgnoreReturnValue
-  interface Builder {
-    Builder usesProducers(boolean usesProduces);
-
-    Builder headerCompilation(boolean headerCompilation);
-
-    Builder fastInit(boolean fastInit);
-
-    Builder formatGeneratedSource(boolean formatGeneratedSource);
-
-    Builder writeProducerNameInToken(boolean writeProducerNameInToken);
-
-    Builder nullableValidationKind(Diagnostic.Kind kind);
-
-    Builder privateMemberValidationKind(Diagnostic.Kind kind);
-
-    Builder staticMemberValidationKind(Diagnostic.Kind kind);
-
-    Builder ignorePrivateAndStaticInjectionForComponent(
-        boolean ignorePrivateAndStaticInjectionForComponent);
-
-    Builder scopeCycleValidationType(ValidationType type);
-
-    Builder warnIfInjectionFactoryNotGeneratedUpstream(
-        boolean warnIfInjectionFactoryNotGeneratedUpstream);
-
-    Builder aheadOfTimeSubcomponents(boolean aheadOfTimeSubcomponents);
-
-    Builder useGradleIncrementalProcessing(boolean enabled);
-
-    Builder moduleBindingValidationType(ValidationType validationType);
-
-    Builder moduleHasDifferentScopesDiagnosticKind(Diagnostic.Kind kind);
-
-    Builder explicitBindingConflictsWithInjectValidationType(ValidationType validationType);
-
-    @CheckReturnValue
-    CompilerOptions build();
-  }
-
-  /** An option that can be set into {@link CompilerOptions}. */
-  private interface Option<T extends Enum<T>> {
-
-    /** Sets the appropriate property on a {@link CompilerOptions.Builder}. */
-    void set(Builder builder, ProcessingEnvironment processingEnvironment);
-
-    /**
-     * {@code true} if {@link #toString()} represents a {@linkplain Processor#getSupportedOptions()
-     * supported command line option}.
-     */
-    default boolean useCommandLineOption() {
-      return true;
-    }
-
-    /** The default value for this option. */
-    T defaultValue();
-
-    /** The valid values for this option. */
-    Set<T> validValues();
-  }
-
-  /** A feature that can be enabled or disabled. */
-  private enum Feature implements Option<FeatureStatus> {
-    HEADER_COMPILATION(Builder::headerCompilation) {
-      @Override
-      boolean isEnabled(ProcessingEnvironment processingEnvironment) {
-        return processingEnvironment.getOptions().containsKey(toString());
-      }
-
-      @Override
-      public String toString() {
-        return "experimental_turbine_hjar";
-      }
-    },
-
-    FAST_INIT(Builder::fastInit),
-
-    EXPERIMENTAL_ANDROID_MODE((builder, ignoredValue) -> {}) {
-      @Override
-      public void set(Builder builder, ProcessingEnvironment processingEnvironment) {
-        noLongerRecognizedWarning(processingEnvironment);
-      }
-    },
-
-    FORMAT_GENERATED_SOURCE(Builder::formatGeneratedSource, ENABLED),
-
-    WRITE_PRODUCER_NAME_IN_TOKEN(Builder::writeProducerNameInToken),
-
-    WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM(
-        Builder::warnIfInjectionFactoryNotGeneratedUpstream),
-
-    IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT(
-        Builder::ignorePrivateAndStaticInjectionForComponent),
-
-    EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS(Builder::aheadOfTimeSubcomponents),
-
-    FLOATING_BINDS_METHODS((builder, ignoredValue) -> {}) {
-      @Override
-      public void set(Builder builder, ProcessingEnvironment processingEnvironment) {
-        noLongerRecognizedWarning(processingEnvironment);
-      }
-    },
-
-    USE_GRADLE_INCREMENTAL_PROCESSING(Builder::useGradleIncrementalProcessing) {
-      @Override
-      boolean isEnabled(ProcessingEnvironment processingEnvironment) {
-        return processingEnvironment.getOptions().containsKey(toString());
-      }
-
-      @Override
-      public String toString() {
-        return "dagger.gradle.incremental";
-      }
-    },
-
-    USES_PRODUCERS(Builder::usesProducers) {
-      @Override
-      boolean isEnabled(ProcessingEnvironment processingEnvironment) {
-        return processingEnvironment
-                .getElementUtils()
-                .getTypeElement(Produces.class.getCanonicalName())
-            != null;
-      }
-
-      @Override
-      public boolean useCommandLineOption() {
-        return false;
-      }
-    },
-    ;
-
-    final OptionParser<FeatureStatus> parser = new OptionParser<>(this);
-    final FeatureStatus defaultValue;
-    final BiConsumer<Builder, Boolean> setter;
-
-    Feature(BiConsumer<Builder, Boolean> setter) {
-      this(setter, DISABLED);
-    }
-
-    Feature(BiConsumer<Builder, Boolean> setter, FeatureStatus defaultValue) {
-      this.setter = setter;
-      this.defaultValue = defaultValue;
-    }
-
-    @Override
-    public FeatureStatus defaultValue() {
-      return defaultValue;
-    }
-
-    @Override
-    public Set<FeatureStatus> validValues() {
-      return EnumSet.allOf(FeatureStatus.class);
-    }
-
-    @Override
-    public void set(Builder builder, ProcessingEnvironment processingEnvironment) {
-      setter.accept(builder, isEnabled(processingEnvironment));
-    }
-
-    boolean isEnabled(ProcessingEnvironment processingEnvironment) {
-      return parser.parse(processingEnvironment).equals(ENABLED);
-    }
-
-    @Override
-    public String toString() {
-      return optionName(name());
-    }
-
-    void noLongerRecognizedWarning(ProcessingEnvironment processingEnvironment) {
-      if (processingEnvironment.getOptions().containsKey(toString())) {
-          processingEnvironment
-            .getMessager()
-            .printMessage(
-                Diagnostic.Kind.WARNING,
-                toString() + " is no longer a recognized option by Dagger");
-      }
-    }
-
-  }
-
-  /** The diagnostic kind or validation type for a kind of validation. */
-  private enum Validation implements Option<ValidationType> {
-    DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(Builder::scopeCycleValidationType),
-
-    NULLABLE_VALIDATION(kindSetter(Builder::nullableValidationKind), ERROR, WARNING) {
-    },
-
-    PRIVATE_MEMBER_VALIDATION(kindSetter(Builder::privateMemberValidationKind), ERROR, WARNING),
-
-    STATIC_MEMBER_VALIDATION(kindSetter(Builder::staticMemberValidationKind), ERROR, WARNING),
-
-    /** Whether to validate partial binding graphs associated with modules. */
-    MODULE_BINDING_VALIDATION(Builder::moduleBindingValidationType, NONE, ERROR, WARNING),
-
-    /**
-     * How to report conflicting scoped bindings when validating partial binding graphs associated
-     * with modules.
-     */
-    MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(
-        kindSetter(Builder::moduleHasDifferentScopesDiagnosticKind), ERROR, WARNING),
-
-    /**
-     * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
-     * constructor used in an ancestor component.
-     */
-    EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(
-        Builder::explicitBindingConflictsWithInjectValidationType, WARNING, ERROR, NONE),
-    ;
-
-    final OptionParser<ValidationType> parser = new OptionParser<>(this);
-
-    static BiConsumer<Builder, ValidationType> kindSetter(
-        BiConsumer<Builder, Diagnostic.Kind> setter) {
-      return (builder, validationType) ->
-          setter.accept(builder, validationType.diagnosticKind().get());
-    }
-
-    final ValidationType defaultType;
-    final ImmutableSet<ValidationType> validTypes;
-    final BiConsumer<Builder, ValidationType> setter;
-
-    Validation(BiConsumer<Builder, ValidationType> setter) {
-      this(setter, ERROR, WARNING, NONE);
-    }
-
-    Validation(
-        BiConsumer<Builder, ValidationType> setter,
-        ValidationType defaultType,
-        ValidationType... moreValidTypes) {
-      this.setter = setter;
-      this.defaultType = defaultType;
-      this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
-    }
-
-    @Override
-    public ValidationType defaultValue() {
-      return defaultType;
-    }
-
-    @Override
-    public Set<ValidationType> validValues() {
-      return validTypes;
-    }
-
-    @Override
-    public void set(Builder builder, ProcessingEnvironment processingEnvironment) {
-      setter.accept(builder, parser.parse(processingEnvironment));
-    }
-
-    @Override
-    public String toString() {
-      return optionName(name());
-    }
-  }
-
-  private static String optionName(String enumName) {
-    return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, enumName);
-  }
-
-  static ImmutableSet<String> supportedOptions() {
-    return Stream.<Option<?>[]>of(Feature.values(), Validation.values())
-        .flatMap(Arrays::stream)
-        .filter(Option::useCommandLineOption)
-        .map(Option::toString)
-        .collect(toImmutableSet());
-  }
-
-  /** A parser for an {@link Option}. */
-  private static class OptionParser<T extends Enum<T>> {
-    private final Option<T> option;
-
-    OptionParser(Option<T> option) {
-      this.option = checkNotNull(option);
-    }
-
-    /**
-     * Returns the value for this option as set on the command line, or the default value if not.
-     */
-    T parse(ProcessingEnvironment processingEnvironment) {
-      String key = option.toString();
-      Map<String, String> options = processingEnvironment.getOptions();
-      if (options.containsKey(key)) {
-        String stringValue = options.get(key);
-        if (stringValue == null) {
-          processingEnvironment
-              .getMessager()
-              .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
-        } else {
-          try {
-            T value = Enum.valueOf(valueClass(), Ascii.toUpperCase(stringValue));
-            if (option.validValues().contains(value)) {
-              return value;
-            }
-          } catch (IllegalArgumentException e) {
-            // handled below
-          }
-          processingEnvironment
-              .getMessager()
-              .printMessage(
-                  Diagnostic.Kind.ERROR,
-                  String.format(
-                      "Processor option -A%s may only have the values %s "
-                          + "(case insensitive), found: %s",
-                      key, option.validValues(), stringValue));
-        }
-      }
-      return option.defaultValue();
-    }
-
-    private Class<T> valueClass() {
-      return option.defaultValue().getDeclaringClass();
-    }
-  }
 }
diff --git a/java/dagger/internal/codegen/ComponentAnnotation.java b/java/dagger/internal/codegen/ComponentAnnotation.java
index 41cbabe..a8f2ece 100644
--- a/java/dagger/internal/codegen/ComponentAnnotation.java
+++ b/java/dagger/internal/codegen/ComponentAnnotation.java
@@ -19,10 +19,9 @@
 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
 import static com.google.auto.common.MoreTypes.asTypeElements;
 import static com.google.auto.common.MoreTypes.isTypeOf;
-import static com.google.common.collect.Sets.union;
-import static dagger.internal.codegen.DaggerElements.getAnyAnnotation;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
 
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
@@ -48,6 +47,21 @@
  * for modules.
  */
 abstract class ComponentAnnotation {
+  /** The root component annotation types. */
+  private static final ImmutableSet<Class<? extends Annotation>> ROOT_COMPONENT_ANNOTATIONS =
+     ImmutableSet.of(Component.class, ProductionComponent.class);
+
+  /** The subcomponent annotation types. */
+  private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
+     ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+
+  /** All component annotation types. */
+  private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS =
+     ImmutableSet.<Class<? extends Annotation>>builder()
+         .addAll(ROOT_COMPONENT_ANNOTATIONS)
+         .addAll(SUBCOMPONENT_ANNOTATIONS)
+         .build();
+
   /** The annotation itself. */
   abstract AnnotationMirror annotation();
 
@@ -117,7 +131,7 @@
    * one is present on {@code typeElement}.
    */
   static Optional<ComponentAnnotation> rootComponentAnnotation(TypeElement typeElement) {
-    return anyComponentAnnotation(typeElement, rootComponentAnnotations());
+    return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS);
   }
 
   /**
@@ -125,7 +139,7 @@
    * typeElement}.
    */
   static Optional<ComponentAnnotation> subcomponentAnnotation(TypeElement typeElement) {
-    return anyComponentAnnotation(typeElement, subcomponentAnnotations());
+    return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS);
   }
 
   /**
@@ -133,7 +147,7 @@
    * on {@code typeElement}.
    */
   static Optional<ComponentAnnotation> anyComponentAnnotation(TypeElement typeElement) {
-    return anyComponentAnnotation(typeElement, allComponentAnnotations());
+    return anyComponentAnnotation(typeElement, ALL_COMPONENT_ANNOTATIONS);
   }
 
   private static Optional<ComponentAnnotation> anyComponentAnnotation(
@@ -143,7 +157,7 @@
 
   /** Returns {@code true} if the argument is a component annotation. */
   static boolean isComponentAnnotation(AnnotationMirror annotation) {
-    return allComponentAnnotations().stream()
+    return ALL_COMPONENT_ANNOTATIONS.stream()
         .anyMatch(annotationClass -> isTypeOf(annotationClass, annotation.getAnnotationType()));
   }
 
@@ -177,17 +191,17 @@
 
   /** The root component annotation types. */
   static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
-    return ImmutableSet.of(Component.class, ProductionComponent.class);
+    return ROOT_COMPONENT_ANNOTATIONS;
   }
 
   /** The subcomponent annotation types. */
   static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
-    return ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+    return SUBCOMPONENT_ANNOTATIONS;
   }
 
   /** All component annotation types. */
   static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
-    return union(rootComponentAnnotations(), subcomponentAnnotations()).immutableCopy();
+    return ALL_COMPONENT_ANNOTATIONS;
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index dd86b92..37cf1e8 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -19,17 +19,17 @@
 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.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
 import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope;
 import static dagger.internal.codegen.MemberSelect.staticFactoryCreation;
 import static dagger.internal.codegen.RequestKinds.isDerivedFromProvider;
-import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
-import static dagger.internal.codegen.TypeNames.SINGLE_CHECK;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static dagger.model.BindingKind.DELEGATE;
 import static dagger.model.BindingKind.MULTIBOUND_MAP;
 import static dagger.model.BindingKind.MULTIBOUND_SET;
@@ -43,6 +43,9 @@
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.RequestKind;
 import java.util.HashMap;
@@ -50,6 +53,7 @@
 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. */
@@ -66,6 +70,7 @@
   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;
@@ -81,6 +86,7 @@
       OptionalFactories optionalFactories,
       DaggerTypes types,
       DaggerElements elements,
+      SourceVersion sourceVersion,
       @GenerationCompilerOptions CompilerOptions compilerOptions) {
     this.parent = parent;
     this.graph = graph;
@@ -89,6 +95,7 @@
     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);
@@ -273,10 +280,8 @@
 
       case PRODUCTION:
         return productionBindingExpression(resolvedBindings, request);
-
-      default:
-        throw new AssertionError(resolvedBindings);
     }
+    throw new AssertionError(resolvedBindings);
   }
 
   /**
@@ -425,7 +430,7 @@
         return producerFromProviderBindingExpression(resolvedBindings);
 
       case FUTURE:
-        return new ImmediateFutureBindingExpression(resolvedBindings, this, types);
+        return new ImmediateFutureBindingExpression(resolvedBindings, this, types, sourceVersion);
 
       case MEMBERS_INJECTION:
         throw new IllegalArgumentException();
@@ -568,7 +573,8 @@
                 resolvedBindings, componentImplementation, graph, this, types, elements));
 
       case OPTIONAL:
-        return Optional.of(new OptionalBindingExpression(resolvedBindings, this, types));
+        return Optional.of(
+            new OptionalBindingExpression(resolvedBindings, this, types, sourceVersion));
 
       case BOUND_INSTANCE:
         return Optional.of(
@@ -587,7 +593,8 @@
                 membersInjectionMethods,
                 componentRequirementExpressions,
                 types,
-                elements));
+                elements,
+                sourceVersion));
 
       case MEMBERS_INJECTOR:
         return Optional.empty();
diff --git a/java/dagger/internal/codegen/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
index ae33514..f6ec7a2 100644
--- a/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
+++ b/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
@@ -33,6 +33,8 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import dagger.BindsInstance;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import java.util.List;
 import javax.lang.model.element.Element;
diff --git a/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
index 0778403..4f06b90 100644
--- a/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
+++ b/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
@@ -23,9 +23,9 @@
 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.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-import static dagger.internal.codegen.TypeSpecs.addSupertype;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
@@ -46,6 +46,9 @@
 import com.squareup.javapoet.TypeSpec;
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.ComponentRequirement.NullPolicy;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 import java.util.Set;
 import javax.inject.Inject;
@@ -68,7 +71,7 @@
 
   /** Returns a new creator implementation for the given component, if necessary. */
   Optional<ComponentCreatorImplementation> create(
-      ComponentImplementation componentImplementation, BindingGraph graph) {
+      ComponentImplementation componentImplementation, Optional<BindingGraph> graph) {
     if (!componentImplementation.componentDescriptor().hasCreator()) {
       return Optional.empty();
     }
@@ -167,15 +170,15 @@
     }
 
     private void setModifiers() {
-      classBuilder.addModifiers(visibility());
+      visibility().ifPresent(classBuilder::addModifiers);
       if (!componentImplementation.isNested()) {
         classBuilder.addModifiers(STATIC);
       }
       classBuilder.addModifiers(componentImplementation.isAbstract() ? ABSTRACT : FINAL);
     }
 
-    /** Returns the visibility modifier the generated class should have. */
-    protected abstract Modifier visibility();
+    /** Returns the visibility modifier the generated class should have, if any. */
+    protected abstract Optional<Modifier> visibility();
 
     /** Sets the superclass being extended or interface being implemented for this creator. */
     protected abstract void setSupertype();
@@ -253,7 +256,7 @@
           .addAnnotation(Deprecated.class)
           .addJavadoc(
               "@deprecated This module is declared, but an instance is not used in the component. "
-                  + "This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.\n")
+                  + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
           .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
       return maybeReturnThis(method);
     }
@@ -378,12 +381,12 @@
   /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
   private final class BuilderForCreatorDescriptor extends Builder {
     final ComponentCreatorDescriptor creatorDescriptor;
-    private final BindingGraph graph;
+    private final Optional<BindingGraph> graph;
 
     BuilderForCreatorDescriptor(
         ComponentImplementation componentImplementation,
         ComponentCreatorDescriptor creatorDescriptor,
-        BindingGraph graph) {
+        Optional<BindingGraph> graph) {
       super(componentImplementation);
       this.creatorDescriptor = creatorDescriptor;
       this.graph = graph;
@@ -395,22 +398,21 @@
     }
 
     @Override
-    protected Modifier visibility() {
+    protected Optional<Modifier> visibility() {
       if (componentImplementation.isAbstract()) {
         // The component creator class of a top-level component implementation in ahead-of-time
         // subcomponents mode must be public, not protected, because the creator's subclass will
         // be a sibling of the component subclass implementation, not nested.
-        return componentImplementation.isNested() ? PROTECTED : PUBLIC;
+        return Optional.of(componentImplementation.isNested() ? PROTECTED : PUBLIC);
       }
-      return PRIVATE;
+      return Optional.of(PRIVATE);
     }
 
     @Override
     protected void setSupertype() {
       if (componentImplementation.baseCreatorImplementation().isPresent()) {
         // If an abstract base implementation for this creator exists, extend that class.
-        classBuilder.superclass(
-            componentImplementation.baseCreatorImplementation().get().name());
+        classBuilder.superclass(componentImplementation.baseCreatorImplementation().get().name());
       } else {
         addSupertype(classBuilder, creatorDescriptor.typeElement());
       }
@@ -476,7 +478,7 @@
      * Returns whether the given {@code requirement} is for a module type owned by the component.
      */
     private boolean isOwnedModule(ComponentRequirement requirement) {
-      return graph.ownedModuleTypes().contains(requirement.typeElement());
+      return graph.map(g -> g.ownedModuleTypes().contains(requirement.typeElement())).orElse(true);
     }
 
     private boolean hasBaseCreatorImplementation() {
@@ -516,8 +518,12 @@
     }
 
     @Override
-    protected Modifier visibility() {
-      return PUBLIC;
+    protected Optional<Modifier> visibility() {
+      return componentImplementation
+          .componentDescriptor()
+          .typeElement()
+          .getModifiers()
+          .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
     }
 
     @Override
diff --git a/java/dagger/internal/codegen/ComponentCreatorValidator.java b/java/dagger/internal/codegen/ComponentCreatorValidator.java
index 4a80f94..c55dadd 100644
--- a/java/dagger/internal/codegen/ComponentCreatorValidator.java
+++ b/java/dagger/internal/codegen/ComponentCreatorValidator.java
@@ -30,6 +30,8 @@
 import com.google.common.collect.ObjectArrays;
 import dagger.BindsInstance;
 import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.List;
 import java.util.Set;
 import javax.inject.Inject;
@@ -59,329 +61,330 @@
     ValidationReport.Builder<TypeElement> report = ValidationReport.about(type);
 
     ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations = getCreatorAnnotations(type);
-    if (creatorAnnotations.size() > 1) {
-      String error =
-          "May not have more than one component Builder or Factory annotation on a type: found "
-              + creatorAnnotations;
-      report.addError(error);
+    if (!validateOnlyOneCreatorAnnotation(creatorAnnotations, report)) {
       return report.build();
     }
 
-    // creatorAnnotations should never be empty because the validate method should only ever be
-    // called for types that have been found to have some creator annotation
-    ComponentCreatorAnnotation creatorAnnotation = getOnlyElement(creatorAnnotations);
-    ComponentCreatorMessages messages = ErrorMessages.creatorMessagesFor(creatorAnnotation);
-
-    Element componentElement = type.getEnclosingElement();
-    if (!isAnnotationPresent(componentElement, creatorAnnotation.componentAnnotation())) {
-      report.addError(messages.mustBeInComponent());
-    }
-
-    // If the type isn't a class or interface, don't validate anything else since the rest of the
-    // messages will be bogus.
-    if (validateIsClassOrInterface(type, report, messages)) {
-      validateTypeRequirements(type, report, messages);
-      switch (creatorAnnotation.creatorKind()) {
-        case FACTORY:
-          validateFactory(type, report, componentElement, messages);
-          break;
-        case BUILDER:
-          validateBuilder(type, report, componentElement, messages);
-      }
-    }
-
     // Note: there's more validation in ComponentDescriptorValidator:
     // - to make sure the setter methods/factory parameters mirror the deps
     // - to make sure each type or key is set by only one method or parameter
-
-    return report.build();
+    ElementValidator validator =
+        new ElementValidator(type, report, getOnlyElement(creatorAnnotations));
+    return validator.validate();
   }
 
-  /** Validates that the type is a class or interface type and returns true if it is. */
-  private boolean validateIsClassOrInterface(
-      TypeElement type,
-      ValidationReport.Builder<TypeElement> report,
-      ComponentCreatorMessages messages) {
-    switch (type.getKind()) {
-      case CLASS:
-        validateConstructor(type, report, messages);
-        return true;
-      case INTERFACE:
-        return true;
-      default:
-        report.addError(messages.mustBeClassOrInterface());
+  private boolean validateOnlyOneCreatorAnnotation(
+      ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations,
+      ValidationReport.Builder<?> report) {
+    // creatorAnnotations should never be empty because this should only ever be called for
+    // types that have been found to have some creator annotation
+    if (creatorAnnotations.size() > 1) {
+      String error =
+          "May not have more than one component Factory or Builder annotation on a type"
+              + ": found "
+              + creatorAnnotations;
+      report.addError(error);
+      return false;
     }
-    return false;
+
+    return true;
   }
 
-  private void validateConstructor(
-      TypeElement type,
-      ValidationReport.Builder<TypeElement> report,
-      ComponentCreatorMessages messages) {
-    List<? extends Element> allElements = type.getEnclosedElements();
-    List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
+  /**
+   * Validator for a single {@link TypeElement} that is annotated with a {@code Builder} or {@code
+   * Factory} annotation.
+   */
+  private final class ElementValidator {
+    private final TypeElement type;
+    private final Element component;
+    private final ValidationReport.Builder<TypeElement> report;
+    private final ComponentCreatorAnnotation annotation;
+    private final ComponentCreatorMessages messages;
 
-    boolean valid = true;
-    if (constructors.size() != 1) {
-      valid = false;
-    } else {
-      ExecutableElement constructor = getOnlyElement(constructors);
-      valid = constructor.getParameters().isEmpty()
-          && !constructor.getModifiers().contains(PRIVATE);
+    private ElementValidator(
+        TypeElement type,
+        ValidationReport.Builder<TypeElement> report,
+        ComponentCreatorAnnotation annotation) {
+      this.type = type;
+      this.component = type.getEnclosingElement();
+      this.report = report;
+      this.annotation = annotation;
+      this.messages = ErrorMessages.creatorMessagesFor(annotation);
     }
 
-    if (!valid) {
-      report.addError(messages.invalidConstructor());
-    }
-  }
+    /** Validates the creator type. */
+    final ValidationReport<TypeElement> validate() {
+      if (!isAnnotationPresent(component, annotation.componentAnnotation())) {
+        report.addError(messages.mustBeInComponent());
+      }
 
-  /** Validates basic requirements about the type that are common to both creator kinds. */
-  private void validateTypeRequirements(
-      TypeElement type,
-      ValidationReport.Builder<TypeElement> report,
-      ComponentCreatorMessages messages) {
-    if (!type.getTypeParameters().isEmpty()) {
-      report.addError(messages.generics());
-    }
+      // If the type isn't a class or interface, don't validate anything else since the rest of the
+      // messages will be bogus.
+      if (!validateIsClassOrInterface()) {
+        return report.build();
+      }
 
-    Set<Modifier> modifiers = type.getModifiers();
-    if (modifiers.contains(PRIVATE)) {
-      report.addError(messages.isPrivate());
-    }
-    if (!modifiers.contains(STATIC)) {
-      report.addError(messages.mustBeStatic());
-    }
-    // Note: Must be abstract, so no need to check for final.
-    if (!modifiers.contains(ABSTRACT)) {
-      report.addError(messages.mustBeAbstract());
-    }
-  }
-
-  private void validateBuilder(
-      TypeElement type,
-      ValidationReport.Builder<TypeElement> report,
-      Element componentElement,
-      ComponentCreatorMessages messages) {
-    ExecutableElement buildMethod = null;
-    for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
-      TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
-      switch (method.getParameters().size()) {
-        case 0: // If this is potentially a build() method, validate it returns the correct type.
-          if (validateFactoryMethodReturnType(
-              report, type, componentElement, messages, method)) {
-            if (buildMethod != null) {
-              // If we found more than one build-like method, fail.
-              error(
-                  report,
-                  method,
-                  messages.twoFactoryMethods(),
-                  messages.inheritedTwoFactoryMethods(),
-                  buildMethod);
-            }
-          }
-          // We set the buildMethod regardless of the return type to reduce error spam.
-          buildMethod = method;
+      validateTypeRequirements();
+      switch (annotation.creatorKind()) {
+        case FACTORY:
+          validateFactory();
           break;
+        case BUILDER:
+          validateBuilder();
+      }
 
-        case 1: // If this correctly had one parameter, make sure the return types are valid.
-          validateSetterMethod(type, method, returnType, report, messages);
-          break;
+      return report.build();
+    }
 
-        default: // more than one parameter
-          error(
-              report,
-              method,
-              messages.setterMethodsMustTakeOneArg(),
-              messages.inheritedSetterMethodsMustTakeOneArg());
-          break;
+    /** Validates that the type is a class or interface type and returns true if it is. */
+    private boolean validateIsClassOrInterface() {
+      switch (type.getKind()) {
+        case CLASS:
+          validateConstructor();
+          return true;
+        case INTERFACE:
+          return true;
+        default:
+          report.addError(messages.mustBeClassOrInterface());
+      }
+      return false;
+    }
+
+    private void validateConstructor() {
+      List<? extends Element> allElements = type.getEnclosedElements();
+      List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
+
+      boolean valid = true;
+      if (constructors.size() != 1) {
+        valid = false;
+      } else {
+        ExecutableElement constructor = getOnlyElement(constructors);
+        valid =
+            constructor.getParameters().isEmpty() && !constructor.getModifiers().contains(PRIVATE);
+      }
+
+      if (!valid) {
+        report.addError(messages.invalidConstructor());
       }
     }
 
-    if (buildMethod == null) {
-      report.addError(messages.missingFactoryMethod());
-    } else {
-      validateNotGeneric(buildMethod, report, messages);
-    }
-  }
+    /** Validates basic requirements about the type that are common to both creator kinds. */
+    private void validateTypeRequirements() {
+      if (!type.getTypeParameters().isEmpty()) {
+        report.addError(messages.generics());
+      }
 
-  private void validateSetterMethod(
-      TypeElement type,
-      ExecutableElement method,
-      TypeMirror returnType,
-      ValidationReport.Builder<TypeElement> report,
-      ComponentCreatorMessages messages) {
-    if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
-      error(
-          report,
-          method,
-          messages.setterMethodsMustReturnVoidOrBuilder(),
-          messages.inheritedSetterMethodsMustReturnVoidOrBuilder());
+      Set<Modifier> modifiers = type.getModifiers();
+      if (modifiers.contains(PRIVATE)) {
+        report.addError(messages.isPrivate());
+      }
+      if (!modifiers.contains(STATIC)) {
+        report.addError(messages.mustBeStatic());
+      }
+      // Note: Must be abstract, so no need to check for final.
+      if (!modifiers.contains(ABSTRACT)) {
+        report.addError(messages.mustBeAbstract());
+      }
     }
 
-    validateNotGeneric(method, report, messages);
+    private void validateBuilder() {
+      ExecutableElement buildMethod = null;
+      for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
+        switch (method.getParameters().size()) {
+          case 0: // If this is potentially a build() method, validate it returns the correct type.
+            if (validateFactoryMethodReturnType(method)) {
+              if (buildMethod != null) {
+                // If we found more than one build-like method, fail.
+                error(
+                    method,
+                    messages.twoFactoryMethods(),
+                    messages.inheritedTwoFactoryMethods(),
+                    buildMethod);
+              }
+            }
+            // We set the buildMethod regardless of the return type to reduce error spam.
+            buildMethod = method;
+            break;
 
-    VariableElement parameter = method.getParameters().get(0);
+          case 1: // If this correctly had one parameter, make sure the return types are valid.
+            validateSetterMethod(method);
+            break;
 
-    boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
-    boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
-    boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
+          default: // more than one parameter
+            error(
+                method,
+                messages.setterMethodsMustTakeOneArg(),
+                messages.inheritedSetterMethodsMustTakeOneArg());
+            break;
+        }
+      }
 
-    if (methodIsBindsInstance && parameterIsBindsInstance) {
-      error(
-          report,
-          method,
-          messages.bindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
-          messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
-    }
-
-    if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
-      error(
-          report,
-          method,
-          messages.nonBindsInstanceParametersMayNotBePrimitives(),
-          messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
-    }
-  }
-
-  private void validateFactory(
-      TypeElement type,
-      ValidationReport.Builder<TypeElement> report,
-      Element componentElement,
-      ComponentCreatorMessages messages) {
-    ImmutableList<ExecutableElement> abstractMethods =
-        elements.getUnimplementedMethods(type).asList();
-    switch (abstractMethods.size()) {
-      case 0:
+      if (buildMethod == null) {
         report.addError(messages.missingFactoryMethod());
-        return;
-      case 1:
-        break; // good
-      default:
-        error(
-            report,
-            abstractMethods.get(1),
-            messages.twoFactoryMethods(),
-            messages.inheritedTwoFactoryMethods(),
-            abstractMethods.get(0));
-        return;
+      } else {
+        validateNotGeneric(buildMethod);
+      }
     }
 
-    ExecutableElement method = getOnlyElement(abstractMethods);
-    validateNotGeneric(method, report, messages);
-
-    if (!validateFactoryMethodReturnType(report, type, componentElement, messages, method)) {
-      // If we can't determine that the single method is a valid factory method, don't bother
-      // validating its parameters.
-      return;
-    }
-
-    for (VariableElement parameter : method.getParameters()) {
-      if (!isAnnotationPresent(parameter, BindsInstance.class)
-          && parameter.asType().getKind().isPrimitive()) {
+    private void validateSetterMethod(ExecutableElement method) {
+      TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+      if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
         error(
-            report,
+            method,
+            messages.setterMethodsMustReturnVoidOrBuilder(),
+            messages.inheritedSetterMethodsMustReturnVoidOrBuilder());
+      }
+
+      validateNotGeneric(method);
+
+      VariableElement parameter = method.getParameters().get(0);
+
+      boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
+      boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
+      boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
+
+      if (methodIsBindsInstance && parameterIsBindsInstance) {
+        error(
+            method,
+            messages.bindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
+            messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
+      }
+
+      if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
+        error(
             method,
             messages.nonBindsInstanceParametersMayNotBePrimitives(),
             messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
       }
     }
-  }
 
-  /**
-   * Validates that the factory method that actually returns a new component instance. Returns true
-   * if the return type was valid.
-   */
-  private boolean validateFactoryMethodReturnType(
-      ValidationReport.Builder<TypeElement> report,
-      TypeElement type,
-      Element componentElement,
-      ComponentCreatorMessages messages,
-      ExecutableElement method) {
-    TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+    private void validateFactory() {
+      ImmutableList<ExecutableElement> abstractMethods =
+          elements.getUnimplementedMethods(type).asList();
+      switch (abstractMethods.size()) {
+        case 0:
+          report.addError(messages.missingFactoryMethod());
+          return;
+        case 1:
+          break; // good
+        default:
+          error(
+              abstractMethods.get(1),
+              messages.twoFactoryMethods(),
+              messages.inheritedTwoFactoryMethods(),
+              abstractMethods.get(0));
+          return;
+      }
 
-    if (!types.isSubtype(componentElement.asType(), returnType)) {
-      error(
-          report,
-          method,
-          messages.factoryMethodMustReturnComponentType(),
-          messages.inheritedFactoryMethodMustReturnComponentType());
-      return false;
+      validateFactoryMethod(getOnlyElement(abstractMethods));
     }
 
-    if (isAnnotationPresent(method, BindsInstance.class)) {
-      error(
-          report,
-          method,
-          messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
-          messages.inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance());
-      return false;
-    }
+    /** Validates that the given {@code method} is a valid component factory method. */
+    private void validateFactoryMethod(ExecutableElement method) {
+      validateNotGeneric(method);
 
-    TypeElement componentType = MoreElements.asType(componentElement);
-    if (!types.isSameType(componentType.asType(), returnType)) {
-      ImmutableSet<ExecutableElement> methodsOnlyInComponent =
-          methodsOnlyInComponent(componentType);
-      if (!methodsOnlyInComponent.isEmpty()) {
-        report.addWarning(
-            messages.factoryMethodReturnsSupertypeWithMissingMethods(
-                componentType, report.getSubject(), returnType, method, methodsOnlyInComponent),
-            method);
+      if (!validateFactoryMethodReturnType(method)) {
+        // If we can't determine that the single method is a valid factory method, don't bother
+        // validating its parameters.
+        return;
+      }
+
+      for (VariableElement parameter : method.getParameters()) {
+        if (!isAnnotationPresent(parameter, BindsInstance.class)
+            && parameter.asType().getKind().isPrimitive()) {
+          error(
+              method,
+              messages.nonBindsInstanceParametersMayNotBePrimitives(),
+              messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
+        }
       }
     }
-    return true;
-  }
 
-  /**
-   * Generates one of two error messages. If the method is enclosed in the subject, we target the
-   * error to the method itself. Otherwise we target the error to the subject and list the method as
-   * an argument. (Otherwise we have no way of knowing if the method is being compiled in this pass
-   * too, so javac might not be able to pinpoint it's line of code.)
-   */
-  /*
-   * For Component.Builder, the prototypical example would be if someone had:
-   *    libfoo: interface SharedBuilder { void badSetter(A a, B b); }
-   *    libbar: BarComponent { BarBuilder extends SharedBuilder } }
-   * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
-   * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
-   * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
-   * failure.
-   *
-   * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
-   * class was included in this compile run.  But that's hard, and this is close enough.
-   */
-  private static void error(
-      ValidationReport.Builder<TypeElement> report,
-      ExecutableElement method,
-      String enclosedError,
-      String inheritedError,
-      Object... extraArgs) {
-    if (method.getEnclosingElement().equals(report.getSubject())) {
-      report.addError(String.format(enclosedError, extraArgs), method);
-    } else {
-      report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
+    /**
+     * Validates that the factory method that actually returns a new component instance. Returns
+     * true if the return type was valid.
+     */
+    private boolean validateFactoryMethodReturnType(ExecutableElement method) {
+      TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+
+      if (!types.isSubtype(component.asType(), returnType)) {
+        error(
+            method,
+            messages.factoryMethodMustReturnComponentType(),
+            messages.inheritedFactoryMethodMustReturnComponentType());
+        return false;
+      }
+
+      if (isAnnotationPresent(method, BindsInstance.class)) {
+        error(
+            method,
+            messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
+            messages.inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance());
+        return false;
+      }
+
+      TypeElement componentType = MoreElements.asType(component);
+      if (!types.isSameType(componentType.asType(), returnType)) {
+        ImmutableSet<ExecutableElement> methodsOnlyInComponent =
+            methodsOnlyInComponent(componentType);
+        if (!methodsOnlyInComponent.isEmpty()) {
+          report.addWarning(
+              messages.factoryMethodReturnsSupertypeWithMissingMethods(
+                  componentType, type, returnType, method, methodsOnlyInComponent),
+              method);
+        }
+      }
+      return true;
     }
-  }
 
-  private void validateNotGeneric(
-      ExecutableElement method,
-      ValidationReport.Builder<TypeElement> report,
-      ComponentCreatorMessages messages) {
-    if (!method.getTypeParameters().isEmpty()) {
-      error(
-          report,
-          method,
-          messages.methodsMayNotHaveTypeParameters(),
-          messages.inheritedMethodsMayNotHaveTypeParameters());
+    /**
+     * Generates one of two error messages. If the method is enclosed in the subject, we target the
+     * error to the method itself. Otherwise we target the error to the subject and list the method
+     * as an argument. (Otherwise we have no way of knowing if the method is being compiled in this
+     * pass too, so javac might not be able to pinpoint it's line of code.)
+     */
+    /*
+     * For Component.Builder, the prototypical example would be if someone had:
+     *    libfoo: interface SharedBuilder { void badSetter(A a, B b); }
+     *    libbar: BarComponent { BarBuilder extends SharedBuilder } }
+     * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
+     * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
+     * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
+     * failure.
+     *
+     * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
+     * class was included in this compile run.  But that's hard, and this is close enough.
+     */
+    private void error(
+        ExecutableElement method,
+        String enclosedError,
+        String inheritedError,
+        Object... extraArgs) {
+      if (method.getEnclosingElement().equals(type)) {
+        report.addError(String.format(enclosedError, extraArgs), method);
+      } else {
+        report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
+      }
     }
-  }
 
-  /**
-   * 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()));
+    /** Validates that the given {@code method} is not generic. * */
+    private void validateNotGeneric(ExecutableElement method) {
+      if (!method.getTypeParameters().isEmpty()) {
+        error(
+            method,
+            messages.methodsMayNotHaveTypeParameters(),
+            messages.inheritedMethodsMayNotHaveTypeParameters());
+      }
+    }
+
+    /**
+     * Returns all methods defind in {@code componentType} which are not inherited from a supertype.
+     */
+    private ImmutableSet<ExecutableElement> methodsOnlyInComponent(TypeElement componentType) {
+      // TODO(ronshapiro): Ideally this shouldn't return methods which are redeclared from a
+      // supertype, but do not change the return type. We don't have a good/simple way of checking
+      // that, and it doesn't seem likely, so the warning won't be too bad.
+      return ImmutableSet.copyOf(methodsIn(componentType.getEnclosedElements()));
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/ComponentDescriptor.java b/java/dagger/internal/codegen/ComponentDescriptor.java
index de979df..769cc4c 100644
--- a/java/dagger/internal/codegen/ComponentDescriptor.java
+++ b/java/dagger/internal/codegen/ComponentDescriptor.java
@@ -21,7 +21,7 @@
 import static com.google.common.base.Preconditions.checkState;
 import static dagger.internal.codegen.DaggerStreams.toImmutableMap;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerTypes.isFutureType;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.type.TypeKind.VOID;
 
@@ -32,11 +32,14 @@
 import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.errorprone.annotations.CheckReturnValue;
 import dagger.Component;
 import dagger.Module;
 import dagger.Subcomponent;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.Scope;
 import dagger.producers.CancellationPolicy;
@@ -194,6 +197,12 @@
   abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
       childComponentsDeclaredByFactoryMethods();
 
+  /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
+  @Memoized
+  ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() {
+    return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
+  }
+
   /** Returns the factory method that declares a child component. */
   final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
       ComponentDescriptor childComponent) {
diff --git a/java/dagger/internal/codegen/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/ComponentDescriptorFactory.java
index e120ade..7d87eac 100644
--- a/java/dagger/internal/codegen/ComponentDescriptorFactory.java
+++ b/java/dagger/internal/codegen/ComponentDescriptorFactory.java
@@ -38,6 +38,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Scope;
 import java.util.Optional;
 import java.util.function.Function;
@@ -110,7 +112,6 @@
 
   private ComponentDescriptor create(
       TypeElement typeElement, ComponentAnnotation componentAnnotation) {
-    DeclaredType declaredComponentType = MoreTypes.asDeclared(typeElement.asType());
     ImmutableSet<ComponentRequirement> componentDependencies =
         componentAnnotation.dependencyTypes().stream()
             .map(ComponentRequirement::forDependency)
diff --git a/java/dagger/internal/codegen/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/ComponentDescriptorValidator.java
index ad8499c..8f85b3a 100644
--- a/java/dagger/internal/codegen/ComponentDescriptorValidator.java
+++ b/java/dagger/internal/codegen/ComponentDescriptorValidator.java
@@ -43,6 +43,8 @@
 import com.google.common.collect.Sets;
 import dagger.internal.codegen.ComponentRequirement.NullPolicy;
 import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Scope;
 import java.util.ArrayDeque;
 import java.util.Collection;
@@ -68,7 +70,7 @@
  * Reports errors in the component hierarchy.
  *
  * <ul>
- *   <li>Validates scope hierarchy of component dependencies and subcompoennts.
+ *   <li>Validates scope hierarchy of component dependencies and subcomponents.
  *   <li>Reports errors if there are component dependency cycles.
  *   <li>Reports errors if any abstract modules have non-abstract instance binding methods.
  *   <li>Validates component creator types.
diff --git a/java/dagger/internal/codegen/ComponentGenerator.java b/java/dagger/internal/codegen/ComponentGenerator.java
index dad54b7..330ec2d 100644
--- a/java/dagger/internal/codegen/ComponentGenerator.java
+++ b/java/dagger/internal/codegen/ComponentGenerator.java
@@ -22,6 +22,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
 import dagger.Component;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
 import javax.inject.Inject;
diff --git a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
index a8d6869..47857ca 100644
--- a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
@@ -24,7 +24,7 @@
 import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
 import static dagger.internal.codegen.ComponentGenerator.componentName;
-import static dagger.internal.codegen.TypeSpecs.addSupertype;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
@@ -42,6 +42,8 @@
 import com.squareup.javapoet.TypeSpec;
 import dagger.BindsInstance;
 import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.producers.internal.CancellationListener;
 import java.lang.annotation.Annotation;
 import java.util.Optional;
@@ -139,8 +141,12 @@
         ClassName generatedTypeName, ComponentDescriptor componentDescriptor) {
       TypeSpec.Builder generatedComponent =
           TypeSpec.classBuilder(generatedTypeName)
-              .addModifiers(PUBLIC, FINAL)
+              .addModifiers(FINAL)
               .addMethod(privateConstructor());
+      if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+        generatedComponent.addModifiers(PUBLIC);
+      }
+
       TypeElement componentElement = componentDescriptor.typeElement();
       addSupertype(generatedComponent, componentElement);
 
@@ -156,8 +162,12 @@
       } else {
         TypeSpec.Builder builder =
             TypeSpec.classBuilder("Builder")
-                .addModifiers(PUBLIC, STATIC, FINAL)
+                .addModifiers(STATIC, FINAL)
                 .addMethod(privateConstructor());
+        if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+          builder.addModifiers(PUBLIC);
+        }
+
         ClassName builderClassName = generatedTypeName.nestedClass("Builder");
         builderMethodReturnType = builderClassName;
         creatorKind = BUILDER;
diff --git a/java/dagger/internal/codegen/ComponentImplementation.java b/java/dagger/internal/codegen/ComponentImplementation.java
index d435e30..340da14 100644
--- a/java/dagger/internal/codegen/ComponentImplementation.java
+++ b/java/dagger/internal/codegen/ComponentImplementation.java
@@ -23,8 +23,9 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.serialization.ProtoSerialization.toAnnotationValue;
 import static java.util.stream.Collectors.toList;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.FINAL;
@@ -32,10 +33,10 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Supplier;
-import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.MultimapBuilder;
@@ -47,12 +48,15 @@
 import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.ConfigureInitializationParameters;
+import dagger.internal.ModifiableBinding;
+import dagger.internal.ModifiableModule;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.TypeSpecs;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -158,8 +162,7 @@
     /** Creates a new {@link ConfigureInitializationMethod}. */
     static ConfigureInitializationMethod create(
         MethodSpec spec, ImmutableSet<ComponentRequirement> parameters) {
-      return new AutoValue_ComponentImplementation_ConfigureInitializationMethod(
-          spec, parameters);
+      return new AutoValue_ComponentImplementation_ConfigureInitializationMethod(spec, parameters);
     }
 
     /** The spec for the method. */
@@ -172,8 +175,9 @@
     abstract ImmutableSet<ComponentRequirement> parameters();
   }
 
+  private final CompilerOptions compilerOptions;
   private final ComponentDescriptor componentDescriptor;
-  private final BindingGraph graph;
+  private final Optional<BindingGraph> graph;
   private final ClassName name;
   private final NestingKind nestingKind;
   private final boolean isAbstract;
@@ -181,7 +185,7 @@
   private Optional<ComponentCreatorImplementation> creatorImplementation;
   private final Map<TypeElement, ComponentImplementation> childImplementations = new HashMap<>();
   private final TypeSpec.Builder component;
-  private final SubcomponentNames subcomponentNames;
+  private final Optional<SubcomponentNames> subcomponentNames;
   private final UniqueNameSet componentFieldNames = new UniqueNameSet();
   private final UniqueNameSet componentMethodNames = new UniqueNameSet();
   private final List<CodeBlock> initializations = new ArrayList<>();
@@ -199,26 +203,29 @@
   private final List<Supplier<TypeSpec>> switchingProviderSupplier = new ArrayList<>();
   private final ModifiableBindingMethods modifiableBindingMethods = new ModifiableBindingMethods();
   private final SetMultimap<BindingRequest, Key> multibindingContributionsMade =
-      HashMultimap.create();
+      LinkedHashMultimap.create();
   private Optional<ConfigureInitializationMethod> configureInitializationMethod = Optional.empty();
   private final Map<ComponentRequirement, String> modifiableModuleMethods = new LinkedHashMap<>();
 
   private ComponentImplementation(
       ComponentDescriptor componentDescriptor,
-      BindingGraph graph,
+      Optional<BindingGraph> graph,
       ClassName name,
       NestingKind nestingKind,
       Optional<ComponentImplementation> superclassImplementation,
-      SubcomponentNames subcomponentNames,
-      Modifier... modifiers) {
+      Optional<SubcomponentNames> subcomponentNames,
+      CompilerOptions compilerOptions,
+      ImmutableSet<Modifier> modifiers) {
     checkName(name, nestingKind);
+    this.compilerOptions = compilerOptions;
     this.componentDescriptor = componentDescriptor;
     this.graph = graph;
     this.name = name;
     this.nestingKind = nestingKind;
-    this.isAbstract = Arrays.asList(modifiers).contains(ABSTRACT);
+    this.isAbstract = modifiers.contains(ABSTRACT);
     this.superclassImplementation = superclassImplementation;
-    this.component = classBuilder(name).addModifiers(modifiers);
+    this.component = classBuilder(name);
+    modifiers.forEach(component::addModifiers);
     this.subcomponentNames = subcomponentNames;
   }
 
@@ -226,16 +233,28 @@
   static ComponentImplementation topLevelComponentImplementation(
       BindingGraph graph,
       ClassName name,
-      SubcomponentNames subcomponentNames) {
+      SubcomponentNames subcomponentNames,
+      CompilerOptions compilerOptions) {
     return new ComponentImplementation(
         graph.componentDescriptor(),
-        graph,
+        Optional.of(graph),
         name,
         NestingKind.TOP_LEVEL,
         Optional.empty(), // superclass implementation
-        subcomponentNames,
-        PUBLIC,
-        graph.componentDescriptor().isSubcomponent() ? ABSTRACT : FINAL);
+        Optional.of(subcomponentNames),
+        compilerOptions,
+        topLevelComponentImplementationModifiers(graph));
+  }
+
+  private static ImmutableSet<Modifier> topLevelComponentImplementationModifiers(
+      BindingGraph graph) {
+    ImmutableSet.Builder<Modifier> modifiers = ImmutableSet.builder();
+    if (graph.componentTypeElement().getModifiers().contains(PUBLIC)
+        || graph.componentDescriptor().isSubcomponent()) {
+      // TODO(ronshapiro): perhaps all generated components should be non-public?
+      modifiers.add(PUBLIC);
+    }
+    return modifiers.add(graph.componentDescriptor().isSubcomponent() ? ABSTRACT : FINAL).build();
   }
 
   /** Returns a component implementation that is a child of the current implementation. */
@@ -245,12 +264,35 @@
       Modifier... modifiers) {
     return new ComponentImplementation(
         graph.componentDescriptor(),
-        graph,
+        Optional.of(graph),
         getSubcomponentName(graph.componentDescriptor()),
         NestingKind.MEMBER,
         superclassImplementation,
         subcomponentNames,
-        modifiers);
+        compilerOptions,
+        ImmutableSet.copyOf(modifiers));
+  }
+
+  /**
+   * Returns a component implementation that models a previously compiled class. This {@link
+   * ComponentImplementation} is not used for code generation itself; it is used to determine what
+   * methods need to be implemented in a subclass implementation.
+   */
+  static ComponentImplementation forDeserializedComponent(
+      ComponentDescriptor componentDescriptor,
+      ClassName name,
+      NestingKind nestingKind,
+      Optional<ComponentImplementation> superclassImplementation,
+      CompilerOptions compilerOptions) {
+    return new ComponentImplementation(
+        componentDescriptor,
+        Optional.empty(),
+        name,
+        nestingKind,
+        superclassImplementation,
+        Optional.empty(),
+        compilerOptions,
+        ImmutableSet.of(PUBLIC, ABSTRACT));
   }
 
   // TODO(dpb): Just determine the nesting kind from the name.
@@ -271,9 +313,22 @@
     }
   }
 
+  /**
+   * Returns {@code true} if this component implementation represents a component that has already
+   * been compiled. If this returns true, the implementation will have no {@link #graph
+   * BindingGraph}.
+   */
+  boolean isDeserializedImplementation() {
+    return !graph.isPresent();
+  }
+
+  // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
+  // need it.
   /** Returns the binding graph for the component being generated. */
   BindingGraph graph() {
-    return graph;
+    checkState(!isDeserializedImplementation(),
+        "A BindingGraph is not available for deserialized component implementations.");
+    return graph.get();
   }
 
   /** Returns the descriptor for the component being generated. */
@@ -362,7 +417,22 @@
    */
   void setConfigureInitializationMethod(ConfigureInitializationMethod method) {
     configureInitializationMethod = Optional.of(method);
-    addMethod(MethodSpecKind.CONFIGURE_INITIALIZATION_METHOD, method.spec());
+    addMethod(
+        MethodSpecKind.CONFIGURE_INITIALIZATION_METHOD,
+        addConfigureInitializationMetadata(method));
+  }
+
+  private MethodSpec addConfigureInitializationMetadata(ConfigureInitializationMethod method) {
+    if (!shouldEmitModifiableMetadataAnnotations()) {
+      return method.spec();
+    }
+    AnnotationSpec.Builder annotation =
+        AnnotationSpec.builder(ConfigureInitializationParameters.class);
+    for (ComponentRequirement parameter : method.parameters()) {
+      annotation.addMember("value", toAnnotationValue(parameter.toProto()));
+    }
+
+    return method.spec().toBuilder().addAnnotation(annotation.build()).build();
   }
 
   void setCreatorImplementation(Optional<ComponentCreatorImplementation> creatorImplementation) {
@@ -403,7 +473,7 @@
    */
   ClassName getCreatorName() {
     return isNested()
-        ? name.peerClass(subcomponentNames.getCreatorName(componentDescriptor()))
+        ? name.peerClass(subcomponentNames().getCreatorName(componentDescriptor()))
         : name.nestedClass(creatorKind().typeName());
   }
 
@@ -414,7 +484,7 @@
         "%s is not a child component of %s",
         childDescriptor.typeElement(),
         componentDescriptor().typeElement());
-    return name.nestedClass(subcomponentNames.get(childDescriptor) + "Impl");
+    return name.nestedClass(subcomponentNames().get(childDescriptor) + "Impl");
   }
 
   /**
@@ -422,7 +492,14 @@
    * {@link Key}.
    */
   String getSubcomponentCreatorSimpleName(Key key) {
-    return subcomponentNames.getCreatorName(key);
+    return subcomponentNames().getCreatorName(key);
+  }
+
+  private SubcomponentNames subcomponentNames() {
+    checkState(
+        subcomponentNames.isPresent(),
+        "SubcomponentNames is not available for deserialized component implementations.");
+    return subcomponentNames.get();
   }
 
   /** Returns the child implementation. */
@@ -482,37 +559,94 @@
       TypeMirror returnType,
       MethodSpec methodSpec,
       boolean finalized) {
-    modifiableBindingMethods.addNewModifiableMethod(
-        type, request, returnType, methodSpec, finalized);
-    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
+    addModifiableMethod(
+        MethodSpecKind.MODIFIABLE_BINDING_METHOD, type, request, returnType, methodSpec, finalized);
   }
 
   /**
-   * Registers a known method as encapsulating a modifiable binding without adding the method to the
-   * current component. This is relevant when a method of a different type, such as a component
-   * method, encapsulates a modifiable binding.
+   * Adds a component method that is modifiable to the component. In this case, the method
+   * represents an encapsulation of a modifiable binding between implementations of a subcomponent.
+   * This is only relevant for ahead-of-time subcomponents.
    */
-  void registerModifiableBindingMethod(
+  void addModifiableComponentMethod(
       ModifiableBindingType type,
       BindingRequest request,
       TypeMirror returnType,
       MethodSpec methodSpec,
       boolean finalized) {
-    modifiableBindingMethods.addNewModifiableMethod(
+    addModifiableMethod(
+        MethodSpecKind.COMPONENT_METHOD, type, request, returnType, methodSpec, finalized);
+  }
+
+  private void addModifiableMethod(
+      MethodSpecKind methodKind,
+      ModifiableBindingType type,
+      BindingRequest request,
+      TypeMirror returnType,
+      MethodSpec methodSpec,
+      boolean finalized) {
+    modifiableBindingMethods.addModifiableMethod(
         type, request, returnType, methodSpec, finalized);
+    methodSpecsMap.put(methodKind, withModifiableBindingMetadata(methodSpec, type, request));
   }
 
   /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
   void addImplementedModifiableBindingMethod(ModifiableBindingMethod method) {
     modifiableBindingMethods.addReimplementedMethod(method);
-    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, method.methodSpec());
+    methodSpecsMap.put(
+        MethodSpecKind.MODIFIABLE_BINDING_METHOD,
+        withModifiableBindingMetadata(method.methodSpec(), method.type(), method.request()));
+  }
+
+  private MethodSpec withModifiableBindingMetadata(
+      MethodSpec method, ModifiableBindingType type, BindingRequest request) {
+    if (!shouldEmitModifiableMetadataAnnotations()) {
+      return method;
+    }
+    AnnotationSpec.Builder metadata =
+        AnnotationSpec.builder(ModifiableBinding.class)
+            .addMember("modifiableBindingType", "$S", type.name())
+            .addMember("bindingRequest", toAnnotationValue(request.toProto()));
+    for (Key multibindingContribution : multibindingContributionsMade.get(request)) {
+      metadata.addMember(
+          "multibindingContributions",
+          toAnnotationValue(KeyFactory.toProto(multibindingContribution)));
+    }
+    return method.toBuilder().addAnnotation(metadata.build()).build();
   }
 
   /** Add's a modifiable module method to this implementation. */
   void addModifiableModuleMethod(ComponentRequirement module, MethodSpec method) {
+    registerModifiableModuleMethod(module, method.name);
+    methodSpecsMap.put(
+        MethodSpecKind.MODIFIABLE_BINDING_METHOD, withModifiableModuleMetadata(module, method));
+  }
+
+  /** Registers a modifiable module method with {@code name} for {@code module}. */
+  void registerModifiableModuleMethod(ComponentRequirement module, String name) {
     checkArgument(module.kind().isModule());
-    checkState(modifiableModuleMethods.put(module, method.name) == null);
-    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, method);
+    checkState(modifiableModuleMethods.put(module, name) == null);
+  }
+
+  private MethodSpec withModifiableModuleMetadata(ComponentRequirement module, MethodSpec method) {
+    if (!shouldEmitModifiableMetadataAnnotations()) {
+      return method;
+    }
+    return method
+        .toBuilder()
+        .addAnnotation(
+            AnnotationSpec.builder(ModifiableModule.class)
+                .addMember("value", toAnnotationValue(module.toProto()))
+                .build())
+        .build();
+  }
+
+  /**
+   * Returns {@code true} if the generated component should include metadata annotations with
+   * information to deserialize this {@link ComponentImplementation} in future compilations.
+   */
+  boolean shouldEmitModifiableMetadataAnnotations() {
+    return isAbstract && compilerOptions.emitModifiableMetadataAnnotations();
   }
 
   /** Adds the given type to the component. */
@@ -645,8 +779,7 @@
     Optional<ComponentImplementation> currentSuperImplementation = superclassImplementation;
     Set<Key> cancelledKeysFromSuperclass = new HashSet<>();
     while (currentSuperImplementation.isPresent()) {
-      cancelledKeysFromSuperclass.addAll(
-          currentSuperImplementation.get().cancellableProducerKeys);
+      cancelledKeysFromSuperclass.addAll(currentSuperImplementation.get().cancellableProducerKeys);
       currentSuperImplementation = currentSuperImplementation.get().superclassImplementation;
     }
     return Sets.difference(cancellableProducerKeys, cancelledKeysFromSuperclass)
@@ -757,13 +890,23 @@
     // We register a multibinding as implemented each time we request the multibinding expression,
     // so only modify the set of contributions once.
     if (!multibindingContributionsMade.containsKey(bindingRequest)) {
-      multibindingContributionsMade.putAll(
+      registerImplementedMultibindingKeys(
           bindingRequest,
           multibinding.dependencies().stream().map(DependencyRequest::key).collect(toList()));
     }
   }
 
   /**
+   * Registers the multibinding contributions represented by {@code keys} as having been implemented
+   * in this component. Multibindings are modifiable across subcomponent implementations and this
+   * allows us to know whether a contribution has been made by a superclass implementation. This is
+   * only relevant for ahead-of-time subcomponents.
+   */
+  void registerImplementedMultibindingKeys(BindingRequest bindingRequest, Iterable<Key> keys) {
+    multibindingContributionsMade.putAll(bindingRequest, keys);
+  }
+
+  /**
    * Returns the set of multibinding contributions associated with all superclass implementations of
    * a multibinding.
    */
diff --git a/java/dagger/internal/codegen/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/ComponentImplementationBuilder.java
index 19cf55b..7579ac9 100644
--- a/java/dagger/internal/codegen/ComponentImplementationBuilder.java
+++ b/java/dagger/internal/codegen/ComponentImplementationBuilder.java
@@ -23,10 +23,7 @@
 import static com.google.common.base.Predicates.in;
 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.CodeBlocks.parameterNames;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.BUILDER_METHOD;
 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD;
@@ -37,6 +34,9 @@
 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR;
 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.SUBCOMPONENT;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
 import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.FINAL;
@@ -55,16 +55,22 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
+import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.ParameterSpec;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.ComponentDefinitionType;
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import dagger.producers.internal.CancellationListener;
 import dagger.producers.internal.Producers;
@@ -116,7 +122,7 @@
     setSupertype();
     componentImplementation.setCreatorImplementation(
         componentCreatorImplementationFactory.create(
-            componentImplementation, componentImplementation.graph()));
+            componentImplementation, Optional.of(componentImplementation.graph())));
     componentImplementation
         .creatorImplementation()
         .map(ComponentCreatorImplementation::spec)
@@ -149,6 +155,13 @@
       componentImplementation.addAnnotation(compilerOptions.toGenerationOptionsAnnotation());
     }
 
+    if (componentImplementation.shouldEmitModifiableMetadataAnnotations()) {
+      componentImplementation.addAnnotation(
+          AnnotationSpec.builder(ComponentDefinitionType.class)
+              .addMember("value", "$T.class", graph.componentTypeElement())
+              .build());
+    }
+
     done = true;
     return componentImplementation;
   }
@@ -207,16 +220,11 @@
       componentImplementation.addMethod(
           COMPONENT_METHOD, implementedComponentMethod.toBuilder().addModifiers(FINAL).build());
     } else {
-      // If the binding for the component method is modifiable, register it as such.
-      ModifiableBindingType modifiableBindingType =
-          bindingExpressions
-              .modifiableBindingExpressions()
-              .registerComponentMethodIfModifiable(methodDescriptor, implementedComponentMethod);
-
-      // If the method should be implemented in this component, implement it.
-      if (modifiableBindingType.hasBaseClassImplementation()) {
-        componentImplementation.addMethod(COMPONENT_METHOD, implementedComponentMethod);
-      }
+      // TODO(b/117833324): Can this class be the one to interface with ComponentImplementation
+      // instead of having it go through ModifiableBindingExpressions?
+      bindingExpressions
+          .modifiableBindingExpressions()
+          .addPossiblyModifiableComponentMethod(methodDescriptor, implementedComponentMethod);
     }
   }
 
diff --git a/java/dagger/internal/codegen/ComponentImplementationFactory.java b/java/dagger/internal/codegen/ComponentImplementationFactory.java
index 5ecdac0..9578ece 100644
--- a/java/dagger/internal/codegen/ComponentImplementationFactory.java
+++ b/java/dagger/internal/codegen/ComponentImplementationFactory.java
@@ -19,10 +19,15 @@
 import static com.google.common.base.Preconditions.checkState;
 import static dagger.internal.codegen.ComponentGenerator.componentName;
 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static javax.tools.Diagnostic.Kind.WARNING;
 
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.serialization.ProtoSerialization.InconsistentSerializedProtoException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
+import javax.annotation.processing.Messager;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import javax.lang.model.element.TypeElement;
@@ -35,17 +40,27 @@
   private final CompilerOptions compilerOptions;
   private final BindingGraphFactory bindingGraphFactory;
   private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder;
+  private final DeserializedComponentImplementationBuilder
+      deserializedComponentImplementationBuilder;
+  private final DaggerElements elements;
+  private final Messager messager;
 
   @Inject
   ComponentImplementationFactory(
       KeyFactory keyFactory,
       CompilerOptions compilerOptions,
       BindingGraphFactory bindingGraphFactory,
-      TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder) {
+      TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder,
+      DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder,
+      DaggerElements elements,
+      Messager messager) {
     this.keyFactory = keyFactory;
     this.compilerOptions = compilerOptions;
     this.bindingGraphFactory = bindingGraphFactory;
     this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder;
+    this.deserializedComponentImplementationBuilder = deserializedComponentImplementationBuilder;
+    this.elements = elements;
+    this.messager = messager;
   }
 
   /**
@@ -66,7 +81,9 @@
         ComponentImplementation.topLevelComponentImplementation(
             bindingGraph,
             componentName(bindingGraph.componentTypeElement()),
-            new SubcomponentNames(bindingGraph, keyFactory));
+            new SubcomponentNames(bindingGraph, keyFactory),
+            compilerOptions);
+
     // TODO(dpb): explore using optional bindings for the "parent" bindings
     CurrentImplementationSubcomponent currentImplementationSubcomponent =
         topLevelImplementationComponentBuilder
@@ -109,6 +126,28 @@
       }
     }
 
+    if (compilerOptions.emitModifiableMetadataAnnotations()) {
+      ClassName childSuperclassName = componentName(child.typeElement());
+      TypeElement generatedChildSuperclassImplementation =
+          elements.getTypeElement(childSuperclassName);
+      if (generatedChildSuperclassImplementation != null) {
+        try {
+          return deserializedComponentImplementationBuilder.create(
+              child, generatedChildSuperclassImplementation);
+        } catch (InconsistentSerializedProtoException e) {
+          messager.printMessage(
+              WARNING,
+              String.format(
+                  "%s was compiled with a different version of Dagger than the version in this "
+                      + "compilation. To ensure the validity of Dagger's generated code, compile "
+                      + "all Dagger code with the same version.",
+                  child.typeElement().getQualifiedName()));
+        }
+      } else if (compilerOptions.forceUseSerializedComponentImplementations()) {
+        throw new TypeNotPresentException(childSuperclassName.toString(), null);
+      }
+    }
+
     // Otherwise, the superclass implementation is top-level, so we must recreate the
     // implementation object for the base implementation of the child by truncating the binding
     // graph at the child.
diff --git a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
index 552a0b2..e98d595 100644
--- a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
@@ -18,6 +18,7 @@
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.javapoet.Expression;
 
 /** A binding expression for the instance of the component itself, i.e. {@code this}. */
 final class ComponentInstanceBindingExpression extends SimpleInvocationBindingExpression {
diff --git a/java/dagger/internal/codegen/ComponentMethodBindingExpression.java b/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
index f9e0b5a..7e7bbd8 100644
--- a/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
@@ -21,6 +21,8 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.TypeMirror;
 
 /**
diff --git a/java/dagger/internal/codegen/ComponentProcessingStep.java b/java/dagger/internal/codegen/ComponentProcessingStep.java
index 32d7d91..645c94f 100644
--- a/java/dagger/internal/codegen/ComponentProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentProcessingStep.java
@@ -23,6 +23,7 @@
 import static dagger.internal.codegen.ComponentCreatorAnnotation.allCreatorAnnotations;
 import static dagger.internal.codegen.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
 import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.ValidationType.NONE;
 import static java.util.Collections.disjoint;
 
 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
@@ -134,6 +135,9 @@
     if (!isValid(componentDescriptor)) {
       return;
     }
+    if (!isFullBindingGraphValid(componentDescriptor)) {
+      return;
+    }
     BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false);
     if (isValid(bindingGraph)) {
       generateComponent(bindingGraph);
@@ -141,7 +145,8 @@
   }
 
   private void processSubcomponent(TypeElement subcomponent) {
-    if (!compilerOptions.aheadOfTimeSubcomponents()) {
+    if (!compilerOptions.aheadOfTimeSubcomponents()
+        && compilerOptions.fullBindingGraphValidationType(subcomponent).equals(NONE)) {
       return;
     }
     if (!isSubcomponentValid(subcomponent)) {
@@ -150,9 +155,14 @@
     ComponentDescriptor subcomponentDescriptor =
         componentDescriptorFactory.subcomponentDescriptor(subcomponent);
     // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
-    BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false);
-    if (isValid(bindingGraph)) {
-      generateComponent(bindingGraph);
+    if (!isFullBindingGraphValid(subcomponentDescriptor)) {
+      return;
+    }
+    if (compilerOptions.aheadOfTimeSubcomponents()) {
+      BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false);
+      if (isValid(bindingGraph)) {
+        generateComponent(bindingGraph);
+      }
     }
   }
 
@@ -220,10 +230,17 @@
       return false;
     }
     ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(subcomponentElement);
-    if (subcomponentReport != null && !subcomponentReport.isClean()) {
-      return false;
+    return subcomponentReport == null || subcomponentReport.isClean();
+  }
+
+  private boolean isFullBindingGraphValid(ComponentDescriptor componentDescriptor) {
+    if (compilerOptions
+        .fullBindingGraphValidationType(componentDescriptor.typeElement())
+        .equals(NONE)) {
+      return true;
     }
-    return true;
+    BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
+    return isValid(fullBindingGraph);
   }
 
   private boolean isValid(ComponentDescriptor componentDescriptor) {
diff --git a/java/dagger/internal/codegen/ComponentProcessor.java b/java/dagger/internal/codegen/ComponentProcessor.java
index 6b137b3..541a4ab 100644
--- a/java/dagger/internal/codegen/ComponentProcessor.java
+++ b/java/dagger/internal/codegen/ComponentProcessor.java
@@ -16,6 +16,8 @@
 
 package dagger.internal.codegen;
 
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
+
 import com.google.auto.common.BasicAnnotationProcessor;
 import com.google.auto.service.AutoService;
 import com.google.common.annotations.VisibleForTesting;
@@ -38,6 +40,7 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import javax.lang.model.SourceVersion;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
 
 /**
  * The annotation processor responsible for generating the classes that drive the Dagger 2.0
@@ -45,6 +48,7 @@
  *
  * <p>TODO(gak): give this some better documentation
  */
+@IncrementalAnnotationProcessor(DYNAMIC)
 @AutoService(Processor.class)
 public class ComponentProcessor extends BasicAnnotationProcessor {
   private final Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins;
@@ -92,7 +96,7 @@
   @Override
   public Set<String> getSupportedOptions() {
     ImmutableSet.Builder<String> options = ImmutableSet.builder();
-    options.addAll(CompilerOptions.supportedOptions());
+    options.addAll(ProcessingEnvironmentCompilerOptions.supportedOptions());
     options.addAll(bindingGraphPlugins.allSupportedOptions());
     if (compilerOptions.useGradleIncrementalProcessing()) {
       options.add("org.gradle.annotation.processing.isolating");
diff --git a/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java b/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
index 723673b..b8c6049 100644
--- a/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
+++ b/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
@@ -21,6 +21,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.Preconditions;
+import dagger.internal.codegen.javapoet.Expression;
 
 /** A binding expression for component provision methods. */
 final class ComponentProvisionBindingExpression extends SimpleInvocationBindingExpression {
diff --git a/java/dagger/internal/codegen/ComponentRequirement.java b/java/dagger/internal/codegen/ComponentRequirement.java
index 924ed95..3bed5da 100644
--- a/java/dagger/internal/codegen/ComponentRequirement.java
+++ b/java/dagger/internal/codegen/ComponentRequirement.java
@@ -19,9 +19,9 @@
 import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
 import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.STATIC;
 
@@ -34,6 +34,10 @@
 import dagger.Binds;
 import dagger.BindsOptionalOf;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.ComponentRequirementProto;
+import dagger.internal.codegen.serialization.ComponentRequirementProto.BoundInstanceRequirement;
 import dagger.model.BindingKind;
 import dagger.model.Key;
 import dagger.multibindings.Multibinds;
@@ -183,6 +187,29 @@
     return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
   }
 
+  /** Creates a proto representation of this requirement. */
+  ComponentRequirementProto toProto() {
+    switch (kind()) {
+      case DEPENDENCY:
+        return ComponentRequirementProto.newBuilder()
+            .setDependency(TypeProtoConverter.toProto(type()))
+            .build();
+      case MODULE:
+        return ComponentRequirementProto.newBuilder()
+            .setModule(TypeProtoConverter.toProto(type()))
+            .build();
+      case BOUND_INSTANCE:
+        return ComponentRequirementProto.newBuilder()
+            .setBoundInstance(
+                BoundInstanceRequirement.newBuilder()
+                    .setKey(KeyFactory.toProto(key().get()))
+                    .setNullable(overrideNullPolicy().equals(Optional.of(NullPolicy.ALLOW)))
+                    .setVariableName(variableName()))
+            .build();
+    }
+    throw new AssertionError(this);
+  }
+
   static ComponentRequirement forDependency(TypeMirror type) {
     return new AutoValue_ComponentRequirement(
         Kind.DEPENDENCY,
diff --git a/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java b/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
index e0cdd5a..d6aa053 100644
--- a/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
+++ b/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
 
 /**
  * A binding expression for instances bound with {@link dagger.BindsInstance} and instances of
diff --git a/java/dagger/internal/codegen/ComponentRequirementExpressions.java b/java/dagger/internal/codegen/ComponentRequirementExpressions.java
index 779a7b5..4ab58c9 100644
--- a/java/dagger/internal/codegen/ComponentRequirementExpressions.java
+++ b/java/dagger/internal/codegen/ComponentRequirementExpressions.java
@@ -36,6 +36,7 @@
 import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
diff --git a/java/dagger/internal/codegen/ComponentTreeTraverser.java b/java/dagger/internal/codegen/ComponentTreeTraverser.java
index 545ec99..cc1efd2 100644
--- a/java/dagger/internal/codegen/ComponentTreeTraverser.java
+++ b/java/dagger/internal/codegen/ComponentTreeTraverser.java
@@ -16,7 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
@@ -47,12 +46,7 @@
   private final Deque<ComponentPath> componentPaths = new ArrayDeque<>();
 
   /** Constructs a traverser for a root (component, not subcomponent) binding graph. */
-  public ComponentTreeTraverser(BindingGraph rootGraph, CompilerOptions compilerOptions) {
-    checkArgument(
-        !rootGraph.componentDescriptor().isSubcomponent()
-            || compilerOptions.aheadOfTimeSubcomponents(),
-        "only root graphs can be traversed, not %s",
-        rootGraph.componentTypeElement().getQualifiedName());
+  public ComponentTreeTraverser(BindingGraph rootGraph) {
     bindingGraphPath.add(rootGraph);
     componentPaths.add(ComponentPath.create(ImmutableList.of(rootGraph.componentTypeElement())));
   }
diff --git a/java/dagger/internal/codegen/ComponentValidator.java b/java/dagger/internal/codegen/ComponentValidator.java
index 7285dac..7739915 100644
--- a/java/dagger/internal/codegen/ComponentValidator.java
+++ b/java/dagger/internal/codegen/ComponentValidator.java
@@ -33,13 +33,13 @@
 import static dagger.internal.codegen.ComponentKind.annotationsFor;
 import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
 import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
-import static dagger.internal.codegen.DaggerElements.getAnnotationMirror;
-import static dagger.internal.codegen.DaggerElements.getAnyAnnotation;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs;
 import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
 import static java.util.Comparator.comparing;
 import static javax.lang.model.element.ElementKind.CLASS;
 import static javax.lang.model.element.ElementKind.INTERFACE;
@@ -59,6 +59,8 @@
 import com.google.common.collect.Sets;
 import dagger.Component;
 import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.producers.CancellationPolicy;
@@ -78,7 +80,9 @@
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.ExecutableType;
 import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
 import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor8;
 
 /**
  * Performs superficial validation of the contract of the {@link Component} and {@link
@@ -482,35 +486,27 @@
 
   private static <T extends Element> void validateComponentDependencies(
       ValidationReport.Builder<T> report, Iterable<TypeMirror> types) {
-    validateTypesAreDeclared(report, types, "component dependency");
     for (TypeMirror type : types) {
-      if (moduleAnnotation(MoreTypes.asTypeElement(type)).isPresent()) {
-        report.addError(
-            String.format("%s is a module, which cannot be a component dependency", type));
-      }
+      type.accept(CHECK_DEPENDENCY_TYPES, report);
     }
   }
 
-  private static <T extends Element> void validateTypesAreDeclared(
-      final ValidationReport.Builder<T> report, Iterable<TypeMirror> types, final String typeName) {
-    for (TypeMirror type : types) {
-      type.accept(
-          new SimpleTypeVisitor6<Void, Void>() {
-            @Override
-            protected Void defaultAction(TypeMirror e, Void aVoid) {
-              report.addError(String.format("%s is not a valid %s type", e, typeName));
-              return null;
-            }
+  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 t, Void aVoid) {
-              // Declared types are valid
-              return null;
-            }
-          },
-          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) {
diff --git a/java/dagger/internal/codegen/ConfigurationAnnotations.java b/java/dagger/internal/codegen/ConfigurationAnnotations.java
index eee7c8b..a9bba29 100644
--- a/java/dagger/internal/codegen/ConfigurationAnnotations.java
+++ b/java/dagger/internal/codegen/ConfigurationAnnotations.java
@@ -21,9 +21,9 @@
 import static com.google.common.collect.Iterables.consumingIterable;
 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
 import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
 import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeListValue;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 import static javax.lang.model.util.ElementFilter.typesIn;
 
 import com.google.auto.common.MoreElements;
@@ -34,6 +34,8 @@
 import com.google.common.collect.Sets;
 import dagger.Component;
 import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.lang.annotation.Annotation;
 import java.util.ArrayDeque;
 import java.util.List;
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index 4da1e44..0958bc8 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -68,7 +68,7 @@
   }
 
   /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
-  Optional<TypeMirror> contributedPrimitiveType() {
+  final Optional<TypeMirror> contributedPrimitiveType() {
     return bindingElement()
         .filter(bindingElement -> bindingElement instanceof ExecutableElement)
         .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
@@ -102,7 +102,7 @@
    *
    * <p>All other bindings use the {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR} strategy.
    */
-  FactoryCreationStrategy factoryCreationStrategy() {
+  final FactoryCreationStrategy factoryCreationStrategy() {
     switch (kind()) {
       case DELEGATE:
         return DELEGATE;
@@ -133,9 +133,8 @@
       case SET_VALUES:
       case UNIQUE:
         return key().type();
-      default:
-        throw new AssertionError();
     }
+    throw new AssertionError();
   }
 
   final boolean isSyntheticMultibinding() {
diff --git a/java/dagger/internal/codegen/ContributionType.java b/java/dagger/internal/codegen/ContributionType.java
index e3f8e37..66b5289 100644
--- a/java/dagger/internal/codegen/ContributionType.java
+++ b/java/dagger/internal/codegen/ContributionType.java
@@ -22,7 +22,7 @@
 import dagger.multibindings.ElementsIntoSet;
 import dagger.multibindings.IntoMap;
 import dagger.multibindings.IntoSet;
-import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Element;
 
 /** Whether a binding or declaration is for a unique contribution or a map or set multibinding. */
 enum ContributionType {
@@ -49,16 +49,17 @@
   }
 
   /**
-   * The contribution type from a binding method annotations. Presumes a well-formed binding method
-   * (at most one of @IntoSet, @IntoMap, @ElementsIntoSet and @Provides.type). {@link
-   * ProvidesMethodValidator} and {@link ProducesMethodValidator} validate correctness on their own.
+   * The contribution type from a binding element's annotations. Presumes a well-formed binding
+   * element (at most one of @IntoSet, @IntoMap, @ElementsIntoSet and @Provides.type). {@link
+   * BindingMethodValidator} and {@link BindsInstanceProcessingStep} validate correctness on their
+   * own.
    */
-  static ContributionType fromBindingMethod(ExecutableElement method) {
-    if (isAnnotationPresent(method, IntoMap.class)) {
+  static ContributionType fromBindingElement(Element element) {
+    if (isAnnotationPresent(element, IntoMap.class)) {
       return ContributionType.MAP;
-    } else if (isAnnotationPresent(method, IntoSet.class)) {
+    } else if (isAnnotationPresent(element, IntoSet.class)) {
       return ContributionType.SET;
-    } else if (isAnnotationPresent(method, ElementsIntoSet.class)) {
+    } else if (isAnnotationPresent(element, ElementsIntoSet.class)) {
       return ContributionType.SET_VALUES;
     }
     return ContributionType.UNIQUE;
diff --git a/java/dagger/internal/codegen/DaggerKythePlugin.java b/java/dagger/internal/codegen/DaggerKythePlugin.java
index 238a50a..5a685ef 100644
--- a/java/dagger/internal/codegen/DaggerKythePlugin.java
+++ b/java/dagger/internal/codegen/DaggerKythePlugin.java
@@ -20,7 +20,7 @@
 package dagger.internal.codegen;
 
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 
 import com.google.auto.service.AutoService;
 import com.google.common.collect.Iterables;
diff --git a/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java b/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
index 502b921..c41cf2c 100644
--- a/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
+++ b/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
@@ -20,6 +20,7 @@
 
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
 
diff --git a/java/dagger/internal/codegen/DelegateBindingExpression.java b/java/dagger/internal/codegen/DelegateBindingExpression.java
index 8f1ed0c..8cdf6d1 100644
--- a/java/dagger/internal/codegen/DelegateBindingExpression.java
+++ b/java/dagger/internal/codegen/DelegateBindingExpression.java
@@ -19,12 +19,15 @@
 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.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.RequestKinds.requestType;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static dagger.model.BindingKind.DELEGATE;
 
 import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.RequestKind;
 import javax.lang.model.type.TypeMirror;
 
diff --git a/java/dagger/internal/codegen/DelegateDeclaration.java b/java/dagger/internal/codegen/DelegateDeclaration.java
index 2dbff78..67991de 100644
--- a/java/dagger/internal/codegen/DelegateDeclaration.java
+++ b/java/dagger/internal/codegen/DelegateDeclaration.java
@@ -28,6 +28,7 @@
 import com.google.common.collect.Iterables;
 import dagger.Binds;
 import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import java.util.Optional;
 import javax.inject.Inject;
@@ -79,7 +80,7 @@
               Iterables.getOnlyElement(bindsMethod.getParameters()),
               Iterables.getOnlyElement(resolvedMethod.getParameterTypes()));
       return new AutoValue_DelegateDeclaration(
-          ContributionType.fromBindingMethod(bindsMethod),
+          ContributionType.fromBindingElement(bindsMethod),
           keyFactory.forBindsMethod(bindsMethod, contributingModule),
           Optional.<Element>of(bindsMethod),
           Optional.of(contributingModule),
diff --git a/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java b/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
index 5d5f463..7524cdf 100644
--- a/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
+++ b/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
@@ -22,6 +22,8 @@
 
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.model.DependencyRequest;
 
 /** A framework instance creation expression for a {@link dagger.Binds @Binds} binding. */
 final class DelegatingFrameworkInstanceCreationExpression
@@ -42,12 +44,13 @@
 
   @Override
   public CodeBlock creationExpression() {
-    FrameworkDependency frameworkDependency = getOnlyElement(binding.frameworkDependencies());
+    DependencyRequest dependency = getOnlyElement(binding.dependencies());
     return CodeBlocks.cast(
         componentBindingExpressions
             .getDependencyExpression(
-                bindingRequest(frameworkDependency), componentImplementation.name())
+                bindingRequest(dependency.key(), binding.frameworkType()),
+                componentImplementation.name())
             .codeBlock(),
-        frameworkDependency.frameworkClass());
+        binding.frameworkType().frameworkClass());
   }
 }
diff --git a/java/dagger/internal/codegen/DependencyEdgeImpl.java b/java/dagger/internal/codegen/DependencyEdgeImpl.java
index b777251..64b0845 100644
--- a/java/dagger/internal/codegen/DependencyEdgeImpl.java
+++ b/java/dagger/internal/codegen/DependencyEdgeImpl.java
@@ -45,7 +45,7 @@
     String string =
         dependencyRequest
             .requestElement()
-            .map(DaggerElements::elementToString)
+            .map(ElementFormatter::elementToString)
             .orElseGet(
                 () ->
                     "synthetic request for "
diff --git a/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
index c934059..b42f9c4 100644
--- a/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
+++ b/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
@@ -19,8 +19,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
-import static dagger.internal.codegen.TypeNames.dependencyMethodProducerOf;
-import static dagger.internal.codegen.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
diff --git a/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
index ca050e6..8532481 100644
--- a/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
@@ -21,7 +21,7 @@
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
-import static dagger.internal.codegen.TypeNames.providerOf;
+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;
diff --git a/java/dagger/internal/codegen/DependencyRequestFactory.java b/java/dagger/internal/codegen/DependencyRequestFactory.java
index a66edd0..3ad12e2 100644
--- a/java/dagger/internal/codegen/DependencyRequestFactory.java
+++ b/java/dagger/internal/codegen/DependencyRequestFactory.java
@@ -34,6 +34,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListenableFuture;
 import dagger.Lazy;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -122,9 +123,8 @@
       case UNIQUE:
         throw new IllegalArgumentException(
             "multibindingContribution must be a multibinding: " + multibindingContribution);
-      default:
-        throw new AssertionError(multibindingContribution.toString());
     }
+    throw new AssertionError(multibindingContribution.toString());
   }
 
   DependencyRequest forRequiredResolvedVariable(
diff --git a/java/dagger/internal/codegen/DependencyRequestFormatter.java b/java/dagger/internal/codegen/DependencyRequestFormatter.java
index 357cca1..25becaf 100644
--- a/java/dagger/internal/codegen/DependencyRequestFormatter.java
+++ b/java/dagger/internal/codegen/DependencyRequestFormatter.java
@@ -16,11 +16,12 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.elementToString;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
 import static dagger.internal.codegen.RequestKinds.requestType;
 
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.producers.Produces;
 import java.util.Optional;
@@ -143,8 +144,8 @@
         return "injected";
 
       case PRODUCED:
-      default:
-        throw new AssertionError("illegal request kind for method: " + request);
+        break;
     }
+    throw new AssertionError("illegal request kind for method: " + request);
   }
 }
diff --git a/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
index b67e1d0..4f2f622 100644
--- a/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
@@ -22,6 +22,8 @@
 
 import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import javax.lang.model.type.TypeMirror;
diff --git a/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java b/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
new file mode 100644
index 0000000..45e99a8
--- /dev/null
+++ b/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.serialization.ProtoSerialization.fromAnnotationValue;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.ComponentDefinitionType;
+import dagger.internal.ConfigureInitializationParameters;
+import dagger.internal.ModifiableBinding;
+import dagger.internal.ModifiableModule;
+import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
+import dagger.internal.codegen.serialization.BindingRequestProto;
+import dagger.internal.codegen.serialization.ComponentRequirementProto;
+import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
+import dagger.internal.codegen.serialization.KeyProto;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Reconstructs {@link ComponentImplementation}s that have already been compiled. Uses metadata
+ * annotations on the generated type and it's methods to reconstitute the equivalent {@link
+ * ComponentImplementation} state.
+ */
+final class DeserializedComponentImplementationBuilder {
+  private final CompilerOptions compilerOptions;
+  private final ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
+  private final TypeProtoConverter typeProtoConverter;
+  private final KeyFactory keyFactory;
+
+  @Inject
+  DeserializedComponentImplementationBuilder(
+      CompilerOptions compilerOptions,
+      ComponentCreatorImplementationFactory componentCreatorImplementationFactory,
+      TypeProtoConverter typeProtoConverter,
+      KeyFactory keyFactory) {
+    this.compilerOptions = compilerOptions;
+    this.componentCreatorImplementationFactory = componentCreatorImplementationFactory;
+    this.typeProtoConverter = typeProtoConverter;
+    this.keyFactory = keyFactory;
+  }
+
+  /** Creates a new {@link ComponentImplementation} from a compiled component. */
+  ComponentImplementation create(ComponentDescriptor component, TypeElement generatedComponent) {
+    Optional<ComponentImplementation> superclassImplementation =
+        deserializedSuperclassImplementation(
+            component, MoreTypes.asTypeElement(generatedComponent.getSuperclass()));
+
+    ComponentImplementation componentImplementation =
+        ComponentImplementation.forDeserializedComponent(
+            component,
+            ClassName.get(generatedComponent),
+            generatedComponent.getNestingKind(),
+            superclassImplementation,
+            compilerOptions);
+
+    componentImplementation.setCreatorImplementation(
+        superclassImplementation.isPresent()
+            ? Optional.empty()
+            : componentCreatorImplementationFactory.create(
+                componentImplementation, Optional.empty()));
+
+    // TODO(b/117833324): Consider omitting superclass implementations, so that only one instance of
+    // ComponentImplementation needs to be created (in most cases, we don't care about nested levels
+    // of superclass implementations, except for the base implementation). If that's possible, use
+    // getLocalAndInheritedMethods instead of getEnclosedElements() here.
+    for (ExecutableElement method : methodsIn(generatedComponent.getEnclosedElements())) {
+      getAnnotationMirror(method, ModifiableBinding.class)
+          .asSet()
+          .forEach(
+              annotation ->
+                  addModifiableBindingMethod(componentImplementation, method, annotation));
+
+      getAnnotationMirror(method, ModifiableModule.class)
+          .asSet()
+          .forEach(
+              annotation -> addModifiableModuleMethod(componentImplementation, method, annotation));
+
+      getAnnotationMirror(method, ConfigureInitializationParameters.class)
+          .asSet()
+          .forEach(
+              annotation ->
+                  setConfigureInitializationMethod(componentImplementation, method, annotation));
+    }
+
+    for (TypeElement nestedType : typesIn(generatedComponent.getEnclosedElements())) {
+      addChildImplementation(component, componentImplementation, nestedType);
+    }
+
+    return componentImplementation;
+  }
+
+  private Optional<ComponentImplementation> deserializedSuperclassImplementation(
+      ComponentDescriptor component, TypeElement superclassElement) {
+    return isAnnotationPresent(superclassElement, ComponentDefinitionType.class)
+        ? Optional.of(create(component, superclassElement))
+        : Optional.empty();
+  }
+
+  private void addModifiableBindingMethod(
+      ComponentImplementation componentImplementation,
+      ExecutableElement method,
+      AnnotationMirror metadataAnnotation) {
+    ModifiableBindingType modifiableBindingType =
+        ModifiableBindingType.valueOf(
+            getAnnotationValue(metadataAnnotation, "modifiableBindingType").getValue().toString());
+
+    BindingRequest request =
+        parseBindingRequest(getAnnotationValue(metadataAnnotation, "bindingRequest"));
+
+    ImmutableList<Key> multibindingContributions =
+        asAnnotationValues(getAnnotationValue(metadataAnnotation, "multibindingContributions"))
+            .stream()
+            .map(this::parseKey)
+            .collect(toImmutableList());
+
+    componentImplementation.addModifiableBindingMethod(
+        modifiableBindingType,
+        request,
+        method.getReturnType(),
+        methodDeclaration(method),
+        method.getModifiers().contains(FINAL));
+    componentImplementation.registerImplementedMultibindingKeys(request, multibindingContributions);
+  }
+
+  private BindingRequest fromProto(BindingRequestProto bindingRequest) {
+    Key key = keyFactory.fromProto(bindingRequest.getKey());
+    return bindingRequest.getFrameworkType().equals(FrameworkTypeWrapper.FrameworkType.UNKNOWN)
+        ? bindingRequest(key, RequestKind.valueOf(bindingRequest.getRequestKind().name()))
+        : bindingRequest(key, FrameworkType.valueOf(bindingRequest.getFrameworkType().name()));
+  }
+
+  /**
+   * Returns a {@link MethodSpec} for a {@link
+   * dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod}. The method contents
+   * are not relevant since this represents a method that has already been compiled.
+   *
+   * <p>Ideally this could be {@code MethodSpec.overriding(method).build()}, but that doesn't work
+   * for {@code final} methods
+   */
+  private MethodSpec methodDeclaration(ExecutableElement method) {
+    return methodBuilder(method.getSimpleName().toString())
+        .addModifiers(method.getModifiers())
+        .returns(TypeName.get(method.getReturnType()))
+        .build();
+  }
+
+  private void addModifiableModuleMethod(
+      ComponentImplementation componentImplementation,
+      ExecutableElement method,
+      AnnotationMirror metadataAnnotation) {
+    ComponentRequirement moduleRequirement =
+        parseComponentRequirement(getAnnotationValue(metadataAnnotation, "value"));
+    componentImplementation.registerModifiableModuleMethod(
+        moduleRequirement, method.getSimpleName().toString());
+  }
+
+  private void setConfigureInitializationMethod(
+      ComponentImplementation componentImplementation,
+      ExecutableElement method,
+      AnnotationMirror metadataAnnotation) {
+    ImmutableSet<ComponentRequirement> parameters =
+        asAnnotationValues(getAnnotationValue(metadataAnnotation, "value")).stream()
+            .map(this::parseComponentRequirement)
+            .collect(toImmutableSet());
+
+    componentImplementation.setConfigureInitializationMethod(
+        ConfigureInitializationMethod.create(MethodSpec.overriding(method).build(), parameters));
+  }
+
+  private void addChildImplementation(
+      ComponentDescriptor component,
+      ComponentImplementation componentImplementation,
+      TypeElement nestedType) {
+    getAnnotationMirror(nestedType, ComponentDefinitionType.class)
+        .transform(annotation -> (TypeMirror) getAnnotationValue(annotation, "value").getValue())
+        .transform(MoreTypes::asTypeElement)
+        .asSet()
+        .forEach(
+            componentDefinitionType -> {
+              ComponentDescriptor child =
+                  component.childComponentsByElement().get(componentDefinitionType);
+              componentImplementation.addChild(child, create(child, nestedType));
+            });
+  }
+
+  private Key parseKey(AnnotationValue annotationValue) {
+    return keyFactory.fromProto(
+        fromAnnotationValue(annotationValue, KeyProto.getDefaultInstance()));
+  }
+
+  private BindingRequest parseBindingRequest(AnnotationValue annotationValue) {
+    return fromProto(
+        fromAnnotationValue(annotationValue, BindingRequestProto.getDefaultInstance()));
+  }
+
+  private ComponentRequirement parseComponentRequirement(AnnotationValue annotationValue) {
+    return fromProto(
+        fromAnnotationValue(annotationValue, ComponentRequirementProto.getDefaultInstance()));
+  }
+
+  private ComponentRequirement fromProto(ComponentRequirementProto proto) {
+    switch (proto.getRequirementCase()) {
+      case MODULE:
+        return ComponentRequirement.forModule(typeProtoConverter.fromProto(proto.getModule()));
+      case DEPENDENCY:
+        return ComponentRequirement.forDependency(
+            typeProtoConverter.fromProto(proto.getDependency()));
+      case BOUND_INSTANCE:
+        return ComponentRequirement.forBoundInstance(
+            keyFactory.fromProto(proto.getBoundInstance().getKey()),
+            proto.getBoundInstance().getNullable(),
+            proto.getBoundInstance().getVariableName());
+      case REQUIREMENT_NOT_SET:
+        // fall through
+    }
+    throw new AssertionError(proto);
+  }
+}
diff --git a/java/dagger/internal/codegen/DiagnosticReporterFactory.java b/java/dagger/internal/codegen/DiagnosticReporterFactory.java
index ada013b..f657cee 100644
--- a/java/dagger/internal/codegen/DiagnosticReporterFactory.java
+++ b/java/dagger/internal/codegen/DiagnosticReporterFactory.java
@@ -24,17 +24,16 @@
 import static com.google.common.collect.Iterables.indexOf;
 import static com.google.common.collect.Iterables.transform;
 import static com.google.common.collect.Lists.asList;
-import static dagger.internal.codegen.DaggerElements.DECLARATION_ORDER;
-import static dagger.internal.codegen.DaggerElements.closestEnclosingTypeElement;
-import static dagger.internal.codegen.DaggerElements.elementEncloses;
-import static dagger.internal.codegen.DaggerElements.elementFormatter;
-import static dagger.internal.codegen.DaggerElements.elementToString;
 import static dagger.internal.codegen.DaggerGraphs.shortestPath;
 import static dagger.internal.codegen.DaggerStreams.instancesOf;
 import static dagger.internal.codegen.DaggerStreams.presentValues;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
 import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
 import static java.util.Collections.min;
 import static java.util.Comparator.comparing;
 import static java.util.Comparator.comparingInt;
@@ -50,6 +49,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Table;
 import com.google.errorprone.annotations.FormatMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingGraph;
 import dagger.model.BindingGraph.ChildFactoryMethodEdge;
 import dagger.model.BindingGraph.ComponentNode;
@@ -77,6 +77,7 @@
   private final DaggerTypes types;
   private final Messager messager;
   private final DependencyRequestFormatter dependencyRequestFormatter;
+  private final ElementFormatter elementFormatter;
   private final CompilerOptions compilerOptions;
 
   @Inject
@@ -84,10 +85,12 @@
       DaggerTypes types,
       Messager messager,
       DependencyRequestFormatter dependencyRequestFormatter,
+      ElementFormatter elementFormatter,
       CompilerOptions compilerOptions) {
     this.types = types;
     this.messager = messager;
     this.dependencyRequestFormatter = dependencyRequestFormatter;
+    this.elementFormatter = elementFormatter;
     this.compilerOptions = compilerOptions;
   }
 
@@ -233,12 +236,14 @@
         Diagnostic.Kind diagnosticKind,
         CharSequence message,
         @NullableDecl Element elementToReport) {
-      if (graph.isModuleBindingGraph()) {
-        if (compilerOptions.moduleBindingValidationType().equals(NONE)) {
+      if (graph.isFullBindingGraph()) {
+        ValidationType validationType =
+            compilerOptions.fullBindingGraphValidationType(rootComponent);
+        if (validationType.equals(NONE)) {
           return;
         }
         if (diagnosticKind.equals(ERROR)) {
-          diagnosticKind = compilerOptions.moduleBindingValidationType().diagnosticKind().get();
+          diagnosticKind = validationType.diagnosticKind().get();
         }
       }
       reportedDiagnosticKinds.add(diagnosticKind);
@@ -296,12 +301,12 @@
       @Override
       public String toString() {
         StringBuilder message =
-            graph.isModuleBindingGraph()
+            graph.isFullBindingGraph()
                 ? new StringBuilder()
                 : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
 
-        // Print the dependency trace unless it's a module binding graph
-        if (!graph.isModuleBindingGraph()) {
+        // Print the dependency trace unless it's a full binding graph
+        if (!graph.isFullBindingGraph()) {
           dependencyTrace.forEach(
               edge ->
                   dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
@@ -316,7 +321,7 @@
                 // if printing entry points, skip entry points and the traced request
                 .filter(
                     request ->
-                        graph.isModuleBindingGraph()
+                        graph.isFullBindingGraph()
                             || (!request.isEntryPoint() && !isTracedRequest(request)))
                 .map(request -> request.dependencyRequest().requestElement())
                 .flatMap(presentValues())
@@ -324,14 +329,14 @@
         if (!requestsToPrint.isEmpty()) {
           message
               .append("\nIt is")
-              .append(graph.isModuleBindingGraph() ? " " : " also ")
+              .append(graph.isFullBindingGraph() ? " " : " also ")
               .append("requested at:");
-          elementFormatter().formatIndentedList(message, requestsToPrint, 1);
+          elementFormatter.formatIndentedList(message, requestsToPrint, 1);
         }
 
-        // Print the remaining entry points, showing which component they're in, unless we're in a
-        // module binding graph
-        if (!graph.isModuleBindingGraph() && entryPoints.size() > 1) {
+        // Print the remaining entry points, showing which component they're in, unless it's a full
+        // binding graph
+        if (!graph.isFullBindingGraph() && entryPoints.size() > 1) {
           message.append("\nThe following other entry points also depend on it:");
           entryPointFormatter.formatIndentedList(
               message,
diff --git a/java/dagger/internal/codegen/DuplicateBindingsValidator.java b/java/dagger/internal/codegen/DuplicateBindingsValidator.java
index c1515e5..3eed45f 100644
--- a/java/dagger/internal/codegen/DuplicateBindingsValidator.java
+++ b/java/dagger/internal/codegen/DuplicateBindingsValidator.java
@@ -206,7 +206,7 @@
             .append(formatWithComponentPath(explicitBinding))
             .append(
                 "\nThis condition was never validated before, and will soon be an error. "
-                    + "See https://google.github.io/dagger/conflicting-inject.");
+                    + "See https://dagger.dev/conflicting-inject.");
 
     diagnosticReporter.reportBinding(diagnosticKind, explicitBinding, message.toString());
   }
diff --git a/java/dagger/internal/codegen/ElementFormatter.java b/java/dagger/internal/codegen/ElementFormatter.java
new file mode 100644
index 0000000..40d7606
--- /dev/null
+++ b/java/dagger/internal/codegen/ElementFormatter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static java.util.stream.Collectors.joining;
+
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * Formats elements into a useful string representation.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+final class ElementFormatter extends Formatter<Element> {
+  @Inject
+  ElementFormatter() {}
+
+  @Override
+  public String format(Element element) {
+    return elementToString(element);
+  }
+
+  /**
+   * Returns a useful string form for an element.
+   *
+   * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+   *
+   * <p>Parameters are given with their enclosing executable, with other parameters elided.
+   */
+  static String elementToString(Element element) {
+    return element.accept(ELEMENT_TO_STRING, null);
+  }
+
+  private static final ElementVisitor<String, Void> ELEMENT_TO_STRING =
+      new ElementKindVisitor8<String, Void>() {
+        @Override
+        public String visitExecutable(ExecutableElement executableElement, Void aVoid) {
+          return enclosingTypeAndMemberName(executableElement)
+              .append(
+                  executableElement.getParameters().stream()
+                      .map(parameter -> parameter.asType().toString())
+                      .collect(joining(", ", "(", ")")))
+              .toString();
+        }
+
+        @Override
+        public String visitVariableAsParameter(VariableElement parameter, Void aVoid) {
+          ExecutableElement methodOrConstructor = asExecutable(parameter.getEnclosingElement());
+          return enclosingTypeAndMemberName(methodOrConstructor)
+              .append('(')
+              .append(
+                  formatArgumentInList(
+                      methodOrConstructor.getParameters().indexOf(parameter),
+                      methodOrConstructor.getParameters().size(),
+                      parameter.getSimpleName()))
+              .append(')')
+              .toString();
+        }
+
+        @Override
+        public String visitVariableAsField(VariableElement field, Void aVoid) {
+          return enclosingTypeAndMemberName(field).toString();
+        }
+
+        @Override
+        public String visitType(TypeElement type, Void aVoid) {
+          return type.getQualifiedName().toString();
+        }
+
+        @Override
+        protected String defaultAction(Element element, Void aVoid) {
+          throw new UnsupportedOperationException(
+              "Can't determine string for " + element.getKind() + " element " + element);
+        }
+
+        private StringBuilder enclosingTypeAndMemberName(Element element) {
+          StringBuilder name = new StringBuilder(element.getEnclosingElement().accept(this, null));
+          if (!element.getSimpleName().contentEquals("<init>")) {
+            name.append('.').append(element.getSimpleName());
+          }
+          return name;
+        }
+      };
+}
diff --git a/java/dagger/internal/codegen/FactoryGenerator.java b/java/dagger/internal/codegen/FactoryGenerator.java
index 3961311..d367bc5 100644
--- a/java/dagger/internal/codegen/FactoryGenerator.java
+++ b/java/dagger/internal/codegen/FactoryGenerator.java
@@ -17,13 +17,10 @@
 package dagger.internal.codegen;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Maps.transformValues;
 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
 import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
@@ -33,7 +30,11 @@
 import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.TypeNames.factoryOf;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
 import static dagger.model.BindingKind.PROVISION;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
@@ -54,6 +55,9 @@
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.List;
 import java.util.Optional;
@@ -161,15 +165,13 @@
     if (binding.requiresModuleInstance()) {
       uniqueFieldNames.claim("module");
     }
-    ImmutableMap.Builder<Key, FieldSpec> fields = ImmutableMap.builder();
-    generateBindingFieldsForDependencies(binding)
-        .forEach(
-            (key, frameworkField) -> {
-              TypeName type = frameworkField.type();
-              String name = uniqueFieldNames.getUniqueName(frameworkField.name());
-              fields.put(key, FieldSpec.builder(type, name, PRIVATE, FINAL).build());
-            });
-    return fields.build();
+    return ImmutableMap.copyOf(
+        transformValues(
+            generateBindingFieldsForDependencies(binding),
+            field ->
+                FieldSpec.builder(
+                        field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
+                    .build()));
   }
 
   private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
diff --git a/java/dagger/internal/codegen/ForwardingCompilerOptions.java b/java/dagger/internal/codegen/ForwardingCompilerOptions.java
new file mode 100644
index 0000000..4a1deda
--- /dev/null
+++ b/java/dagger/internal/codegen/ForwardingCompilerOptions.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** A {@link CompilerOptions} object that delegates to another one. */
+class ForwardingCompilerOptions extends CompilerOptions {
+
+  private final CompilerOptions delegate;
+
+  ForwardingCompilerOptions(CompilerOptions delegate) {
+    this.delegate = checkNotNull(delegate);
+  }
+
+  @Override
+  boolean usesProducers() {
+    return delegate.usesProducers();
+  }
+
+  @Override
+  boolean fastInit() {
+    return delegate.fastInit();
+  }
+
+  @Override
+  boolean formatGeneratedSource() {
+    return delegate.formatGeneratedSource();
+  }
+
+  @Override
+  boolean writeProducerNameInToken() {
+    return delegate.writeProducerNameInToken();
+  }
+
+  @Override
+  Diagnostic.Kind nullableValidationKind() {
+    return delegate.nullableValidationKind();
+  }
+
+  @Override
+  Diagnostic.Kind privateMemberValidationKind() {
+    return delegate.privateMemberValidationKind();
+  }
+
+  @Override
+  Diagnostic.Kind staticMemberValidationKind() {
+    return delegate.staticMemberValidationKind();
+  }
+
+  @Override
+  boolean ignorePrivateAndStaticInjectionForComponent() {
+    return delegate.ignorePrivateAndStaticInjectionForComponent();
+  }
+
+  @Override
+  ValidationType scopeCycleValidationType() {
+    return delegate.scopeCycleValidationType();
+  }
+
+  @Override
+  boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+    return delegate.warnIfInjectionFactoryNotGeneratedUpstream();
+  }
+
+  @Override
+  boolean headerCompilation() {
+    return delegate.headerCompilation();
+  }
+
+  @Override
+  boolean aheadOfTimeSubcomponents() {
+    return delegate.aheadOfTimeSubcomponents();
+  }
+
+  @Override
+  boolean forceUseSerializedComponentImplementations() {
+    return delegate.forceUseSerializedComponentImplementations();
+  }
+
+  @Override
+  boolean emitModifiableMetadataAnnotations() {
+    return delegate.emitModifiableMetadataAnnotations();
+  }
+
+  @Override
+  boolean useGradleIncrementalProcessing() {
+    return delegate.useGradleIncrementalProcessing();
+  }
+
+  @Override
+  ValidationType fullBindingGraphValidationType(TypeElement element) {
+    return delegate.fullBindingGraphValidationType(element);
+  }
+
+  @Override
+  Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+    return delegate.moduleHasDifferentScopesDiagnosticKind();
+  }
+
+  @Override
+  ValidationType explicitBindingConflictsWithInjectValidationType() {
+    return delegate.explicitBindingConflictsWithInjectValidationType();
+  }
+}
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
index b69e3b2..a3de083 100644
--- a/java/dagger/internal/codegen/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
@@ -17,8 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
 import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
 import static javax.lang.model.element.Modifier.PRIVATE;
 
 import com.squareup.javapoet.ClassName;
@@ -26,6 +26,8 @@
 import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.internal.DelegateFactory;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.producers.internal.DelegateProducer;
 import java.util.Optional;
 
@@ -121,9 +123,6 @@
       case DELEGATED:
       case INITIALIZED:
         break;
-
-      default:
-        throw new AssertionError("Unhandled initialization state: " + fieldInitializationState);
     }
   }
 
diff --git a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
index 870aef9..6f62d66 100644
--- a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
@@ -17,11 +17,14 @@
 package dagger.internal.codegen;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.FieldSpec;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeMirror;
 
diff --git a/java/dagger/internal/codegen/FrameworkType.java b/java/dagger/internal/codegen/FrameworkType.java
index 0e279fc..f4e3779 100644
--- a/java/dagger/internal/codegen/FrameworkType.java
+++ b/java/dagger/internal/codegen/FrameworkType.java
@@ -29,6 +29,8 @@
 import dagger.Lazy;
 import dagger.internal.DoubleCheck;
 import dagger.internal.ProviderOfLazy;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.RequestKind;
 import dagger.producers.Produced;
diff --git a/java/dagger/internal/codegen/GenerationOptionsModule.java b/java/dagger/internal/codegen/GenerationOptionsModule.java
index 9820d4f..aa3c461 100644
--- a/java/dagger/internal/codegen/GenerationOptionsModule.java
+++ b/java/dagger/internal/codegen/GenerationOptionsModule.java
@@ -19,6 +19,7 @@
 import dagger.Module;
 import dagger.Provides;
 import dagger.internal.GenerationOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Optional;
 
 /** Adds bindings for serializing and rereading {@link GenerationOptions}. */
diff --git a/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java b/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
index d4e0820..69b107c 100644
--- a/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
+++ b/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
@@ -23,21 +23,27 @@
 import com.google.common.util.concurrent.ListenableFuture;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import dagger.model.RequestKind;
+import javax.lang.model.SourceVersion;
 
 final class ImmediateFutureBindingExpression extends BindingExpression {
 
   private final ComponentBindingExpressions componentBindingExpressions;
   private final DaggerTypes types;
+  private final SourceVersion sourceVersion;
   private final Key key;
 
   ImmediateFutureBindingExpression(
       ResolvedBindings resolvedBindings,
       ComponentBindingExpressions componentBindingExpressions,
-      DaggerTypes types) {
+      DaggerTypes types,
+      SourceVersion sourceVersion) {
     this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
     this.types = checkNotNull(types);
+    this.sourceVersion = checkNotNull(sourceVersion);
     this.key = resolvedBindings.key();
   }
 
@@ -52,14 +58,16 @@
     Expression expression =
         componentBindingExpressions.getDependencyExpression(
             bindingRequest(key, RequestKind.INSTANCE), requestingClass);
-    // Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
-    // cast.
-    //
-    // For example, javac7 cannot detect that Futures.immediateFuture(ImmutableSet.of("T"))
-    // can safely be assigned to ListenableFuture<Set<T>>.
-    if (!types.isSameType(expression.type(), key.type())) {
-      return CodeBlock.of(
-          "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
+    if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+      // Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
+      // cast.
+      //
+      // For example, javac7 cannot detect that Futures.immediateFuture(ImmutableSet.of("T"))
+      // can safely be assigned to ListenableFuture<Set<T>>.
+      if (!types.isSameType(expression.type(), key.type())) {
+        return CodeBlock.of(
+            "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
+      }
     }
     return expression.codeBlock();
   }
diff --git a/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
index 878c4ec..19c4d19 100644
--- a/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
+++ b/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
@@ -24,6 +24,8 @@
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
 import javax.inject.Inject;
diff --git a/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
index 7bbbd07..d649e46 100644
--- a/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
+++ b/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
@@ -16,9 +16,9 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.closestEnclosingTypeElement;
 import static dagger.internal.codegen.Formatter.INDENT;
 import static dagger.internal.codegen.Scopes.getReadableSource;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
 import static dagger.model.BindingKind.INJECTION;
 import static java.util.stream.Collectors.joining;
 import static javax.tools.Diagnostic.Kind.ERROR;
@@ -70,9 +70,11 @@
                 ComponentNode componentNode =
                     bindingGraph.componentNode(binding.componentPath()).get();
                 if (!componentNode.scopes().contains(scope)) {
-                  // @Inject bindings in full binding graphs will appear at the properly scoped
-                  // ancestor component, so ignore them here.
-                  if (binding.kind().equals(INJECTION) && bindingGraph.isFullBindingGraph()) {
+                  // @Inject bindings in module or subcomponent binding graphs will appear at the
+                  // properly scoped ancestor component, so ignore them here.
+                  if (binding.kind().equals(INJECTION)
+                      && (bindingGraph.rootComponentNode().isSubcomponent()
+                          || !bindingGraph.rootComponentNode().isRealComponent())) {
                     return;
                   }
                   incompatibleBindings.put(componentNode, binding);
diff --git a/java/dagger/internal/codegen/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
index 3771a8e..45dc391 100644
--- a/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
+++ b/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
@@ -35,6 +35,8 @@
 import dagger.Component;
 import dagger.MembersInjector;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.ArrayDeque;
 import java.util.Deque;
diff --git a/java/dagger/internal/codegen/InjectValidator.java b/java/dagger/internal/codegen/InjectValidator.java
index 949d0c5..d3c4ce8 100644
--- a/java/dagger/internal/codegen/InjectValidator.java
+++ b/java/dagger/internal/codegen/InjectValidator.java
@@ -29,7 +29,9 @@
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.ValidationReport.Builder;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Scope;
 import java.util.Optional;
 import java.util.Set;
@@ -307,7 +309,8 @@
     return false;
   }
 
-  private void checkInjectIntoPrivateClass(Element element, Builder<TypeElement> builder) {
+  private void checkInjectIntoPrivateClass(
+      Element element, ValidationReport.Builder<TypeElement> builder) {
     if (!Accessibility.isElementAccessibleFromOwnPackage(
         DaggerElements.closestEnclosingTypeElement(element))) {
       builder.addItem(
diff --git a/java/dagger/internal/codegen/InjectionMethod.java b/java/dagger/internal/codegen/InjectionMethod.java
index b739088..2785b18 100644
--- a/java/dagger/internal/codegen/InjectionMethod.java
+++ b/java/dagger/internal/codegen/InjectionMethod.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
 import static javax.lang.model.element.Modifier.PUBLIC;
 import static javax.lang.model.element.Modifier.STATIC;
 
@@ -34,6 +34,8 @@
 import com.squareup.javapoet.ParameterSpec;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.List;
 import java.util.Optional;
 import javax.lang.model.element.ExecutableElement;
diff --git a/java/dagger/internal/codegen/InjectionMethods.java b/java/dagger/internal/codegen/InjectionMethods.java
index e0fecc0..d61aaa5 100644
--- a/java/dagger/internal/codegen/InjectionMethods.java
+++ b/java/dagger/internal/codegen/InjectionMethods.java
@@ -19,18 +19,18 @@
 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
-import static dagger.internal.codegen.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.toConcatenatedCodeBlock;
 import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
 import static dagger.internal.codegen.RequestKinds.requestTypeName;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.TypeNames.rawTypeName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static java.util.stream.Collectors.toList;
 import static javax.lang.model.element.Modifier.STATIC;
 import static javax.lang.model.type.TypeKind.VOID;
@@ -43,6 +43,9 @@
 import com.squareup.javapoet.ParameterSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import dagger.model.RequestKind;
 import java.util.ArrayList;
@@ -261,9 +264,8 @@
               MoreElements.asVariable(injectionSite.element()),
               methodName,
               elements);
-        default:
-          throw new AssertionError(injectionSite);
       }
+      throw new AssertionError(injectionSite);
     }
 
     /**
@@ -513,7 +515,7 @@
     injectionMethod
         .methodBodyBuilder()
         .add(checkNotNullPolicy.checkForNull(proxyInvocation.build()))
-        .add(";");
+        .add(";\n");
     return injectionMethod.build();
   }
 
diff --git a/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
index fdb225f..2d2e8cb 100644
--- a/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
@@ -22,6 +22,7 @@
 
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.CodeBlocks;
 import javax.inject.Provider;
 
 /**
diff --git a/java/dagger/internal/codegen/InjectionSiteFactory.java b/java/dagger/internal/codegen/InjectionSiteFactory.java
new file mode 100644
index 0000000..d09f91d
--- /dev/null
+++ b/java/dagger/internal/codegen/InjectionSiteFactory.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.MembersInjectionBinding.InjectionSite.Kind.METHOD;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/** A factory for {@link Binding} objects. */
+final class InjectionSiteFactory {
+  private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
+      new ElementKindVisitor8<Optional<InjectionSite>, DeclaredType>(Optional.empty()) {
+        @Override
+        public Optional<InjectionSite> visitExecutableAsMethod(
+            ExecutableElement method, DeclaredType type) {
+          ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(type, method));
+          return Optional.of(
+              InjectionSite.method(
+                  method,
+                  dependencyRequestFactory.forRequiredResolvedVariables(
+                      method.getParameters(), resolved.getParameterTypes())));
+        }
+
+        @Override
+        public Optional<InjectionSite> visitVariableAsField(
+            VariableElement field, DeclaredType type) {
+          if (!isAnnotationPresent(field, Inject.class)
+              || field.getModifiers().contains(PRIVATE)
+              || field.getModifiers().contains(STATIC)) {
+            return Optional.empty();
+          }
+          TypeMirror resolved = types.asMemberOf(type, field);
+          return Optional.of(
+              InjectionSite.field(
+                  field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
+        }
+      };
+
+  private final DaggerTypes types;
+  private final DaggerElements elements;
+  private final DependencyRequestFactory dependencyRequestFactory;
+
+  @Inject
+  InjectionSiteFactory(
+      DaggerTypes types,
+      DaggerElements elements,
+      DependencyRequestFactory dependencyRequestFactory) {
+    this.types = types;
+    this.elements = elements;
+    this.dependencyRequestFactory = dependencyRequestFactory;
+  }
+
+  /** Returns the injection sites for a type. */
+  ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
+    Set<InjectionSite> injectionSites = new HashSet<>();
+    List<TypeElement> ancestors = new ArrayList<>();
+    SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
+    for (Optional<DeclaredType> currentType = Optional.of(declaredType);
+        currentType.isPresent();
+        currentType = types.nonObjectSuperclass(currentType.get())) {
+      DeclaredType type = currentType.get();
+      ancestors.add(MoreElements.asType(type.asElement()));
+      for (Element enclosedElement : type.asElement().getEnclosedElements()) {
+        Optional<InjectionSite> maybeInjectionSite =
+            injectionSiteVisitor.visit(enclosedElement, type);
+        if (maybeInjectionSite.isPresent()) {
+          InjectionSite injectionSite = maybeInjectionSite.get();
+          if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
+            injectionSites.add(injectionSite);
+          }
+          if (injectionSite.kind().equals(METHOD)) {
+            ExecutableElement injectionSiteMethod =
+                MoreElements.asExecutable(injectionSite.element());
+            overriddenMethodMap.put(
+                injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
+          }
+        }
+      }
+    }
+    return ImmutableSortedSet.copyOf(
+        // supertypes before subtypes
+        Comparator.comparing(
+                (InjectionSite injectionSite) ->
+                    ancestors.indexOf(injectionSite.element().getEnclosingElement()))
+            .reversed()
+            // fields before methods
+            .thenComparing(injectionSite -> injectionSite.element().getKind())
+            // then sort by whichever element comes first in the parent
+            // this isn't necessary, but makes the processor nice and predictable
+            .thenComparing(InjectionSite::element, DECLARATION_ORDER),
+        injectionSites);
+  }
+
+  private boolean shouldBeInjected(
+      Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
+    if (!isAnnotationPresent(injectionSite, Inject.class)
+        || injectionSite.getModifiers().contains(PRIVATE)
+        || injectionSite.getModifiers().contains(STATIC)) {
+      return false;
+    }
+
+    if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
+      return true;
+    }
+
+    // For each method with the same name belonging to any descendant class, return false if any
+    // method has already overridden the injectionSite method. To decrease the number of methods
+    // that are checked, we store the already injected methods in a SetMultimap and only
+    // check the methods with the same name.
+    ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
+    TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
+    for (ExecutableElement method :
+        overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
+      if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/java/dagger/internal/codegen/InnerSwitchingProviders.java b/java/dagger/internal/codegen/InnerSwitchingProviders.java
index 91c1317..74c4366 100644
--- a/java/dagger/internal/codegen/InnerSwitchingProviders.java
+++ b/java/dagger/internal/codegen/InnerSwitchingProviders.java
@@ -26,6 +26,8 @@
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import javax.inject.Provider;
 import javax.lang.model.type.TypeMirror;
diff --git a/java/dagger/internal/codegen/JavacPluginModule.java b/java/dagger/internal/codegen/JavacPluginModule.java
index 44f11fd..bc5b66e 100644
--- a/java/dagger/internal/codegen/JavacPluginModule.java
+++ b/java/dagger/internal/codegen/JavacPluginModule.java
@@ -25,11 +25,14 @@
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.annotation.processing.Messager;
 import javax.inject.Inject;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.Types;
 import javax.tools.Diagnostic;
 
@@ -41,21 +44,97 @@
 abstract class JavacPluginModule {
   @Provides
   static CompilerOptions compilerOptions() {
-    return CompilerOptions.builder()
-        .usesProducers(true)
-        .writeProducerNameInToken(true)
-        .nullableValidationKind(NOTE)
-        .privateMemberValidationKind(NOTE)
-        .staticMemberValidationKind(NOTE)
-        .ignorePrivateAndStaticInjectionForComponent(false)
-        .scopeCycleValidationType(NONE)
-        .warnIfInjectionFactoryNotGeneratedUpstream(false)
-        .fastInit(false)
-        .aheadOfTimeSubcomponents(false)
-        .moduleBindingValidationType(NONE)
-        .moduleHasDifferentScopesDiagnosticKind(NOTE)
-        .explicitBindingConflictsWithInjectValidationType(NONE)
-        .build();
+    return new CompilerOptions() {
+      @Override
+      boolean usesProducers() {
+        return true;
+      }
+
+      @Override
+      boolean fastInit() {
+        return false;
+      }
+
+      @Override
+      boolean formatGeneratedSource() {
+        return false;
+      }
+
+      @Override
+      boolean writeProducerNameInToken() {
+        return true;
+      }
+
+      @Override
+      Diagnostic.Kind nullableValidationKind() {
+        return NOTE;
+      }
+
+      @Override
+      Diagnostic.Kind privateMemberValidationKind() {
+        return NOTE;
+      }
+
+      @Override
+      Diagnostic.Kind staticMemberValidationKind() {
+        return NOTE;
+      }
+
+      @Override
+      boolean ignorePrivateAndStaticInjectionForComponent() {
+        return false;
+      }
+
+      @Override
+      ValidationType scopeCycleValidationType() {
+        return NONE;
+      }
+
+      @Override
+      boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+        return false;
+      }
+
+      @Override
+      boolean headerCompilation() {
+        return false;
+      }
+
+      @Override
+      boolean aheadOfTimeSubcomponents() {
+        return false;
+      }
+
+      @Override
+      boolean forceUseSerializedComponentImplementations() {
+        return false;
+      }
+
+      @Override
+      boolean emitModifiableMetadataAnnotations() {
+        return false;
+      }
+
+      @Override
+      boolean useGradleIncrementalProcessing() {
+        return false;
+      }
+
+      @Override
+      ValidationType fullBindingGraphValidationType(TypeElement element) {
+        return NONE;
+      }
+
+      @Override
+      Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+        return NOTE;
+      }
+
+      @Override
+      ValidationType explicitBindingConflictsWithInjectValidationType() {
+        return NONE;
+      }
+    };
   }
 
   @Binds
diff --git a/java/dagger/internal/codegen/KeyFactory.java b/java/dagger/internal/codegen/KeyFactory.java
index 633c306..b6ac27f 100644
--- a/java/dagger/internal/codegen/KeyFactory.java
+++ b/java/dagger/internal/codegen/KeyFactory.java
@@ -23,13 +23,13 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerTypes.isFutureType;
 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
 import static dagger.internal.codegen.MapKeys.getMapKey;
 import static dagger.internal.codegen.MapKeys.mapKeyType;
 import static dagger.internal.codegen.Optionals.firstPresent;
 import static dagger.internal.codegen.RequestKinds.extractKeyType;
 import static dagger.internal.codegen.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
 import static java.util.Arrays.asList;
 import static javax.lang.model.element.ElementKind.METHOD;
 
@@ -37,6 +37,9 @@
 import com.google.common.collect.ImmutableSet;
 import dagger.Binds;
 import dagger.BindsOptionalOf;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.KeyProto;
 import dagger.model.Key;
 import dagger.model.Key.MultibindingContributionIdentifier;
 import dagger.model.RequestKind;
@@ -65,11 +68,19 @@
 final class KeyFactory {
   private final DaggerTypes types;
   private final DaggerElements elements;
+  private final TypeProtoConverter typeProtoConverter;
+  private final AnnotationProtoConverter annotationProtoConverter;
 
   @Inject
-  KeyFactory(DaggerTypes types, DaggerElements elements) {
+  KeyFactory(
+      DaggerTypes types,
+      DaggerElements elements,
+      TypeProtoConverter typeProtoConverter,
+      AnnotationProtoConverter annotationProtoConverter) {
     this.types = checkNotNull(types);
     this.elements = checkNotNull(elements);
+    this.typeProtoConverter = typeProtoConverter;
+    this.annotationProtoConverter = annotationProtoConverter;
   }
 
   private TypeMirror boxPrimitives(TypeMirror type) {
@@ -148,7 +159,7 @@
     ExecutableType methodType =
         MoreTypes.asExecutable(
             types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
-    ContributionType contributionType = ContributionType.fromBindingMethod(method);
+    ContributionType contributionType = ContributionType.fromBindingElement(method);
     TypeMirror returnType = methodType.getReturnType();
     if (frameworkType.isPresent()
         && frameworkType.get().equals(elements.getTypeElement(Producer.class))
@@ -213,15 +224,14 @@
         // TODO(gak): do we want to allow people to use "covariant return" here?
         checkArgument(SetType.isSet(returnType));
         return returnType;
-      default:
-        throw new AssertionError();
     }
+    throw new AssertionError();
   }
 
   /**
    * Returns the key for a binding associated with a {@link DelegateDeclaration}.
    *
-   * If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
+   * <p>If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
    * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
    * delegateDeclaration} is not a map contribution, its key is returned.
    */
@@ -424,4 +434,36 @@
             .type(extractKeyType(getRequestKind(optionalValueType), optionalValueType))
             .build());
   }
+
+  /** Translates a {@link Key} to a proto representation. */
+  static KeyProto toProto(Key key) {
+    KeyProto.Builder builder =
+        KeyProto.newBuilder().setType(TypeProtoConverter.toProto(key.type()));
+    key.qualifier().map(AnnotationProtoConverter::toProto).ifPresent(builder::setQualifier);
+    key.multibindingContributionIdentifier()
+        .ifPresent(
+            mci ->
+                builder
+                    .getMultibindingContributionIdentifierBuilder()
+                    .setModule(mci.module())
+                    .setBindingElement(mci.bindingElement()));
+    return builder.build();
+  }
+
+  /** Creates a {@link Key} from its proto representation. */
+  Key fromProto(KeyProto key) {
+    Key.Builder builder = Key.builder(typeProtoConverter.fromProto(key.getType()));
+    if (key.hasQualifier()) {
+      builder.qualifier(annotationProtoConverter.fromProto(key.getQualifier()));
+    }
+    if (key.hasMultibindingContributionIdentifier()) {
+      KeyProto.MultibindingContributionIdentifier multibindingContributionIdentifier =
+          key.getMultibindingContributionIdentifier();
+      builder.multibindingContributionIdentifier(
+          new MultibindingContributionIdentifier(
+              multibindingContributionIdentifier.getBindingElement(),
+              multibindingContributionIdentifier.getModule()));
+    }
+    return builder.build();
+  }
 }
diff --git a/java/dagger/internal/codegen/Keys.java b/java/dagger/internal/codegen/Keys.java
index 274d244..670fda7 100644
--- a/java/dagger/internal/codegen/Keys.java
+++ b/java/dagger/internal/codegen/Keys.java
@@ -18,6 +18,7 @@
 
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.Optional;
 import javax.lang.model.element.AnnotationMirror;
diff --git a/java/dagger/internal/codegen/META-INF/gradle/incremental.annotation.processors b/java/dagger/internal/codegen/META-INF/gradle/incremental.annotation.processors
deleted file mode 100644
index db550fe..0000000
--- a/java/dagger/internal/codegen/META-INF/gradle/incremental.annotation.processors
+++ /dev/null
@@ -1 +0,0 @@
-dagger.internal.codegen.ComponentProcessor,dynamic
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
index 9d4fdf0..526c493 100644
--- a/java/dagger/internal/codegen/MapBindingExpression.java
+++ b/java/dagger/internal/codegen/MapBindingExpression.java
@@ -18,10 +18,10 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static dagger.model.BindingKind.MULTIBOUND_MAP;
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
@@ -30,6 +30,9 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.MapBuilder;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingKind;
 import dagger.model.DependencyRequest;
 import java.util.Collections;
diff --git a/java/dagger/internal/codegen/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/MapFactoryCreationExpression.java
index 0fff44b..187b792 100644
--- a/java/dagger/internal/codegen/MapFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/MapFactoryCreationExpression.java
@@ -20,9 +20,10 @@
 import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
 import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
 import dagger.producers.Produced;
 import dagger.producers.Producer;
 import javax.inject.Provider;
@@ -67,19 +68,18 @@
       builder.add("<$T, $T>", mapType.keyType(), valueType);
     }
 
-    ImmutableList<FrameworkDependency> frameworkDependencies = binding.frameworkDependencies();
-    builder.add("builder($L)", frameworkDependencies.size());
+    builder.add("builder($L)", binding.dependencies().size());
 
     superContributions()
         .ifPresent(superContributions -> builder.add(".putAll($L)", superContributions));
 
-    for (FrameworkDependency frameworkDependency : frameworkDependenciesToImplement()) {
+    for (DependencyRequest dependency : dependenciesToImplement()) {
       ContributionBinding contributionBinding =
-          graph.contributionBindings().get(frameworkDependency.key()).contributionBinding();
+          graph.contributionBindings().get(dependency.key()).contributionBinding();
       builder.add(
           ".put($L, $L)",
           getMapKeyExpression(contributionBinding, componentImplementation.name(), elements),
-          multibindingDependencyExpression(frameworkDependency));
+          multibindingDependencyExpression(dependency));
     }
     builder.add(".build()");
 
diff --git a/java/dagger/internal/codegen/MapKeyAccessibility.java b/java/dagger/internal/codegen/MapKeyAccessibility.java
index fe52e3b..ce27877 100644
--- a/java/dagger/internal/codegen/MapKeyAccessibility.java
+++ b/java/dagger/internal/codegen/MapKeyAccessibility.java
@@ -16,8 +16,9 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 
+import dagger.internal.codegen.langmodel.Accessibility;
 import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
diff --git a/java/dagger/internal/codegen/MapKeyProcessingStep.java b/java/dagger/internal/codegen/MapKeyProcessingStep.java
index 6e5dc10..19975a7 100644
--- a/java/dagger/internal/codegen/MapKeyProcessingStep.java
+++ b/java/dagger/internal/codegen/MapKeyProcessingStep.java
@@ -23,6 +23,7 @@
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.lang.annotation.Annotation;
 import java.util.Set;
 import javax.annotation.processing.Messager;
diff --git a/java/dagger/internal/codegen/MapKeyValidator.java b/java/dagger/internal/codegen/MapKeyValidator.java
index dfc0b07..f2568ef 100644
--- a/java/dagger/internal/codegen/MapKeyValidator.java
+++ b/java/dagger/internal/codegen/MapKeyValidator.java
@@ -19,6 +19,7 @@
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
 import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.List;
 import javax.inject.Inject;
 import javax.lang.model.element.Element;
diff --git a/java/dagger/internal/codegen/MapKeys.java b/java/dagger/internal/codegen/MapKeys.java
index 48fb1e6..d8da6af 100644
--- a/java/dagger/internal/codegen/MapKeys.java
+++ b/java/dagger/internal/codegen/MapKeys.java
@@ -35,6 +35,8 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import javax.lang.model.element.AnnotationMirror;
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
index 2806207..c42c7c9 100644
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -17,17 +17,17 @@
 package dagger.internal.codegen;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
-import static dagger.internal.codegen.TypeNames.FACTORY;
-import static dagger.internal.codegen.TypeNames.MAP_FACTORY;
-import static dagger.internal.codegen.TypeNames.PRODUCER;
-import static dagger.internal.codegen.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.TypeNames.PROVIDER;
+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;
@@ -35,6 +35,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
 import java.util.List;
 import java.util.Optional;
 import javax.lang.model.type.DeclaredType;
diff --git a/java/dagger/internal/codegen/MembersInjectionBinding.java b/java/dagger/internal/codegen/MembersInjectionBinding.java
index 95ad100..4918fa1 100644
--- a/java/dagger/internal/codegen/MembersInjectionBinding.java
+++ b/java/dagger/internal/codegen/MembersInjectionBinding.java
@@ -28,8 +28,10 @@
 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.
@@ -123,5 +125,16 @@
           .collect(toList())
           .indexOf(element());
     }
+
+    static InjectionSite field(VariableElement element, DependencyRequest dependency) {
+      return new AutoValue_MembersInjectionBinding_InjectionSite(
+          Kind.FIELD, element, ImmutableSet.of(dependency));
+    }
+
+    static InjectionSite method(
+        ExecutableElement element, Iterable<DependencyRequest> dependencies) {
+      return new AutoValue_MembersInjectionBinding_InjectionSite(
+          Kind.METHOD, element, ImmutableSet.copyOf(dependencies));
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
index 12e8070..e9a8ffc 100644
--- a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
+++ b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
@@ -23,6 +23,7 @@
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.ParameterSpec;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
 import javax.lang.model.element.ExecutableElement;
 
 /**
diff --git a/java/dagger/internal/codegen/MembersInjectionMethods.java b/java/dagger/internal/codegen/MembersInjectionMethods.java
index bef30fc..1e04669 100644
--- a/java/dagger/internal/codegen/MembersInjectionMethods.java
+++ b/java/dagger/internal/codegen/MembersInjectionMethods.java
@@ -18,9 +18,9 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static javax.lang.model.element.Modifier.PRIVATE;
 
 import com.google.common.collect.ImmutableSet;
@@ -31,6 +31,8 @@
 import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.LinkedHashMap;
 import java.util.Map;
diff --git a/java/dagger/internal/codegen/MembersInjectorGenerator.java b/java/dagger/internal/codegen/MembersInjectorGenerator.java
index dedf267..4360af7 100644
--- a/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ b/java/dagger/internal/codegen/MembersInjectorGenerator.java
@@ -20,18 +20,18 @@
 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.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
 import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
 import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
 import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
@@ -50,6 +50,8 @@
 import dagger.MembersInjector;
 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.Map.Entry;
 import java.util.Optional;
diff --git a/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java b/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
index 25d034a..8e863e5 100644
--- a/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
@@ -19,8 +19,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.TypeNames.INSTANCE_FACTORY;
-import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTORS;
+import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTORS;
 
 import com.google.auto.common.MoreTypes;
 import com.squareup.javapoet.CodeBlock;
diff --git a/java/dagger/internal/codegen/MethodBindingExpression.java b/java/dagger/internal/codegen/MethodBindingExpression.java
index dfaa4ef..2120e80 100644
--- a/java/dagger/internal/codegen/MethodBindingExpression.java
+++ b/java/dagger/internal/codegen/MethodBindingExpression.java
@@ -32,6 +32,8 @@
 import dagger.internal.MemoizedSentinel;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.RequestKind;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
diff --git a/java/dagger/internal/codegen/MethodSignature.java b/java/dagger/internal/codegen/MethodSignature.java
index 75f25e6..ef5c9c5 100644
--- a/java/dagger/internal/codegen/MethodSignature.java
+++ b/java/dagger/internal/codegen/MethodSignature.java
@@ -23,6 +23,7 @@
 import com.google.common.base.Equivalence;
 import com.google.common.collect.ImmutableList;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.List;
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.ExecutableType;
diff --git a/java/dagger/internal/codegen/MethodSignatureFormatter.java b/java/dagger/internal/codegen/MethodSignatureFormatter.java
index 28eb614..012d5b0 100644
--- a/java/dagger/internal/codegen/MethodSignatureFormatter.java
+++ b/java/dagger/internal/codegen/MethodSignatureFormatter.java
@@ -22,6 +22,7 @@
 
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
index c7fa17c..610d052 100644
--- a/java/dagger/internal/codegen/MissingBindingExpression.java
+++ b/java/dagger/internal/codegen/MissingBindingExpression.java
@@ -18,6 +18,7 @@
 
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
 
diff --git a/java/dagger/internal/codegen/MissingBindingValidator.java b/java/dagger/internal/codegen/MissingBindingValidator.java
index 642f7a4..f39361d 100644
--- a/java/dagger/internal/codegen/MissingBindingValidator.java
+++ b/java/dagger/internal/codegen/MissingBindingValidator.java
@@ -23,6 +23,7 @@
 import static dagger.internal.codegen.RequestKinds.canBeSatisfiedByProductionBinding;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingGraph;
 import dagger.model.BindingGraph.ComponentNode;
 import dagger.model.BindingGraph.DependencyEdge;
@@ -54,7 +55,7 @@
 
   @Override
   public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
-    // Don't report missing bindings when validating a full graph or a graph built from a
+    // Don't report missing bindings when validating a full binding graph or a graph built from a
     // subcomponent.
     if (graph.isFullBindingGraph() || graph.rootComponentNode().isSubcomponent()) {
       return;
diff --git a/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
index 1020944..412acae 100644
--- a/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
@@ -25,6 +25,9 @@
 import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
 
diff --git a/java/dagger/internal/codegen/ModifiableBindingExpressions.java b/java/dagger/internal/codegen/ModifiableBindingExpressions.java
index 4481f3e..76bcb8b 100644
--- a/java/dagger/internal/codegen/ModifiableBindingExpressions.java
+++ b/java/dagger/internal/codegen/ModifiableBindingExpressions.java
@@ -27,8 +27,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.MethodSpec;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ComponentImplementation.MethodSpecKind;
 import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingKind;
 import dagger.model.DependencyRequest;
 import java.util.Optional;
@@ -62,22 +64,23 @@
   }
 
   /**
-   * Records the binding exposed by the given component method as modifiable, if it is, and returns
-   * the {@link ModifiableBindingType} associated with the binding.
+   * Adds {@code method} to the component implementation. If the binding for the method is
+   * modifiable, also registers the relevant modifiable binding information.
    */
-  ModifiableBindingType registerComponentMethodIfModifiable(
+  void addPossiblyModifiableComponentMethod(
       ComponentMethodDescriptor componentMethod, MethodSpec method) {
     BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
     ModifiableBindingType modifiableBindingType = getModifiableBindingType(request);
     if (modifiableBindingType.isModifiable()) {
-      componentImplementation.registerModifiableBindingMethod(
+      componentImplementation.addModifiableComponentMethod(
           modifiableBindingType,
           request,
           componentMethod.resolvedReturnType(types),
           method,
           newModifiableBindingWillBeFinalized(modifiableBindingType, request));
+    } else {
+      componentImplementation.addMethod(MethodSpecKind.COMPONENT_METHOD, method);
     }
-    return modifiableBindingType;
   }
 
   /**
@@ -381,8 +384,17 @@
           // Futures backed by provision bindings are inlined and contain no wrapping producer, so
           // if the binding is modifiable and is resolved as a provision binding in a superclass
           // but later resolved as a production binding, we can't take the same shortcut as before.
-          if (componentImplementation.superclassImplementation().isPresent()) {
-            return bindingTypeChanged(request, resolvedBindings);
+          Optional<ComponentImplementation> superclassImplementation =
+              componentImplementation.superclassImplementation();
+          if (superclassImplementation.isPresent()) {
+            if (superclassImplementation.get().isDeserializedImplementation()) {
+              // TODO(b/117833324): consider serializing the binding type so that we don't need to
+              // branch here. Or, instead, consider removing this optimization entirely if there
+              // aren't that many FUTURE entry point methods to justify the extra code.
+              break;
+            } else {
+              return bindingTypeChanged(request, resolvedBindings);
+            }
           }
           return false;
 
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
index 55dd1ef..ead708d 100644
--- a/java/dagger/internal/codegen/ModifiableBindingMethods.java
+++ b/java/dagger/internal/codegen/ModifiableBindingMethods.java
@@ -16,7 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
 
 import com.google.auto.common.MoreTypes;
@@ -43,13 +42,14 @@
   private final Map<BindingRequest, ModifiableBindingMethod> methods = Maps.newLinkedHashMap();
 
   /** Registers a new method encapsulating a modifiable binding. */
-  void addNewModifiableMethod(
+  void addModifiableMethod(
       ModifiableBindingType type,
       BindingRequest request,
       TypeMirror returnType,
       MethodSpec method,
       boolean finalized) {
-    checkArgument(type.isModifiable());
+    // It's ok for the type to not be modifiable, since it could be overriding a previously
+    // modifiable method (such as with addReimplementedMethod).
     addMethod(ModifiableBindingMethod.create(type, request, returnType, method, finalized));
   }
 
diff --git a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
index 41b0fa4..907466b 100644
--- a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
@@ -23,6 +23,7 @@
 import static javax.lang.model.element.Modifier.PROTECTED;
 
 import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import java.util.Optional;
 
 /**
diff --git a/java/dagger/internal/codegen/ModuleAnnotation.java b/java/dagger/internal/codegen/ModuleAnnotation.java
index 866c66a..27dd071 100644
--- a/java/dagger/internal/codegen/ModuleAnnotation.java
+++ b/java/dagger/internal/codegen/ModuleAnnotation.java
@@ -19,9 +19,9 @@
 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.DaggerElements.getAnyAnnotation;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
 
 import com.google.auto.common.MoreTypes;
 import com.google.auto.value.AutoValue;
@@ -39,6 +39,9 @@
 /** A {@code @Module} or {@code @ProducerModule} annotation. */
 @AutoValue
 abstract class ModuleAnnotation {
+  private static final ImmutableSet<Class<? extends Annotation>> MODULE_ANNOTATIONS =
+      ImmutableSet.of(Module.class, ProducerModule.class);
+
   /** The annotation itself. */
   // This does not use AnnotationMirrors.equivalence() because we want the actual annotation
   // instance.
@@ -102,8 +105,10 @@
         .anyMatch(asTypeElement(annotation.getAnnotationType()).getQualifiedName()::contentEquals);
   }
 
-  private static final ImmutableSet<Class<? extends Annotation>> MODULE_ANNOTATIONS =
-      ImmutableSet.of(Module.class, ProducerModule.class);
+  /** The module annotation types. */
+  static ImmutableSet<Class<? extends Annotation>> moduleAnnotations() {
+    return MODULE_ANNOTATIONS;
+  }
 
   /**
    * Creates an object that represents a {@code @Module} or {@code @ProducerModule}.
diff --git a/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java b/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
index 2798eba..5f0d687 100644
--- a/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
+++ b/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
@@ -28,6 +28,7 @@
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
 import javax.inject.Inject;
diff --git a/java/dagger/internal/codegen/ModuleDescriptor.java b/java/dagger/internal/codegen/ModuleDescriptor.java
index ce248e1..ac7a4b7 100644
--- a/java/dagger/internal/codegen/ModuleDescriptor.java
+++ b/java/dagger/internal/codegen/ModuleDescriptor.java
@@ -22,11 +22,11 @@
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.Verify.verify;
 import static com.google.common.collect.Iterables.transform;
-import static dagger.internal.codegen.DaggerElements.isAnnotationPresent;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
 import static dagger.internal.codegen.SourceFiles.classFileName;
 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
 import static javax.lang.model.type.TypeKind.DECLARED;
 import static javax.lang.model.type.TypeKind.NONE;
 import static javax.lang.model.util.ElementFilter.methodsIn;
@@ -43,6 +43,7 @@
 import dagger.BindsOptionalOf;
 import dagger.Module;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.model.Key;
 import dagger.multibindings.Multibinds;
 import dagger.producers.Produces;
@@ -94,9 +95,7 @@
 
   /** Returns the keys of all bindings declared by this module. */
   ImmutableSet<Key> allBindingKeys() {
-    return allBindingDeclarations().stream()
-        .map(BindingDeclaration::key)
-        .collect(toImmutableSet());
+    return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet());
   }
 
   @Singleton
@@ -178,8 +177,7 @@
 
     @CanIgnoreReturnValue
     private Set<TypeElement> collectIncludedModules(
-        Set<TypeElement> includedModules,
-        TypeElement moduleElement) {
+        Set<TypeElement> includedModules, TypeElement moduleElement) {
       TypeMirror superclass = moduleElement.getSuperclass();
       if (!superclass.getKind().equals(NONE)) {
         verify(superclass.getKind().equals(DECLARED));
@@ -205,8 +203,7 @@
       if (contributesAndroidInjector == null) {
         return ImmutableSet.of();
       }
-      return methodsIn(moduleElement.getEnclosedElements())
-          .stream()
+      return methodsIn(moduleElement.getEnclosedElements()).stream()
           .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType()))
           .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method)))
           .collect(toImmutableSet());
diff --git a/java/dagger/internal/codegen/ModuleKind.java b/java/dagger/internal/codegen/ModuleKind.java
index ae169e3..c93d8ce 100644
--- a/java/dagger/internal/codegen/ModuleKind.java
+++ b/java/dagger/internal/codegen/ModuleKind.java
@@ -17,16 +17,14 @@
 package dagger.internal.codegen;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.DaggerElements.getAnnotationMirror;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
 
 import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import dagger.Module;
-import dagger.Provides;
 import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
 import java.lang.annotation.Annotation;
 import java.util.EnumSet;
 import java.util.Optional;
@@ -37,10 +35,10 @@
 /** Enumeration of the kinds of modules. */
 enum ModuleKind {
   /** {@code @Module} */
-  MODULE(Module.class, Provides.class),
+  MODULE(Module.class),
 
   /** {@code @ProducerModule} */
-  PRODUCER_MODULE(ProducerModule.class, Produces.class);
+  PRODUCER_MODULE(ProducerModule.class);
 
   /** Returns the annotations for modules of the given kinds. */
   static ImmutableSet<Class<? extends Annotation>> annotationsFor(Set<ModuleKind> kinds) {
@@ -74,12 +72,9 @@
   }
 
   private final Class<? extends Annotation> moduleAnnotation;
-  private final Class<? extends Annotation> methodAnnotation;
 
-  ModuleKind(
-      Class<? extends Annotation> moduleAnnotation, Class<? extends Annotation> methodAnnotation) {
+  ModuleKind(Class<? extends Annotation> moduleAnnotation) {
     this.moduleAnnotation = moduleAnnotation;
-    this.methodAnnotation = methodAnnotation;
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ModuleProxies.java b/java/dagger/internal/codegen/ModuleProxies.java
index f5db82a..6426e69 100644
--- a/java/dagger/internal/codegen/ModuleProxies.java
+++ b/java/dagger/internal/codegen/ModuleProxies.java
@@ -16,7 +16,7 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.STATIC;
@@ -24,6 +24,8 @@
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Optional;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
diff --git a/java/dagger/internal/codegen/ModuleValidator.java b/java/dagger/internal/codegen/ModuleValidator.java
index 403a321..094bf34 100644
--- a/java/dagger/internal/codegen/ModuleValidator.java
+++ b/java/dagger/internal/codegen/ModuleValidator.java
@@ -26,14 +26,15 @@
 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
 import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
 import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator;
-import static dagger.internal.codegen.DaggerElements.getAnnotationMirror;
-import static dagger.internal.codegen.DaggerElements.isAnyAnnotationPresent;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.ModuleAnnotation.isModuleAnnotation;
 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
 import static dagger.internal.codegen.MoreAnnotationMirrors.simpleName;
 import static dagger.internal.codegen.MoreAnnotationValues.asType;
 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 import static java.util.EnumSet.noneOf;
 import static java.util.stream.Collectors.joining;
 import static javax.lang.model.element.Modifier.ABSTRACT;
@@ -49,8 +50,11 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.FormatMethod;
 import dagger.Module;
 import dagger.Subcomponent;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingGraph;
 import dagger.producers.ProducerModule;
 import dagger.producers.ProductionSubcomponent;
@@ -225,7 +229,7 @@
     validateSelfCycles(module, builder);
 
     if (builder.build().isClean()
-        && !compilerOptions.moduleBindingValidationType().equals(ValidationType.NONE)) {
+        && !compilerOptions.fullBindingGraphValidationType(module).equals(NONE)) {
       validateModuleBindings(module, builder);
     }
 
@@ -430,6 +434,7 @@
                   return null;
                 }
 
+                @FormatMethod
                 private void reportError(String format, Object... args) {
                   subreport.addError(
                       String.format(format, args), annotatedType, annotation, includedModule);
diff --git a/java/dagger/internal/codegen/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/MonitoringModuleGenerator.java
index 9aa87b3..1738fe9 100644
--- a/java/dagger/internal/codegen/MonitoringModuleGenerator.java
+++ b/java/dagger/internal/codegen/MonitoringModuleGenerator.java
@@ -19,9 +19,9 @@
 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.TypeNames.PRODUCTION_COMPONENT_MONITOR_FACTORY;
-import static dagger.internal.codegen.TypeNames.providerOf;
-import static dagger.internal.codegen.TypeNames.setOf;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCTION_COMPONENT_MONITOR_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.setOf;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.STATIC;
@@ -31,6 +31,7 @@
 import com.squareup.javapoet.TypeSpec;
 import dagger.Module;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.multibindings.Multibinds;
 import dagger.producers.ProductionScope;
 import dagger.producers.monitoring.ProductionComponentMonitor;
diff --git a/java/dagger/internal/codegen/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/MoreAnnotationMirrors.java
index fb5bb74..92825a0 100644
--- a/java/dagger/internal/codegen/MoreAnnotationMirrors.java
+++ b/java/dagger/internal/codegen/MoreAnnotationMirrors.java
@@ -59,15 +59,6 @@
   }
 
   /**
-   * Returns the value named {@code name} from {@code annotation}.
-   *
-   * @throws IllegalArgumentException unless that member represents a single type
-   */
-  static TypeMirror getTypeValue(AnnotationMirror annotation, String name) {
-    return MoreAnnotationValues.asType(getAnnotationValue(annotation, name));
-  }
-
-  /**
    * 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
diff --git a/java/dagger/internal/codegen/MultibindingAnnotations.java b/java/dagger/internal/codegen/MultibindingAnnotations.java
index eaf7a17..b478704 100644
--- a/java/dagger/internal/codegen/MultibindingAnnotations.java
+++ b/java/dagger/internal/codegen/MultibindingAnnotations.java
@@ -16,21 +16,21 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.getAllAnnotations;
+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.ExecutableElement;
+import javax.lang.model.element.Element;
 
 /**
  * Utility methods related to processing {@link IntoSet}, {@link ElementsIntoSet}, and {@link
  * IntoMap}.
  */
 final class MultibindingAnnotations {
-  static ImmutableSet<AnnotationMirror> forMethod(ExecutableElement method) {
+  static ImmutableSet<AnnotationMirror> forElement(Element method) {
     return getAllAnnotations(method, IntoSet.class, ElementsIntoSet.class, IntoMap.class);
   }
 }
diff --git a/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java b/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
index 0535062..2bb0a7e 100644
--- a/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
+++ b/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
@@ -16,7 +16,7 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
 import com.google.auto.common.MoreElements;
diff --git a/java/dagger/internal/codegen/MultibindingDeclaration.java b/java/dagger/internal/codegen/MultibindingDeclaration.java
index 95e2b3f..c3724dc 100644
--- a/java/dagger/internal/codegen/MultibindingDeclaration.java
+++ b/java/dagger/internal/codegen/MultibindingDeclaration.java
@@ -23,6 +23,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import dagger.internal.codegen.ContributionType.HasContributionType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import dagger.multibindings.Multibinds;
 import java.util.Map;
diff --git a/java/dagger/internal/codegen/MultibindingExpression.java b/java/dagger/internal/codegen/MultibindingExpression.java
index d5439ec..f0523eb 100644
--- a/java/dagger/internal/codegen/MultibindingExpression.java
+++ b/java/dagger/internal/codegen/MultibindingExpression.java
@@ -21,6 +21,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
diff --git a/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java b/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
index 43e6978..abe161a 100644
--- a/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
@@ -23,6 +23,8 @@
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import java.util.Optional;
 
@@ -43,28 +45,29 @@
   }
 
   /** Returns the expression for a dependency of this multibinding. */
-  protected final CodeBlock multibindingDependencyExpression(
-      FrameworkDependency frameworkDependency) {
+  protected final CodeBlock multibindingDependencyExpression(DependencyRequest dependency) {
     CodeBlock expression =
         componentBindingExpressions
             .getDependencyExpression(
-                BindingRequest.bindingRequest(frameworkDependency), componentImplementation.name())
+                BindingRequest.bindingRequest(dependency.key(), binding.frameworkType()),
+                componentImplementation.name())
             .codeBlock();
+
     return useRawType()
-        ? CodeBlocks.cast(expression, frameworkDependency.frameworkClass())
+        ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClass())
         : expression;
   }
 
-  protected final ImmutableSet<FrameworkDependency> frameworkDependenciesToImplement() {
+  protected final ImmutableSet<DependencyRequest> dependenciesToImplement() {
     ImmutableSet<Key> alreadyImplementedKeys =
         componentImplementation.superclassContributionsMade(bindingRequest());
-    return binding.frameworkDependencies().stream()
-        .filter(frameworkDependency -> !alreadyImplementedKeys.contains(frameworkDependency.key()))
+    return binding.dependencies().stream()
+        .filter(dependency -> !alreadyImplementedKeys.contains(dependency.key()))
         .collect(toImmutableSet());
   }
 
   protected Optional<CodeBlock> superContributions() {
-    if (frameworkDependenciesToImplement().size() == binding.frameworkDependencies().size()) {
+    if (dependenciesToImplement().size() == binding.dependencies().size()) {
       return Optional.empty();
     }
     ModifiableBindingMethod superMethod =
@@ -74,9 +77,7 @@
 
   /** The binding request for this framework instance. */
   protected final BindingRequest bindingRequest() {
-    return BindingRequest.bindingRequest(
-        binding.key(),
-        binding instanceof ProvisionBinding ? FrameworkType.PROVIDER : FrameworkType.PRODUCER_NODE);
+    return BindingRequest.bindingRequest(binding.key(), binding.frameworkType());
   }
 
   /**
diff --git a/java/dagger/internal/codegen/MultibindsMethodValidator.java b/java/dagger/internal/codegen/MultibindsMethodValidator.java
index f912792..bc97d30 100644
--- a/java/dagger/internal/codegen/MultibindsMethodValidator.java
+++ b/java/dagger/internal/codegen/MultibindsMethodValidator.java
@@ -16,15 +16,17 @@
 
 package dagger.internal.codegen;
 
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsMultibindings.NO_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
 import static dagger.internal.codegen.FrameworkTypes.isFrameworkType;
 
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.multibindings.Multibinds;
 import dagger.producers.ProducerModule;
 import javax.inject.Inject;
@@ -53,45 +55,49 @@
   }
 
   @Override
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    super.checkMethod(builder);
-
-    checkParameters(builder);
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
   }
 
-  @Override
-  protected void checkParameters(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!builder.getSubject().getParameters().isEmpty()) {
-      builder.addError(bindingMethods("cannot have parameters"));
+  private class Validator extends MethodValidator {
+    Validator(ExecutableElement element) {
+      super(element);
     }
-  }
 
-  /** Adds an error unless the method returns a {@code Map<K, V>} or {@code Set<T>}. */
-  @Override
-  protected void checkReturnType(ValidationReport.Builder<ExecutableElement> builder) {
-    if (!isPlainMap(builder.getSubject().getReturnType())
-        && !isPlainSet(builder.getSubject().getReturnType())) {
-      builder.addError(bindingMethods("must return Map<K, V> or Set<T>"));
+    @Override
+    protected void checkParameters() {
+      if (!element.getParameters().isEmpty()) {
+        report.addError(bindingMethods("cannot have parameters"));
+      }
     }
-  }
 
-  private boolean isPlainMap(TypeMirror returnType) {
-    if (!MapType.isMap(returnType)) {
-      return false;
+    /** Adds an error unless the method returns a {@code Map<K, V>} or {@code Set<T>}. */
+    @Override
+    protected void checkType() {
+      if (!isPlainMap(element.getReturnType())
+          && !isPlainSet(element.getReturnType())) {
+        report.addError(bindingMethods("must return Map<K, V> or Set<T>"));
+      }
     }
-    MapType mapType = MapType.from(returnType);
-    return !mapType.isRawType()
-        && MoreTypes.isType(mapType.valueType()) // No wildcards.
-        && !isFrameworkType(mapType.valueType());
-  }
 
-  private boolean isPlainSet(TypeMirror returnType) {
-    if (!SetType.isSet(returnType)) {
-      return false;
+    private boolean isPlainMap(TypeMirror returnType) {
+      if (!MapType.isMap(returnType)) {
+        return false;
+      }
+      MapType mapType = MapType.from(returnType);
+      return !mapType.isRawType()
+          && MoreTypes.isType(mapType.valueType()) // No wildcards.
+          && !isFrameworkType(mapType.valueType());
     }
-    SetType setType = SetType.from(returnType);
-    return !setType.isRawType()
-        && MoreTypes.isType(setType.elementType()) // No wildcards.
-        && !isFrameworkType(setType.elementType());
+
+    private boolean isPlainSet(TypeMirror returnType) {
+      if (!SetType.isSet(returnType)) {
+        return false;
+      }
+      SetType setType = SetType.from(returnType);
+      return !setType.isRawType()
+          && MoreTypes.isType(setType.elementType()) // No wildcards.
+          && !isFrameworkType(setType.elementType());
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/OptionalBindingExpression.java b/java/dagger/internal/codegen/OptionalBindingExpression.java
index 1ec9f45..dbb0e37 100644
--- a/java/dagger/internal/codegen/OptionalBindingExpression.java
+++ b/java/dagger/internal/codegen/OptionalBindingExpression.java
@@ -17,30 +17,36 @@
 package dagger.internal.codegen;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.OptionalType.OptionalKind;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
 
 /** A binding expression for optional bindings. */
 final class OptionalBindingExpression extends SimpleInvocationBindingExpression {
   private final ProvisionBinding binding;
   private final ComponentBindingExpressions componentBindingExpressions;
   private final DaggerTypes types;
+  private final SourceVersion sourceVersion;
 
   @Inject
   OptionalBindingExpression(
       ResolvedBindings resolvedBindings,
       ComponentBindingExpressions componentBindingExpressions,
-      DaggerTypes types) {
+      DaggerTypes types,
+      SourceVersion sourceVersion) {
     super(resolvedBindings);
     this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
     this.componentBindingExpressions = componentBindingExpressions;
     this.types = types;
+    this.sourceVersion = sourceVersion;
   }
 
   @Override
@@ -48,14 +54,16 @@
     OptionalType optionalType = OptionalType.from(binding.key());
     OptionalKind optionalKind = optionalType.kind();
     if (binding.dependencies().isEmpty()) {
-      // 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));
+      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());
     }
diff --git a/java/dagger/internal/codegen/OptionalFactories.java b/java/dagger/internal/codegen/OptionalFactories.java
index f4c1460..51c9939 100644
--- a/java/dagger/internal/codegen/OptionalFactories.java
+++ b/java/dagger/internal/codegen/OptionalFactories.java
@@ -24,16 +24,16 @@
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.AnnotationSpecs.Suppression.UNCHECKED;
 import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
 import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
 import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
 import static dagger.internal.codegen.RequestKinds.requestTypeName;
-import static dagger.internal.codegen.TypeNames.PROVIDER;
-import static dagger.internal.codegen.TypeNames.abstractProducerOf;
-import static dagger.internal.codegen.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
@@ -56,6 +56,7 @@
 import dagger.internal.InstanceFactory;
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.OptionalType.OptionalKind;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
 import dagger.model.RequestKind;
 import dagger.producers.Producer;
 import dagger.producers.internal.Producers;
@@ -403,10 +404,8 @@
             throw new UnsupportedOperationException(
                 spec.factoryType() + " objects are not supported");
         }
-
-      default:
-        throw new AssertionError(spec.frameworkType());
     }
+    throw new AssertionError(spec.frameworkType());
   }
 
   /**
diff --git a/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java b/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
index 9d53c0e..ba9e25f 100644
--- a/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
+++ b/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
@@ -52,7 +52,8 @@
             binding,
             componentBindingExpressions
                 .getDependencyExpression(
-                    bindingRequest(getOnlyElement(binding.frameworkDependencies())),
+                    bindingRequest(
+                        getOnlyElement(binding.dependencies()).key(), binding.frameworkType()),
                     componentImplementation.name())
                 .codeBlock());
   }
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
index ba62369..482c123 100644
--- a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
@@ -23,6 +23,7 @@
 import static javax.lang.model.element.Modifier.PRIVATE;
 
 import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 
 /**
  * A binding expression that wraps the dependency expressions in a private, no-arg method.
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
new file mode 100644
index 0000000..cf2475d
--- /dev/null
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.FeatureStatus.DISABLED;
+import static dagger.internal.codegen.FeatureStatus.ENABLED;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EMIT_MODIFIABLE_METADATA_ANNOTATIONS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_ANDROID_MODE;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FAST_INIT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WRITE_PRODUCER_NAME_IN_TOKEN;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.HEADER_COMPILATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.USE_GRADLE_INCREMENTAL_PROCESSING;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.DISABLE_INTER_COMPONENT_SCOPE_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.EXPLICIT_BINDING_CONFLICTS_WITH_INJECT;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.FULL_BINDING_GRAPH_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.MODULE_HAS_DIFFERENT_SCOPES_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.NULLABLE_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.PRIVATE_MEMBER_VALIDATION;
+import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.STATIC_MEMBER_VALIDATION;
+import static dagger.internal.codegen.ValidationType.ERROR;
+import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.ValidationType.WARNING;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Stream.concat;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.producers.Produces;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+final class ProcessingEnvironmentCompilerOptions extends CompilerOptions {
+  /** Returns a valid {@link CompilerOptions} parsed from the processing environment. */
+  static CompilerOptions create(ProcessingEnvironment processingEnvironment) {
+    return new ProcessingEnvironmentCompilerOptions(processingEnvironment).checkValid();
+  }
+
+  private final ProcessingEnvironment processingEnvironment;
+  private final Map<EnumOption<?>, Object> enumOptions = new HashMap<>();
+  private final Map<EnumOption<?>, ImmutableMap<String, ? extends Enum<?>>> allCommandLineOptions =
+      new HashMap<>();
+
+  private ProcessingEnvironmentCompilerOptions(ProcessingEnvironment processingEnvironment) {
+    this.processingEnvironment = processingEnvironment;
+  }
+
+  @Override
+  boolean usesProducers() {
+    return processingEnvironment.getElementUtils().getTypeElement(Produces.class.getCanonicalName())
+        != null;
+  }
+
+  @Override
+  boolean headerCompilation() {
+    return isEnabled(HEADER_COMPILATION);
+  }
+
+  @Override
+  boolean fastInit() {
+    return isEnabled(FAST_INIT);
+  }
+
+  @Override
+  boolean formatGeneratedSource() {
+    return isEnabled(FORMAT_GENERATED_SOURCE);
+  }
+
+  @Override
+  boolean writeProducerNameInToken() {
+    return isEnabled(WRITE_PRODUCER_NAME_IN_TOKEN);
+  }
+
+  @Override
+  Diagnostic.Kind nullableValidationKind() {
+    return diagnosticKind(NULLABLE_VALIDATION);
+  }
+
+  @Override
+  Diagnostic.Kind privateMemberValidationKind() {
+    return diagnosticKind(PRIVATE_MEMBER_VALIDATION);
+  }
+
+  @Override
+  Diagnostic.Kind staticMemberValidationKind() {
+    return diagnosticKind(STATIC_MEMBER_VALIDATION);
+  }
+
+  @Override
+  boolean ignorePrivateAndStaticInjectionForComponent() {
+    return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
+  }
+
+  @Override
+  ValidationType scopeCycleValidationType() {
+    return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION);
+  }
+
+  @Override
+  boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+    return isEnabled(WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM);
+  }
+
+  @Override
+  boolean aheadOfTimeSubcomponents() {
+    return isEnabled(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
+  }
+
+  @Override
+  boolean forceUseSerializedComponentImplementations() {
+    return isEnabled(FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS);
+  }
+
+  @Override
+  boolean emitModifiableMetadataAnnotations() {
+    return isEnabled(EMIT_MODIFIABLE_METADATA_ANNOTATIONS);
+  }
+
+  @Override
+  boolean useGradleIncrementalProcessing() {
+    return isEnabled(USE_GRADLE_INCREMENTAL_PROCESSING);
+  }
+
+  @Override
+  ValidationType fullBindingGraphValidationType(TypeElement element) {
+    return fullBindingGraphValidationType();
+  }
+
+  private ValidationType fullBindingGraphValidationType() {
+    return parseOption(FULL_BINDING_GRAPH_VALIDATION);
+  }
+
+  @Override
+  Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+    return diagnosticKind(MODULE_HAS_DIFFERENT_SCOPES_VALIDATION);
+  }
+
+  @Override
+  ValidationType explicitBindingConflictsWithInjectValidationType() {
+    return parseOption(EXPLICIT_BINDING_CONFLICTS_WITH_INJECT);
+  }
+
+  private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
+    return processingEnvironment.getOptions().containsKey(keyOnlyOption.toString());
+  }
+
+  private boolean isEnabled(Feature feature) {
+    return parseOption(feature).equals(ENABLED);
+  }
+
+  private Diagnostic.Kind diagnosticKind(Validation validation) {
+    return parseOption(validation).diagnosticKind().get();
+  }
+
+  @SuppressWarnings("CheckReturnValue")
+  private ProcessingEnvironmentCompilerOptions checkValid() {
+    for (KeyOnlyOption keyOnlyOption : KeyOnlyOption.values()) {
+      isEnabled(keyOnlyOption);
+    }
+    for (Feature feature : Feature.values()) {
+      parseOption(feature);
+    }
+    for (Validation validation : Validation.values()) {
+      parseOption(validation);
+    }
+    noLongerRecognized(EXPERIMENTAL_ANDROID_MODE);
+    noLongerRecognized(FLOATING_BINDS_METHODS);
+    return this;
+  }
+
+  private void noLongerRecognized(CommandLineOption commandLineOption) {
+    if (processingEnvironment.getOptions().containsKey(commandLineOption.toString())) {
+      processingEnvironment
+          .getMessager()
+          .printMessage(
+              Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
+    }
+  }
+
+  private interface CommandLineOption {
+    /** The key of the option (appears after "-A"). */
+    @Override
+    String toString();
+
+    /**
+     * Returns all aliases besides {@link #toString()}, such as old names for an option, in order of
+     * precedence.
+     */
+    default ImmutableList<String> aliases() {
+      return ImmutableList.of();
+    }
+
+    /** All the command-line names for this option, in order of precedence. */
+    default Stream<String> allNames() {
+      return concat(Stream.of(toString()), aliases().stream());
+    }
+  }
+
+  /** An option that can be set on the command line. */
+  private interface EnumOption<E extends Enum<E>> extends CommandLineOption {
+    /** The default value for this option. */
+    E defaultValue();
+
+    /** The valid values for this option. */
+    Set<E> validValues();
+  }
+
+  enum KeyOnlyOption implements CommandLineOption {
+    HEADER_COMPILATION {
+      @Override
+      public String toString() {
+        return "experimental_turbine_hjar";
+      }
+    },
+
+    USE_GRADLE_INCREMENTAL_PROCESSING {
+      @Override
+      public String toString() {
+        return "dagger.gradle.incremental";
+      }
+    },
+  }
+
+  /**
+   * A feature that can be enabled or disabled on the command line by setting {@code -Akey=ENABLED}
+   * or {@code -Akey=DISABLED}.
+   */
+  enum Feature implements EnumOption<FeatureStatus> {
+    FAST_INIT,
+
+    EXPERIMENTAL_ANDROID_MODE,
+
+    FORMAT_GENERATED_SOURCE,
+
+    WRITE_PRODUCER_NAME_IN_TOKEN,
+
+    WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
+
+    IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
+
+    EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
+
+    FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS,
+
+    EMIT_MODIFIABLE_METADATA_ANNOTATIONS(ENABLED),
+
+    FLOATING_BINDS_METHODS,
+    ;
+
+    final FeatureStatus defaultValue;
+
+    Feature() {
+      this(DISABLED);
+    }
+
+    Feature(FeatureStatus defaultValue) {
+      this.defaultValue = defaultValue;
+    }
+
+    @Override
+    public FeatureStatus defaultValue() {
+      return defaultValue;
+    }
+
+    @Override
+    public Set<FeatureStatus> validValues() {
+      return EnumSet.allOf(FeatureStatus.class);
+    }
+
+    @Override
+    public String toString() {
+      return optionName(this);
+    }
+  }
+
+  /** The diagnostic kind or validation type for a kind of validation. */
+  enum Validation implements EnumOption<ValidationType> {
+    DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(),
+
+    NULLABLE_VALIDATION(ERROR, WARNING),
+
+    PRIVATE_MEMBER_VALIDATION(ERROR, WARNING),
+
+    STATIC_MEMBER_VALIDATION(ERROR, WARNING),
+
+    /** Whether to validate full binding graphs for components, subcomponents, and modules. */
+    FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING) {
+      @Override
+      public ImmutableList<String> aliases() {
+        return ImmutableList.of("dagger.moduleBindingValidation");
+      }
+    },
+
+    /**
+     * How to report conflicting scoped bindings when validating partial binding graphs associated
+     * with modules.
+     */
+    MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(ERROR, WARNING),
+
+    /**
+     * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
+     * constructor used in an ancestor component.
+     */
+    EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(WARNING, ERROR, NONE),
+    ;
+
+    final ValidationType defaultType;
+    final ImmutableSet<ValidationType> validTypes;
+
+    Validation() {
+      this(ERROR, WARNING, NONE);
+    }
+
+    Validation(ValidationType defaultType, ValidationType... moreValidTypes) {
+      this.defaultType = defaultType;
+      this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
+    }
+
+    @Override
+    public ValidationType defaultValue() {
+      return defaultType;
+    }
+
+    @Override
+    public Set<ValidationType> validValues() {
+      return validTypes;
+    }
+
+    @Override
+    public String toString() {
+      return optionName(this);
+    }
+  }
+
+  private static String optionName(Enum<? extends EnumOption<?>> option) {
+    return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, option.name());
+  }
+
+  /** The supported command-line options. */
+  static ImmutableSet<String> supportedOptions() {
+    // need explicit type parameter to avoid a runtime stream error
+    return Stream.<CommandLineOption[]>of(
+            KeyOnlyOption.values(), Feature.values(), Validation.values())
+        .flatMap(Arrays::stream)
+        .flatMap(CommandLineOption::allNames)
+        .collect(toImmutableSet());
+  }
+
+  /**
+   * Returns the value for the option as set on the command line by any name, or the default value
+   * if not set.
+   *
+   * <p>If more than one name is used to set the value, but all names specify the same value,
+   * reports a warning and returns that value.
+   *
+   * <p>If more than one name is used to set the value, and not all names specify the same value,
+   * reports an error and returns the default value.
+   */
+  private <T extends Enum<T>> T parseOption(EnumOption<T> option) {
+    @SuppressWarnings("unchecked") // we only put covariant values into the map
+    T value = (T) enumOptions.computeIfAbsent(option, this::parseOptionUncached);
+    return value;
+  }
+
+  private <T extends Enum<T>> T parseOptionUncached(EnumOption<T> option) {
+    ImmutableMap<String, T> values = parseOptionWithAllNames(option);
+
+    // If no value is specified, return the default value.
+    if (values.isEmpty()) {
+      return option.defaultValue();
+    }
+
+    // If all names have the same value, return that.
+    if (values.asMultimap().inverse().keySet().size() == 1) {
+      // Warn if an option was set with more than one name. That would be an error if the values
+      // differed.
+      if (values.size() > 1) {
+        reportUseOfDifferentNamesForOption(Diagnostic.Kind.WARNING, option, values.keySet());
+      }
+      return values.values().asList().get(0);
+    }
+
+    // If different names have different values, report an error and return the default
+    // value.
+    reportUseOfDifferentNamesForOption(Diagnostic.Kind.ERROR, option, values.keySet());
+    return option.defaultValue();
+  }
+
+  private void reportUseOfDifferentNamesForOption(
+      Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames) {
+    processingEnvironment
+        .getMessager()
+        .printMessage(
+            diagnosticKind,
+            String.format(
+                "Only one of the equivalent options (%s) should be used; prefer -A%s",
+                usedNames.stream().map(name -> "-A" + name).collect(joining(", ")), option));
+  }
+
+  private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNames(
+      EnumOption<T> option) {
+    @SuppressWarnings("unchecked") // map is covariant
+    ImmutableMap<String, T> aliasValues =
+        (ImmutableMap<String, T>)
+            allCommandLineOptions.computeIfAbsent(option, this::parseOptionWithAllNamesUncached);
+    return aliasValues;
+  }
+
+  private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNamesUncached(
+      EnumOption<T> option) {
+    ImmutableMap.Builder<String, T> values = ImmutableMap.builder();
+    getUsedNames(option)
+        .forEach(
+            name -> parseOptionWithName(option, name).ifPresent(value -> values.put(name, value)));
+    return values.build();
+  }
+
+  private <T extends Enum<T>> Optional<T> parseOptionWithName(EnumOption<T> option, String key) {
+    checkArgument(processingEnvironment.getOptions().containsKey(key), "key %s not found", key);
+    String stringValue = processingEnvironment.getOptions().get(key);
+    if (stringValue == null) {
+      processingEnvironment
+          .getMessager()
+          .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
+    } else {
+      try {
+        T value =
+            Enum.valueOf(option.defaultValue().getDeclaringClass(), Ascii.toUpperCase(stringValue));
+        if (option.validValues().contains(value)) {
+          return Optional.of(value);
+        }
+      } catch (IllegalArgumentException e) {
+        // handled below
+      }
+      processingEnvironment
+          .getMessager()
+          .printMessage(
+              Diagnostic.Kind.ERROR,
+              String.format(
+                  "Processor option -A%s may only have the values %s "
+                      + "(case insensitive), found: %s",
+                  key, option.validValues(), stringValue));
+    }
+    return Optional.empty();
+  }
+
+  private Stream<String> getUsedNames(CommandLineOption option) {
+    return option.allNames().filter(name -> processingEnvironment.getOptions().containsKey(name));
+  }
+}
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
index b5cbc81..1730574 100644
--- a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
@@ -22,6 +22,7 @@
 import dagger.Module;
 import dagger.Provides;
 import dagger.Reusable;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Map;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
@@ -78,7 +79,7 @@
   @Provides
   @Reusable // to avoid parsing options more than once
   CompilerOptions compilerOptions() {
-    return CompilerOptions.create(processingEnvironment);
+    return ProcessingEnvironmentCompilerOptions.create(processingEnvironment);
   }
 
   @Provides
diff --git a/java/dagger/internal/codegen/ProducerEntryPointView.java b/java/dagger/internal/codegen/ProducerEntryPointView.java
index 8cb41ff..87b5a4a 100644
--- a/java/dagger/internal/codegen/ProducerEntryPointView.java
+++ b/java/dagger/internal/codegen/ProducerEntryPointView.java
@@ -23,6 +23,8 @@
 import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.RequestKind;
 import dagger.producers.Producer;
 import dagger.producers.internal.CancellationListener;
diff --git a/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
index e80c1d6..3f2bef4 100644
--- a/java/dagger/internal/codegen/ProducerFactoryGenerator.java
+++ b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
@@ -22,22 +22,22 @@
 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.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
 import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.TypeNames.FUTURES;
-import static dagger.internal.codegen.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.TypeNames.PRODUCER_TOKEN;
-import static dagger.internal.codegen.TypeNames.VOID_CLASS;
-import static dagger.internal.codegen.TypeNames.listOf;
-import static dagger.internal.codegen.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.TypeNames.producedOf;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.FUTURES;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_TOKEN;
+import static dagger.internal.codegen.javapoet.TypeNames.VOID_CLASS;
+import static dagger.internal.codegen.javapoet.TypeNames.listOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
 import static java.util.stream.Collectors.joining;
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
@@ -57,6 +57,9 @@
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -327,6 +330,11 @@
       return false;
     }
 
+    CodeBlock frameworkTypeUsageStatement(DependencyRequest dependency) {
+      return SourceFiles.frameworkTypeUsageStatement(
+          CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind());
+    }
+
     static FutureTransform create(
         ImmutableMap<Key, FieldSpec> fields,
         ProductionBinding binding,
@@ -364,13 +372,9 @@
 
     @Override
     ImmutableList<CodeBlock> parameterCodeBlocks() {
-      ImmutableList.Builder<CodeBlock> parameterCodeBlocks = ImmutableList.builder();
-      for (DependencyRequest dependency : binding.explicitDependencies()) {
-        parameterCodeBlocks.add(
-            frameworkTypeUsageStatement(
-                CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
-      }
-      return parameterCodeBlocks.build();
+      return binding.explicitDependencies().stream()
+          .map(this::frameworkTypeUsageStatement)
+          .collect(toImmutableList());
     }
   }
 
@@ -413,10 +417,7 @@
         if (dependency == asyncDependency) {
           parameterCodeBlocks.add(CodeBlock.of("$L", applyArgName()));
         } else {
-          parameterCodeBlocks.add(
-              // TODO(ronshapiro) extract this into a method shared by FutureTransform subclasses
-              frameworkTypeUsageStatement(
-                  CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
+          parameterCodeBlocks.add(frameworkTypeUsageStatement(dependency));
         }
       }
       return parameterCodeBlocks.build();
@@ -458,7 +459,19 @@
 
     @Override
     ImmutableList<CodeBlock> parameterCodeBlocks() {
-      return getParameterCodeBlocks(binding, fields, applyArgName());
+      int argIndex = 0;
+      ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
+      for (DependencyRequest dependency : binding.explicitDependencies()) {
+        if (isAsyncDependency(dependency)) {
+          codeBlocks.add(
+              CodeBlock.of(
+                  "($T) $L.get($L)", asyncDependencyType(dependency), applyArgName(), argIndex));
+          argIndex++;
+        } else {
+          codeBlocks.add(frameworkTypeUsageStatement(dependency));
+        }
+      }
+      return codeBlocks.build();
     }
 
     @Override
@@ -489,25 +502,6 @@
     }
   }
 
-  private static ImmutableList<CodeBlock> getParameterCodeBlocks(
-      ProductionBinding binding, ImmutableMap<Key, FieldSpec> fields, String listArgName) {
-    int argIndex = 0;
-    ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
-    for (DependencyRequest dependency : binding.explicitDependencies()) {
-      if (isAsyncDependency(dependency)) {
-        codeBlocks.add(
-            CodeBlock.of(
-                "($T) $L.get($L)", asyncDependencyType(dependency), listArgName, argIndex));
-        argIndex++;
-      } else {
-        codeBlocks.add(
-            frameworkTypeUsageStatement(
-                CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
-      }
-    }
-    return codeBlocks.build();
-  }
-
   /**
    * Creates a code block for the invocation of the producer method from the module, which should be
    * used entirely within a method body.
diff --git a/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
index 5ad9236..aca9756 100644
--- a/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
@@ -22,6 +22,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.model.RequestKind;
 import dagger.producers.Producer;
 import java.util.Optional;
diff --git a/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java b/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
index 5c7dd01..18818d5 100644
--- a/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
@@ -20,6 +20,9 @@
 
 import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 
 /** Binding expression for producer node instances. */
diff --git a/java/dagger/internal/codegen/ProducesMethodValidator.java b/java/dagger/internal/codegen/ProducesMethodValidator.java
index 4003706..bf45948 100644
--- a/java/dagger/internal/codegen/ProducesMethodValidator.java
+++ b/java/dagger/internal/codegen/ProducesMethodValidator.java
@@ -17,13 +17,15 @@
 package dagger.internal.codegen;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsScoping.NO_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.EXCEPTION;
 
 import com.google.auto.common.MoreTypes;
 import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.multibindings.ElementsIntoSet;
 import dagger.producers.ProducerModule;
 import dagger.producers.Produces;
@@ -34,9 +36,7 @@
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeMirror;
 
-/**
- * A validator for {@link Produces} methods.
- */
+/** A validator for {@link Produces} methods. */
 final class ProducesMethodValidator extends BindingMethodValidator {
 
   @Inject
@@ -57,70 +57,77 @@
   }
 
   @Override
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    super.checkMethod(builder);
-    checkNullable(builder);
-  }
-
-  /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
-  // TODO(beder): Properly handle nullable with producer methods.
-  private void checkNullable(ValidationReport.Builder<ExecutableElement> builder) {
-    if (ConfigurationAnnotations.getNullableType(builder.getSubject()).isPresent()) {
-      builder.addWarning("@Nullable on @Produces methods does not do anything");
-    }
+  protected String elementsIntoSetNotASetMessage() {
+    return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
   }
 
   @Override
-  protected String badReturnTypeMessage() {
+  protected String badTypeMessage() {
     return "@Produces methods can return only a primitive, an array, a type variable, "
         + "a declared type, or a ListenableFuture of one of those types";
   }
 
-  /**
-   * {@inheritDoc}
-   *
-   * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
-   */
   @Override
-  protected void checkKeyType(
-      ValidationReport.Builder<ExecutableElement> reportBuilder, TypeMirror keyType) {
-    Optional<TypeMirror> typeToCheck = unwrapListenableFuture(reportBuilder, keyType);
-    if (typeToCheck.isPresent()) {
-      super.checkKeyType(reportBuilder, typeToCheck.get());
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
+  }
+
+  private class Validator extends MethodValidator {
+    Validator(ExecutableElement element) {
+      super(element);
     }
-  }
 
-  /**
-   * {@inheritDoc}
-   *
-   * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return a
-   * {@link ListenableFuture} of a {@link Set} as well.
-   */
-  @Override
-  protected void checkSetValuesType(ValidationReport.Builder<ExecutableElement> builder) {
-    Optional<TypeMirror> typeToCheck =
-        unwrapListenableFuture(builder, builder.getSubject().getReturnType());
-    if (typeToCheck.isPresent()) {
-      checkSetValuesType(builder, typeToCheck.get());
+    @Override
+    protected void checkAdditionalMethodProperties() {
+      checkNullable();
     }
-  }
 
-  @Override
-  protected String badSetValuesTypeMessage() {
-    return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
-  }
-
-  private static Optional<TypeMirror> unwrapListenableFuture(
-      ValidationReport.Builder<ExecutableElement> reportBuilder, TypeMirror type) {
-    if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
-      DeclaredType declaredType = MoreTypes.asDeclared(type);
-      if (declaredType.getTypeArguments().isEmpty()) {
-        reportBuilder.addError("@Produces methods cannot return a raw ListenableFuture");
-        return Optional.empty();
-      } else {
-        return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
+    /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
+    // TODO(beder): Properly handle nullable with producer methods.
+    private void checkNullable() {
+      if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
+        report.addWarning("@Nullable on @Produces methods does not do anything");
       }
     }
-    return Optional.of(type);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
+     */
+    @Override
+    protected void checkKeyType(TypeMirror keyType) {
+      Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType);
+      if (typeToCheck.isPresent()) {
+        super.checkKeyType(typeToCheck.get());
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
+     * a {@link ListenableFuture} of a {@link Set} as well.
+     */
+    @Override
+    protected void checkSetValuesType() {
+      Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType());
+      if (typeToCheck.isPresent()) {
+        checkSetValuesType(typeToCheck.get());
+      }
+    }
+
+    private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
+      if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
+        DeclaredType declaredType = MoreTypes.asDeclared(type);
+        if (declaredType.getTypeArguments().isEmpty()) {
+          report.addError("@Produces methods cannot return a raw ListenableFuture");
+          return Optional.empty();
+        } else {
+          return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
+        }
+      }
+      return Optional.of(type);
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
index d9c2645..a22f21c 100644
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -17,7 +17,7 @@
 package dagger.internal.codegen;
 
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerTypes.isFutureType;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
 
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
@@ -66,7 +66,7 @@
     static ProductionKind fromProducesMethod(ExecutableElement producesMethod) {
       if (isFutureType(producesMethod.getReturnType())) {
         return FUTURE;
-      } else if (ContributionType.fromBindingMethod(producesMethod)
+      } else if (ContributionType.fromBindingElement(producesMethod)
               .equals(ContributionType.SET_VALUES)
           && isFutureType(SetType.from(producesMethod.getReturnType()).elementType())) {
         return SET_OF_FUTURE;
diff --git a/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java b/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
index aa8c815..60166de 100644
--- a/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen;
 
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+
 /** Binding expression for provider instances. */
 final class ProviderInstanceBindingExpression extends FrameworkInstanceBindingExpression {
 
diff --git a/java/dagger/internal/codegen/ProvidesMethodValidator.java b/java/dagger/internal/codegen/ProvidesMethodValidator.java
index db24e66..01e71ae 100644
--- a/java/dagger/internal/codegen/ProvidesMethodValidator.java
+++ b/java/dagger/internal/codegen/ProvidesMethodValidator.java
@@ -16,22 +16,22 @@
 
 package dagger.internal.codegen;
 
+import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingMethodValidator.AllowsScoping.ALLOWS_SCOPING;
 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
 
 import com.google.common.collect.ImmutableSet;
 import dagger.Module;
 import dagger.Provides;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.producers.ProducerModule;
 import javax.inject.Inject;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.VariableElement;
 
-/**
- * A validator for {@link Provides} methods.
- */
+/** A validator for {@link Provides} methods. */
 final class ProvidesMethodValidator extends BindingMethodValidator {
 
   private final DependencyRequestValidator dependencyRequestValidator;
@@ -55,15 +55,24 @@
   }
 
   @Override
-  protected void checkMethod(ValidationReport.Builder<ExecutableElement> builder) {
-    super.checkMethod(builder);
+  protected ElementValidator elementValidator(ExecutableElement element) {
+    return new Validator(element);
   }
 
-  /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
-  @Override
-  protected void checkParameter(
-      ValidationReport.Builder<ExecutableElement> builder, VariableElement parameter) {
-    super.checkParameter(builder, parameter);
-    dependencyRequestValidator.checkNotProducer(builder, parameter);
+  private class Validator extends MethodValidator {
+    Validator(ExecutableElement element) {
+      super(element);
+    }
+
+    @Override
+    protected void checkAdditionalMethodProperties() {
+    }
+
+    /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
+    @Override
+    protected void checkParameter(VariableElement parameter) {
+      super.checkParameter(parameter);
+      dependencyRequestValidator.checkNotProducer(report, parameter);
+    }
   }
 }
diff --git a/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
index f76c49c..6eb92ac 100644
--- a/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
@@ -20,6 +20,8 @@
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.MissingBindingFactory;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.producers.internal.MissingBindingProducer;
 import java.util.Optional;
 
diff --git a/java/dagger/internal/codegen/RequestKinds.java b/java/dagger/internal/codegen/RequestKinds.java
index 3c6b2f0..aa17f5e 100644
--- a/java/dagger/internal/codegen/RequestKinds.java
+++ b/java/dagger/internal/codegen/RequestKinds.java
@@ -21,12 +21,12 @@
 import static com.google.auto.common.MoreTypes.isTypeOf;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerTypes.checkTypePresent;
-import static dagger.internal.codegen.TypeNames.lazyOf;
-import static dagger.internal.codegen.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.TypeNames.producedOf;
-import static dagger.internal.codegen.TypeNames.producerOf;
-import static dagger.internal.codegen.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent;
 import static dagger.model.RequestKind.INSTANCE;
 import static dagger.model.RequestKind.LAZY;
 import static dagger.model.RequestKind.PRODUCED;
@@ -39,6 +39,7 @@
 import com.google.common.util.concurrent.ListenableFuture;
 import com.squareup.javapoet.TypeName;
 import dagger.Lazy;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.RequestKind;
 import dagger.producers.Produced;
 import dagger.producers.Producer;
diff --git a/java/dagger/internal/codegen/Scopes.java b/java/dagger/internal/codegen/Scopes.java
index e188f9b..252f712 100644
--- a/java/dagger/internal/codegen/Scopes.java
+++ b/java/dagger/internal/codegen/Scopes.java
@@ -22,6 +22,7 @@
 
 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;
diff --git a/java/dagger/internal/codegen/SetBindingExpression.java b/java/dagger/internal/codegen/SetBindingExpression.java
index e6a3a68..5efb85f 100644
--- a/java/dagger/internal/codegen/SetBindingExpression.java
+++ b/java/dagger/internal/codegen/SetBindingExpression.java
@@ -17,15 +17,18 @@
 package dagger.internal.codegen;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.SetBuilder;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import java.util.Collections;
 import java.util.Optional;
diff --git a/java/dagger/internal/codegen/SetFactoryCreationExpression.java b/java/dagger/internal/codegen/SetFactoryCreationExpression.java
index d69f4a2..9712091 100644
--- a/java/dagger/internal/codegen/SetFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/SetFactoryCreationExpression.java
@@ -20,6 +20,7 @@
 import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.model.DependencyRequest;
 import dagger.producers.Produced;
 import java.util.Optional;
 
@@ -69,9 +70,9 @@
       setProviders++;
     }
 
-    for (FrameworkDependency frameworkDependency : frameworkDependenciesToImplement()) {
+    for (DependencyRequest dependency : dependenciesToImplement()) {
       ContributionType contributionType =
-          graph.contributionBindings().get(frameworkDependency.key()).contributionType();
+          graph.contributionBindings().get(dependency.key()).contributionType();
       String methodNamePrefix;
       switch (contributionType) {
         case SET:
@@ -83,14 +84,14 @@
           methodNamePrefix = "addCollection";
           break;
         default:
-          throw new AssertionError(frameworkDependency + " is not a set multibinding");
+          throw new AssertionError(dependency + " is not a set multibinding");
       }
 
       builderMethodCalls.add(
           ".$N$N($L)",
           methodNamePrefix,
           methodNameSuffix,
-          multibindingDependencyExpression(frameworkDependency));
+          multibindingDependencyExpression(dependency));
     }
     builder.add("builder($L, $L)", individualProviders, setProviders);
     builder.add(builderMethodCalls.build());
diff --git a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
index 3bd908f..805ac7b 100644
--- a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
@@ -18,10 +18,10 @@
 
 import static com.google.auto.common.MoreElements.asExecutable;
 import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
 import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
-import static dagger.internal.codegen.TypeNames.rawTypeName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableMap;
@@ -31,9 +31,13 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.DependencyRequest;
 import java.util.Optional;
 import java.util.function.Function;
+import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.type.DeclaredType;
@@ -51,6 +55,7 @@
   private final ComponentRequirementExpressions componentRequirementExpressions;
   private final DaggerTypes types;
   private final DaggerElements elements;
+  private final SourceVersion sourceVersion;
 
   SimpleMethodBindingExpression(
       ResolvedBindings resolvedBindings,
@@ -59,7 +64,8 @@
       MembersInjectionMethods membersInjectionMethods,
       ComponentRequirementExpressions componentRequirementExpressions,
       DaggerTypes types,
-      DaggerElements elements) {
+      DaggerElements elements,
+      SourceVersion sourceVersion) {
     super(resolvedBindings);
     this.compilerOptions = compilerOptions;
     this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
@@ -72,6 +78,7 @@
     this.componentRequirementExpressions = componentRequirementExpressions;
     this.types = types;
     this.elements = elements;
+    this.sourceVersion = sourceVersion;
   }
 
   @Override
@@ -151,14 +158,15 @@
     if (provisionBinding.injectionSites().isEmpty()) {
       return Expression.create(simpleMethodReturnType(), instance);
     }
-    // Java 7 type inference can't figure out that instance in
-    // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
-    // Parameterized<Object>
-    if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
-      TypeName keyType = TypeName.get(provisionBinding.key().type());
-      instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
+    if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+      // Java 7 type inference can't figure out that instance in
+      // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
+      // Parameterized<Object>
+      if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
+        TypeName keyType = TypeName.get(provisionBinding.key().type());
+        instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
+      }
     }
-
     MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key());
     TypeMirror returnType =
         membersInjectionMethod.returnType.equals(TypeName.OBJECT)
diff --git a/java/dagger/internal/codegen/SourceFileGenerator.java b/java/dagger/internal/codegen/SourceFileGenerator.java
index 747acf6..7dddc2f 100644
--- a/java/dagger/internal/codegen/SourceFileGenerator.java
+++ b/java/dagger/internal/codegen/SourceFileGenerator.java
@@ -24,6 +24,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.JavaFile;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.Messager;
@@ -38,7 +39,7 @@
  * @param <T> The input type from which source is to be generated.
  */
 abstract class SourceFileGenerator<T> {
-  private static final String GENERATED_COMMENTS = "https://google.github.io/dagger";
+  private static final String GENERATED_COMMENTS = "https://dagger.dev";
 
   private final Filer filer;
   private final DaggerElements elements;
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index d7ce535..fd93d0d 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -24,16 +24,16 @@
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.Optionals.optionalComparator;
-import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
-import static dagger.internal.codegen.TypeNames.MAP_FACTORY;
-import static dagger.internal.codegen.TypeNames.MAP_OF_PRODUCED_PRODUCER;
-import static dagger.internal.codegen.TypeNames.MAP_OF_PRODUCER_PRODUCER;
-import static dagger.internal.codegen.TypeNames.MAP_PRODUCER;
-import static dagger.internal.codegen.TypeNames.MAP_PROVIDER_FACTORY;
-import static dagger.internal.codegen.TypeNames.PROVIDER_OF_LAZY;
-import static dagger.internal.codegen.TypeNames.SET_FACTORY;
-import static dagger.internal.codegen.TypeNames.SET_OF_PRODUCED_PRODUCER;
-import static dagger.internal.codegen.TypeNames.SET_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCER_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PROVIDER_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER_OF_LAZY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_PRODUCER;
 import static dagger.model.BindingKind.INJECTION;
 import static dagger.model.BindingKind.MULTIBOUND_MAP;
 import static dagger.model.BindingKind.MULTIBOUND_SET;
@@ -201,10 +201,8 @@
       case MEMBERS_INJECTION:
         return membersInjectorNameForType(
             ((MembersInjectionBinding) binding).membersInjectedType());
-
-      default:
-        throw new AssertionError();
     }
+    throw new AssertionError();
   }
 
   /**
@@ -247,10 +245,6 @@
     return siblingClassName(componentElement, "_MonitoringModule");
   }
 
-  static ClassName generatedProductionExecutorModuleName(TypeElement componentElement) {
-    return siblingClassName(componentElement, "_ProductionExecutorModule");
-  }
-
   // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
   // which could use this.
   private static ClassName siblingClassName(TypeElement typeElement, String suffix) {
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
index 782feb8..c97024e 100644
--- a/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
+++ b/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
@@ -17,6 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.DaggerStreams.presentValues;
+import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.collect.ImmutableSet;
@@ -26,23 +28,27 @@
 /** An implementation of {@link SubcomponentCreatorBindingEdge}. */
 final class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge {
 
-  private final ImmutableSet<TypeElement> declaringModules;
+  private final ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations;
 
-  SubcomponentCreatorBindingEdgeImpl(Iterable<TypeElement> declaringModules) {
-    this.declaringModules = ImmutableSet.copyOf(declaringModules);
+  SubcomponentCreatorBindingEdgeImpl(
+      ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+    this.subcomponentDeclarations = subcomponentDeclarations;
   }
 
   @Override
   public ImmutableSet<TypeElement> declaringModules() {
-    return declaringModules;
+    return subcomponentDeclarations.stream()
+        .map(SubcomponentDeclaration::contributingModule)
+        .flatMap(presentValues())
+        .collect(toImmutableSet());
   }
 
   @Override
   public String toString() {
     return "subcomponent declared by "
-        + (declaringModules.size() == 1
-            ? getOnlyElement(declaringModules).getQualifiedName()
-            : declaringModules.stream()
+        + (subcomponentDeclarations.size() == 1
+            ? getOnlyElement(declaringModules()).getQualifiedName()
+            : declaringModules().stream()
                 .map(TypeElement::getQualifiedName)
                 .collect(joining(", ", "{", "}")));
   }
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
index 510e8c4..b415d3f 100644
--- a/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
+++ b/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.Expression;
 import javax.lang.model.type.TypeMirror;
 
 /** A binding expression for a subcomponent creator that just invokes the constructor. */
diff --git a/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
index 7d95ee4..0c6a006 100644
--- a/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
+++ b/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
@@ -29,6 +29,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.BindingGraph;
 import dagger.model.BindingGraph.ChildFactoryMethodEdge;
 import dagger.model.BindingGraph.ComponentNode;
diff --git a/java/dagger/internal/codegen/SwitchingProviders.java b/java/dagger/internal/codegen/SwitchingProviders.java
index f1fe857..29633d9 100644
--- a/java/dagger/internal/codegen/SwitchingProviders.java
+++ b/java/dagger/internal/codegen/SwitchingProviders.java
@@ -21,10 +21,10 @@
 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.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.AnnotationSpecs.suppressWarnings;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
 
@@ -35,6 +35,9 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeSpec;
 import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import dagger.model.Key;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
diff --git a/java/dagger/internal/codegen/TypeNames.java b/java/dagger/internal/codegen/TypeNames.java
deleted file mode 100644
index 2b590d4..0000000
--- a/java/dagger/internal/codegen/TypeNames.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.util.concurrent.AsyncFunction;
-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.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.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.Optional;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import javax.inject.Provider;
-
-/**
- * Common names and convenience methods for JavaPoet {@link TypeName} usage.
- */
-final class TypeNames {
-
-  static final ClassName ABSTRACT_PRODUCER = ClassName.get(AbstractProducer.class);
-  static final ClassName ASYNC_FUNCTION = ClassName.get(AsyncFunction.class);
-  static final ClassName DEPENDENCY_METHOD_PRODUCER = ClassName.get(DependencyMethodProducer.class);
-  static final ClassName DOUBLE_CHECK = ClassName.get(DoubleCheck.class);
-  static final ClassName EXECUTOR = ClassName.get(Executor.class);
-  static final ClassName FACTORY = ClassName.get(Factory.class);
-  static final ClassName FUTURES = ClassName.get(Futures.class);
-  static final ClassName INSTANCE_FACTORY = ClassName.get(InstanceFactory.class);
-  static final ClassName LAZY = ClassName.get(Lazy.class);
-  static final ClassName LIST = ClassName.get(List.class);
-  static final ClassName LISTENABLE_FUTURE = ClassName.get(ListenableFuture.class);
-  static final ClassName MAP_FACTORY = ClassName.get(MapFactory.class);
-  static final ClassName MAP_OF_PRODUCED_PRODUCER = ClassName.get(MapOfProducedProducer.class);
-  static final ClassName MAP_OF_PRODUCER_PRODUCER = ClassName.get(MapOfProducerProducer.class);
-  static final ClassName MAP_PRODUCER = ClassName.get(MapProducer.class);
-  static final ClassName MAP_PROVIDER_FACTORY = ClassName.get(MapProviderFactory.class);
-  static final ClassName MEMBERS_INJECTOR = ClassName.get(MembersInjector.class);
-  static final ClassName MEMBERS_INJECTORS = ClassName.get(MembersInjectors.class);
-  static final ClassName OPTIONAL = ClassName.get(Optional.class);
-  static final ClassName PRODUCER_TOKEN = ClassName.get(ProducerToken.class);
-  static final ClassName PRODUCED = ClassName.get(Produced.class);
-  static final ClassName PRODUCER = ClassName.get(Producer.class);
-  static final ClassName PRODUCERS = ClassName.get(Producers.class);
-  static final ClassName PRODUCTION_COMPONENT_MONITOR_FACTORY =
-      ClassName.get(ProductionComponentMonitor.Factory.class);
-  static final ClassName PROVIDER = ClassName.get(Provider.class);
-  static final ClassName PROVIDER_OF_LAZY = ClassName.get(ProviderOfLazy.class);
-  static final ClassName RUNNABLE = ClassName.get(Runnable.class);
-  static final ClassName SET = ClassName.get(Set.class);
-  static final ClassName SET_FACTORY = ClassName.get(SetFactory.class);
-  static final ClassName SET_OF_PRODUCED_PRODUCER = ClassName.get(SetOfProducedProducer.class);
-  static final ClassName SET_PRODUCER = ClassName.get(SetProducer.class);
-  static final ClassName SINGLE_CHECK = ClassName.get(SingleCheck.class);
-
-  /**
-   * {@link TypeName#VOID} is lowercase-v {@code void} whereas this represents the class, {@link
-   * Void}.
-   */
-  static final ClassName VOID_CLASS = ClassName.get(Void.class);
-
-  static ParameterizedTypeName abstractProducerOf(TypeName typeName) {
-    return ParameterizedTypeName.get(ABSTRACT_PRODUCER, typeName);
-  }
-
-  static ParameterizedTypeName factoryOf(TypeName factoryType) {
-    return ParameterizedTypeName.get(FACTORY, factoryType);
-  }
-
-  static ParameterizedTypeName lazyOf(TypeName typeName) {
-    return ParameterizedTypeName.get(LAZY, typeName);
-  }
-
-  static ParameterizedTypeName listOf(TypeName typeName) {
-    return ParameterizedTypeName.get(LIST, typeName);
-  }
-
-  static ParameterizedTypeName listenableFutureOf(TypeName typeName) {
-    return ParameterizedTypeName.get(LISTENABLE_FUTURE, typeName);
-  }
-
-  static ParameterizedTypeName membersInjectorOf(TypeName membersInjectorType) {
-    return ParameterizedTypeName.get(MEMBERS_INJECTOR, membersInjectorType);
-  }
-
-  static ParameterizedTypeName optionalOf(TypeName type) {
-    return ParameterizedTypeName.get(OPTIONAL, type);
-  }
-
-  static ParameterizedTypeName producedOf(TypeName typeName) {
-    return ParameterizedTypeName.get(PRODUCED, typeName);
-  }
-
-  static ParameterizedTypeName producerOf(TypeName typeName) {
-    return ParameterizedTypeName.get(PRODUCER, typeName);
-  }
-
-  static ParameterizedTypeName dependencyMethodProducerOf(TypeName typeName) {
-    return ParameterizedTypeName.get(DEPENDENCY_METHOD_PRODUCER, typeName);
-  }
-
-  static ParameterizedTypeName providerOf(TypeName typeName) {
-    return ParameterizedTypeName.get(PROVIDER, typeName);
-  }
-
-  static ParameterizedTypeName setOf(TypeName elementType) {
-    return ParameterizedTypeName.get(SET, elementType);
-  }
-
-  /**
-   * 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.
-   */
-  static TypeName rawTypeName(TypeName typeName) {
-    return (typeName instanceof ParameterizedTypeName)
-        ? ((ParameterizedTypeName) typeName).rawType
-        : typeName;
-  }
-
-  private TypeNames() {}
-}
diff --git a/java/dagger/internal/codegen/TypeProtoConverter.java b/java/dagger/internal/codegen/TypeProtoConverter.java
new file mode 100644
index 0000000..c703bd8
--- /dev/null
+++ b/java/dagger/internal/codegen/TypeProtoConverter.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.TypeProto;
+import dagger.internal.codegen.serialization.TypeProto.PrimitiveKind;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+
+/** Converts {@link TypeMirror}s to {@link TypeProto}s and vice-versa. */
+final class TypeProtoConverter {
+  // TODO(ronshapiro): if DaggerTypes and DaggerElements become public, move this file to
+  // dagger.internal.codegen.serialization
+  private final DaggerTypes types;
+  private final DaggerElements elements;
+
+  @Inject
+  TypeProtoConverter(DaggerTypes types, DaggerElements elements) {
+    this.types = types;
+    this.elements = elements;
+  }
+
+  /** Translates a {@link TypeMirror} to a proto representation. */
+  static TypeProto toProto(TypeMirror type) {
+    TypeProto.Builder builder = TypeProto.newBuilder();
+    int arrayDimensions = 0;
+    while (type.getKind().equals(TypeKind.ARRAY)) {
+      type = MoreTypes.asArray(type).getComponentType();
+      arrayDimensions++;
+    }
+    builder.setArrayDimensions(arrayDimensions);
+    if (type.getKind().isPrimitive()) {
+      builder.setPrimitiveKind(PrimitiveKind.valueOf(type.getKind().name()));
+    } else if (type.getKind().equals(TypeKind.WILDCARD)) {
+      WildcardType wildcardType = MoreTypes.asWildcard(type);
+      TypeProto.Wildcard.Builder wildcardBuilder = TypeProto.Wildcard.newBuilder();
+      if (wildcardType.getExtendsBound() != null) {
+        wildcardBuilder.setExtendsBound(toProto(wildcardType.getExtendsBound()));
+      } else if (wildcardType.getSuperBound() != null) {
+        wildcardBuilder.setSuperBound(toProto(wildcardType.getSuperBound()));
+      }
+      builder.setWildcard(wildcardBuilder);
+    } else {
+      TypeElement typeElement = MoreTypes.asTypeElement(type);
+      DeclaredType declaredType = MoreTypes.asDeclared(type);
+      TypeMirror enclosingType = declaredType.getEnclosingType();
+      if (enclosingType.getKind().equals(TypeKind.NONE)) {
+        builder.setQualifiedName(typeElement.getQualifiedName().toString());
+      } else {
+        builder
+            .setEnclosingType(toProto(enclosingType))
+            .setSimpleName(typeElement.getSimpleName().toString());
+      }
+      declaredType.getTypeArguments().stream()
+          .map(TypeProtoConverter::toProto)
+          .forEachOrdered(builder::addTypeArguments);
+    }
+    return builder.build();
+  }
+
+  /** Creates an {@link TypeMirror} from its proto representation. */
+  TypeMirror fromProto(TypeProto type) {
+    if (type.hasWildcard()) {
+      return wildcardType(type.getWildcard());
+    }
+
+    TypeMirror[] typeArguments =
+        type.getTypeArgumentsList().stream().map(this::fromProto).toArray(TypeMirror[]::new);
+    TypeMirror typeMirror;
+    if (!type.getPrimitiveKind().equals(PrimitiveKind.UNKNOWN)) {
+      typeMirror = types.getPrimitiveType(TypeKind.valueOf(type.getPrimitiveKind().name()));
+    } else if (type.hasEnclosingType()) {
+      DeclaredType enclosingType = MoreTypes.asDeclared(fromProto(type.getEnclosingType()));
+      TypeElement typeElement =
+          typesIn(enclosingType.asElement().getEnclosedElements()).stream()
+              .filter(inner -> inner.getSimpleName().contentEquals(type.getSimpleName()))
+              .findFirst()
+              .get();
+      typeMirror = types.getDeclaredType(enclosingType, typeElement, typeArguments);
+    } else {
+      typeMirror =
+          types.getDeclaredType(elements.getTypeElement(type.getQualifiedName()), typeArguments);
+    }
+    for (int i = 0; i < type.getArrayDimensions(); i++) {
+      typeMirror = types.getArrayType(typeMirror);
+    }
+    return typeMirror;
+  }
+
+  private TypeMirror wildcardType(TypeProto.Wildcard wildcard) {
+    if (wildcard.hasExtendsBound()) {
+      return types.getWildcardType(fromProto(wildcard.getExtendsBound()), null);
+    } else if (wildcard.hasSuperBound()) {
+      return types.getWildcardType(null, fromProto(wildcard.getSuperBound()));
+    } else {
+      return types.getWildcardType(null, null);
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java b/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
index 9f7e40c..2b7b02c 100644
--- a/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
+++ b/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.Set;
 import javax.annotation.processing.Filer;
 import javax.inject.Inject;
diff --git a/java/dagger/internal/codegen/Util.java b/java/dagger/internal/codegen/Util.java
index 7a56944..1869b7c 100644
--- a/java/dagger/internal/codegen/Util.java
+++ b/java/dagger/internal/codegen/Util.java
@@ -77,10 +77,9 @@
       case ANONYMOUS:
       case LOCAL:
         return true;
-      default:
-        throw new AssertionError(
-            "TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
     }
+    throw new AssertionError(
+        "TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ValidationReport.java b/java/dagger/internal/codegen/ValidationReport.java
index 0a3765a..d7c3252 100644
--- a/java/dagger/internal/codegen/ValidationReport.java
+++ b/java/dagger/internal/codegen/ValidationReport.java
@@ -16,8 +16,8 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.DaggerElements.elementToString;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.ElementFormatter.elementToString;
 import static javax.tools.Diagnostic.Kind.ERROR;
 import static javax.tools.Diagnostic.Kind.NOTE;
 import static javax.tools.Diagnostic.Kind.WARNING;
diff --git a/java/dagger/internal/codegen/AnnotationSpecs.java b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
similarity index 77%
rename from java/dagger/internal/codegen/AnnotationSpecs.java
rename to java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
index 06416f9..cc0d7de 100644
--- a/java/dagger/internal/codegen/AnnotationSpecs.java
+++ b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -24,8 +24,10 @@
 import com.squareup.javapoet.AnnotationSpec;
 import java.util.Arrays;
 
-final class AnnotationSpecs {
-  enum Suppression {
+/** Static factories to create {@link AnnotationSpec}s. */
+public final class AnnotationSpecs {
+  /** Values for an {@link SuppressWarnings} annotation. */
+  public enum Suppression {
     RAWTYPES,
     UNCHECKED,
     ;
@@ -36,7 +38,8 @@
     }
   }
 
-  static AnnotationSpec suppressWarnings(Suppression first, Suppression... rest) {
+  /** Creates an {@link AnnotationSpec} for {@link SuppressWarnings}. */
+  public static AnnotationSpec suppressWarnings(Suppression first, Suppression... rest) {
     checkNotNull(first);
     Arrays.stream(rest).forEach(Preconditions::checkNotNull);
     AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class);
diff --git a/java/dagger/internal/codegen/javapoet/BUILD b/java/dagger/internal/codegen/javapoet/BUILD
new file mode 100644
index 0000000..f829d49
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/BUILD
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+#   JavaPoet extensions for use in Dagger
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "javapoet",
+    srcs = glob(["*.java"]),
+    plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+    tags = ["maven:merged"],
+    deps = [
+        "//java/dagger:core",
+        "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/producers",
+        "@google_bazel_common//third_party/java/auto:common",
+        "@google_bazel_common//third_party/java/error_prone:annotations",
+        "@google_bazel_common//third_party/java/guava",
+        "@google_bazel_common//third_party/java/javapoet",
+    ],
+)
diff --git a/java/dagger/internal/codegen/CodeBlocks.java b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
similarity index 79%
rename from java/dagger/internal/codegen/CodeBlocks.java
rename to java/dagger/internal/codegen/javapoet/CodeBlocks.java
index e551aeb..3e9f75d 100644
--- a/java/dagger/internal/codegen/CodeBlocks.java
+++ b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
-import static dagger.internal.codegen.TypeNames.providerOf;
-import static dagger.internal.codegen.TypeNames.rawTypeName;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
 import static java.util.stream.StreamSupport.stream;
 import static javax.lang.model.element.Modifier.PUBLIC;
 
@@ -35,24 +35,25 @@
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeMirror;
 
-final class CodeBlocks {
+/** Convenience methods for creating {@link CodeBlock}s. */
+public final class CodeBlocks {
   /**
    * Joins {@link CodeBlock} instances in a manner suitable for use as method parameters (or
    * arguments).
    */
-  static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() {
+  public static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() {
     // TODO(ronshapiro,jakew): consider adding zero-width spaces to help line breaking when the
     // formatter is off. If not, inline this
     return CodeBlock.joining(", ");
   }
 
   /** Concatenates {@link CodeBlock} instances separated by newlines for readability. */
-  static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() {
+  public static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() {
     return CodeBlock.joining("\n", "", "\n");
   }
 
   /** Returns a comma-separated version of {@code codeBlocks} as one unified {@link CodeBlock}. */
-  static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) {
+  public static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) {
     return stream(codeBlocks.spliterator(), false).collect(toParametersCodeBlock());
   }
 
@@ -60,7 +61,7 @@
    * Returns a comma-separated {@link CodeBlock} using the name of every parameter in {@code
    * parameters}.
    */
-  static CodeBlock parameterNames(Iterable<ParameterSpec> parameters) {
+  public static CodeBlock parameterNames(Iterable<ParameterSpec> parameters) {
     // TODO(ronshapiro): Add DaggerStreams.stream(Iterable)
     return stream(parameters.spliterator(), false)
         .map(p -> CodeBlock.of("$N", p))
@@ -71,12 +72,12 @@
    * Returns one unified {@link CodeBlock} which joins each item in {@code codeBlocks} with a
    * newline.
    */
-  static CodeBlock concat(Iterable<CodeBlock> codeBlocks) {
+  public static CodeBlock concat(Iterable<CodeBlock> codeBlocks) {
     return stream(codeBlocks.spliterator(), false).collect(toConcatenatedCodeBlock());
   }
 
   /** Adds an annotation to a method. */
-  static void addAnnotation(MethodSpec.Builder method, DeclaredType nullableType) {
+  public static void addAnnotation(MethodSpec.Builder method, DeclaredType nullableType) {
     method.addAnnotation(ClassName.get(MoreTypes.asTypeElement(nullableType)));
   }
 
@@ -84,7 +85,7 @@
    * Returns an anonymous {@link javax.inject.Provider} class with the single {@link
    * javax.inject.Provider#get()} method that returns the given {@code expression}.
    */
-  static CodeBlock anonymousProvider(Expression expression) {
+  public static CodeBlock anonymousProvider(Expression expression) {
     // More of a precondition check that the type Provider is parameterized with is a DeclaredType
     DeclaredType type = MoreTypes.asDeclared(expression.type());
     return anonymousProvider(
@@ -95,8 +96,9 @@
    * Returns an anonymous {@link javax.inject.Provider} class with the single {@link
    * javax.inject.Provider#get()} method implemented by {@code body}.
    */
-  static CodeBlock anonymousProvider(TypeName providedType, CodeBlock body) {
-    return CodeBlock.of("$L",
+  public static CodeBlock anonymousProvider(TypeName providedType, CodeBlock body) {
+    return CodeBlock.of(
+        "$L",
         anonymousClassBuilder("")
             .superclass(providerOf(providedType))
             .addMethod(
@@ -110,20 +112,20 @@
   }
 
   /** Returns {@code expression} cast to a type. */
-  static CodeBlock cast(CodeBlock expression, Class<?> castTo) {
+  public static CodeBlock cast(CodeBlock expression, Class<?> castTo) {
     return CodeBlock.of("($T) $L", castTo, expression);
   }
 
-  static CodeBlock type(TypeMirror type) {
+  public static CodeBlock type(TypeMirror type) {
     return CodeBlock.of("$T", type);
   }
 
-  static CodeBlock stringLiteral(String toWrap) {
+  public static CodeBlock stringLiteral(String toWrap) {
     return CodeBlock.of("$S", toWrap);
   }
 
   /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */
-  static CodeBlock javadocLinkTo(ExecutableElement executableElement) {
+  public static CodeBlock javadocLinkTo(ExecutableElement executableElement) {
     CodeBlock.Builder builder =
         CodeBlock.builder()
             .add(
@@ -146,9 +148,7 @@
     }
     builder.add("(");
     builder.add(
-        executableElement
-            .getParameters()
-            .stream()
+        executableElement.getParameters().stream()
             .map(parameter -> CodeBlock.of("$T", rawTypeName(TypeName.get(parameter.asType()))))
             .collect(toParametersCodeBlock()));
     return builder.add(")}").build();
diff --git a/java/dagger/internal/codegen/Expression.java b/java/dagger/internal/codegen/javapoet/Expression.java
similarity index 85%
rename from java/dagger/internal/codegen/Expression.java
rename to java/dagger/internal/codegen/javapoet/Expression.java
index b6d215c..b79c55c 100644
--- a/java/dagger/internal/codegen/Expression.java
+++ b/java/dagger/internal/codegen/javapoet/Expression.java
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import com.google.auto.common.MoreTypes;
 import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.TypeMirror;
 
 /**
@@ -34,7 +35,7 @@
  * <p>An {@code Expression} for {@code fooImplProvider.get()} would have a {@link #type()} of {@code
  * java.lang.Object} and not {@code FooImpl}.
  */
-final class Expression {
+public final class Expression {
   private final TypeMirror type;
   private final CodeBlock codeBlock;
 
@@ -44,7 +45,7 @@
   }
 
   /** Creates a new {@link Expression} with a {@link TypeMirror} and {@link CodeBlock}. */
-  static Expression create(TypeMirror type, CodeBlock expression) {
+  public static Expression create(TypeMirror type, CodeBlock expression) {
     return new Expression(type, expression);
   }
 
@@ -52,14 +53,14 @@
    * Creates a new {@link Expression} with a {@link TypeMirror}, {@linkplain CodeBlock#of(String,
    * Object[]) format, and arguments}.
    */
-  static Expression create(TypeMirror type, String format, Object... args) {
+  public static Expression create(TypeMirror type, String format, Object... args) {
     return create(type, CodeBlock.of(format, args));
   }
 
   /** 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.
-  Expression castTo(TypeMirror newType) {
+  public Expression castTo(TypeMirror newType) {
     return create(newType, "($T) $L", newType, codeBlock);
   }
 
@@ -67,19 +68,19 @@
    * Returns a new expression that {@link #castTo(TypeMirror)} casts the current expression to its
    * boxed type if this expression has a primitive type.
    */
-  Expression box(DaggerTypes types) {
+  public Expression box(DaggerTypes types) {
     return type.getKind().isPrimitive()
         ? castTo(types.boxedClass(MoreTypes.asPrimitiveType(type)).asType())
         : this;
   }
 
   /** The {@link TypeMirror type} to which the expression evaluates. */
-  TypeMirror type() {
+  public TypeMirror type() {
     return type;
   }
 
   /** The code of the expression. */
-  CodeBlock codeBlock() {
+  public CodeBlock codeBlock() {
     return codeBlock;
   }
 
diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java
new file mode 100644
index 0000000..9301dbc
--- /dev/null
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -0,0 +1,149 @@
+/*
+ * 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.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.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.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;
+
+/** 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);
+  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);
+  public static final ClassName MAP_OF_PRODUCED_PRODUCER =
+      ClassName.get(MapOfProducedProducer.class);
+  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 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);
+  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);
+
+  /**
+   * {@link TypeName#VOID} is lowercase-v {@code void} whereas this represents the class, {@link
+   * Void}.
+   */
+  public static final ClassName VOID_CLASS = ClassName.get(Void.class);
+
+  public static ParameterizedTypeName abstractProducerOf(TypeName typeName) {
+    return ParameterizedTypeName.get(ABSTRACT_PRODUCER, typeName);
+  }
+
+  public static ParameterizedTypeName factoryOf(TypeName factoryType) {
+    return ParameterizedTypeName.get(FACTORY, factoryType);
+  }
+
+  public static ParameterizedTypeName lazyOf(TypeName typeName) {
+    return ParameterizedTypeName.get(LAZY, typeName);
+  }
+
+  public static ParameterizedTypeName listOf(TypeName typeName) {
+    return ParameterizedTypeName.get(LIST, typeName);
+  }
+
+  public static ParameterizedTypeName listenableFutureOf(TypeName typeName) {
+    return ParameterizedTypeName.get(LISTENABLE_FUTURE, typeName);
+  }
+
+  public static ParameterizedTypeName membersInjectorOf(TypeName membersInjectorType) {
+    return ParameterizedTypeName.get(MEMBERS_INJECTOR, membersInjectorType);
+  }
+
+  public static ParameterizedTypeName producedOf(TypeName typeName) {
+    return ParameterizedTypeName.get(PRODUCED, typeName);
+  }
+
+  public static ParameterizedTypeName producerOf(TypeName typeName) {
+    return ParameterizedTypeName.get(PRODUCER, typeName);
+  }
+
+  public static ParameterizedTypeName dependencyMethodProducerOf(TypeName typeName) {
+    return ParameterizedTypeName.get(DEPENDENCY_METHOD_PRODUCER, typeName);
+  }
+
+  public static ParameterizedTypeName providerOf(TypeName typeName) {
+    return ParameterizedTypeName.get(PROVIDER, typeName);
+  }
+
+  public static ParameterizedTypeName setOf(TypeName elementType) {
+    return ParameterizedTypeName.get(SET, elementType);
+  }
+
+  /**
+   * Returns the {@link TypeName} for the raw type of the given type name. If the argument isn't a
+   * parameterized type, it returns the argument unchanged.
+   */
+  public static TypeName rawTypeName(TypeName typeName) {
+    return (typeName instanceof ParameterizedTypeName)
+        ? ((ParameterizedTypeName) typeName).rawType
+        : typeName;
+  }
+
+  private TypeNames() {}
+}
diff --git a/java/dagger/internal/codegen/TypeSpecs.java b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
similarity index 84%
rename from java/dagger/internal/codegen/TypeSpecs.java
rename to java/dagger/internal/codegen/javapoet/TypeSpecs.java
index 92f1a3a..8ec8747 100644
--- a/java/dagger/internal/codegen/TypeSpecs.java
+++ b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
 import javax.lang.model.element.TypeElement;
 
-/**
- * Convenience methods for use with JavaPoet's {@link TypeSpec}.
- */
-final class TypeSpecs {
+/** Convenience methods for use with JavaPoet's {@link TypeSpec}. */
+public final class TypeSpecs {
 
   /**
    * If {@code supertype} is a class, adds it as a superclass for {@code typeBuilder}; if it is an
@@ -33,7 +31,7 @@
    * @return {@code typeBuilder}
    */
   @CanIgnoreReturnValue
-  static TypeSpec.Builder addSupertype(TypeSpec.Builder typeBuilder, TypeElement supertype) {
+  public static TypeSpec.Builder addSupertype(TypeSpec.Builder typeBuilder, TypeElement supertype) {
     switch (supertype.getKind()) {
       case CLASS:
         return typeBuilder.superclass(ClassName.get(supertype));
diff --git a/java/dagger/internal/codegen/Accessibility.java b/java/dagger/internal/codegen/langmodel/Accessibility.java
similarity index 84%
rename from java/dagger/internal/codegen/Accessibility.java
rename to java/dagger/internal/codegen/langmodel/Accessibility.java
index 558b58f..62c1fbd 100644
--- a/java/dagger/internal/codegen/Accessibility.java
+++ b/java/dagger/internal/codegen/langmodel/Accessibility.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.langmodel;
 
 import static com.google.auto.common.MoreElements.getPackage;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -45,31 +45,28 @@
 
 /**
  * Utility methods for determining whether a {@linkplain TypeMirror type} or an {@linkplain Element
- * element} is accessible given the rules outlined in
- * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the
+ * element} is accessible given the rules outlined in <a
+ * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6">section 6.6 of the
  * Java Language Specification</a>.
  *
- * <p>This class only provides an approximation for accessibility.  It does not always yield the
- * same result as the compiler, but will always err on the side of declaring something inaccessible.
- * This ensures that using this class will never result in generating code that will not compile.
+ * <p>This class only provides an approximation for accessibility. It does not always yield the same
+ * result as the compiler, but will always err on the side of declaring something inaccessible. This
+ * ensures that using this class will never result in generating code that will not compile.
  *
  * <p>Whenever compiler independence is not a requirement, the compiler-specific implementation of
- * this functionality should be preferred.  For example,
- * {@link com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be
+ * this functionality should be preferred. For example, {@link
+ * com.sun.source.util.Trees#isAccessible(com.sun.source.tree.Scope, TypeElement)} would be
  * preferable for {@code javac}.
  */
-final class Accessibility {
-
-  /**
-   * Returns true if the given type can be referenced from code in the given package.
-   */
-  static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) {
-    return type.accept(new TypeAccessibilityVisitor(packageName), null);
+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);
   }
 
-  /** Returns true if the given type can be referenced from any package. */
-  static boolean isTypePubliclyAccessible(TypeMirror type) {
-    return type.accept(new TypeAccessibilityVisitor(), null);
+  /** 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);
   }
 
   private static boolean isTypeAccessibleFrom(TypeMirror type, Optional<String> packageName) {
@@ -151,20 +148,21 @@
 
     @Override
     protected Boolean defaultAction(TypeMirror type, Void p) {
-      throw new IllegalArgumentException(String.format(
-          "%s of kind %s should not be checked for accessibility", type, type.getKind()));
+      throw new IllegalArgumentException(
+          String.format(
+              "%s of kind %s should not be checked for accessibility", type, type.getKind()));
     }
   }
 
-  /** Returns true if the given element can be referenced from code in the given package. */
-  //TODO(gak): account for protected
-  static boolean isElementAccessibleFrom(Element element, String packageName) {
-    return element.accept(new ElementAccessibilityVisitor(packageName), null);
+  /** Returns true if the given element can be referenced from any package. */
+  public static boolean isElementPubliclyAccessible(Element element) {
+    return element.accept(new ElementAccessibilityVisitor(), null);
   }
 
-  /** Returns true if the given element can be referenced from any package. */
-  static boolean isElementPubliclyAccessible(Element element) {
-    return element.accept(new ElementAccessibilityVisitor(), null);
+  /** 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);
   }
 
   private static boolean isElementAccessibleFrom(Element element, Optional<String> packageName) {
@@ -172,7 +170,7 @@
   }
 
   /** Returns true if the given element can be referenced from other code in its own package. */
-  static boolean isElementAccessibleFromOwnPackage(Element element) {
+  public static boolean isElementAccessibleFromOwnPackage(Element element) {
     return isElementAccessibleFrom(
         element, MoreElements.getPackage(element).getQualifiedName().toString());
   }
@@ -208,9 +206,8 @@
         case ANONYMOUS:
         case LOCAL:
           return false;
-        default:
-          throw new AssertionError();
       }
+      throw new AssertionError();
     }
 
     boolean accessibleMember(Element element) {
@@ -266,15 +263,14 @@
       };
 
   /** Returns true if the raw type of {@code type} is accessible from the given package. */
-  static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
+  public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
     return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.of(requestingPackage));
   }
 
   /** Returns true if the raw type of {@code type} is accessible from any package. */
-  static boolean isRawTypePubliclyAccessible(TypeMirror type) {
+  public static boolean isRawTypePubliclyAccessible(TypeMirror type) {
     return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.empty());
   }
 
   private Accessibility() {}
 }
-
diff --git a/java/dagger/internal/codegen/langmodel/BUILD b/java/dagger/internal/codegen/langmodel/BUILD
new file mode 100644
index 0000000..16fa5d8
--- /dev/null
+++ b/java/dagger/internal/codegen/langmodel/BUILD
@@ -0,0 +1,31 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+#   Dagger-specific extensions to the javax.lang.model APIs
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "langmodel",
+    srcs = glob(["*.java"]),
+    plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+    tags = ["maven:merged"],
+    deps = [
+        "//java/dagger:core",
+        "@google_bazel_common//third_party/java/auto:common",
+        "@google_bazel_common//third_party/java/guava",
+        "@google_bazel_common//third_party/java/javapoet",
+    ],
+)
diff --git a/java/dagger/internal/codegen/DaggerElements.java b/java/dagger/internal/codegen/langmodel/DaggerElements.java
similarity index 67%
rename from java/dagger/internal/codegen/DaggerElements.java
rename to java/dagger/internal/codegen/langmodel/DaggerElements.java
index 5faab26..873ad3d 100644
--- a/java/dagger/internal/codegen/DaggerElements.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.langmodel;
 
 import static com.google.auto.common.MoreElements.asExecutable;
 import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
 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.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.Formatter.formatArgumentInList;
 import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toSet;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 
@@ -55,26 +52,24 @@
 import javax.lang.model.element.Name;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor8;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.SimpleElementVisitor8;
 import javax.lang.model.util.Types;
 
 /** Extension of {@link Elements} that adds Dagger-specific methods. */
 @Reusable
-final class DaggerElements implements Elements {
+public final class DaggerElements implements Elements {
 
   private final Elements elements;
   private final Types types;
 
-  DaggerElements(Elements elements, Types types) {
+  public DaggerElements(Elements elements, Types types) {
     this.elements = checkNotNull(elements);
     this.types = checkNotNull(types);
   }
 
-  DaggerElements(ProcessingEnvironment processingEnv) {
+  public DaggerElements(ProcessingEnvironment processingEnv) {
     this(processingEnv.getElementUtils(), processingEnv.getTypeUtils());
   }
 
@@ -82,21 +77,21 @@
    * Returns {@code true} if {@code encloser} is equal to {@code enclosed} or recursively encloses
    * it.
    */
-  static boolean elementEncloses(TypeElement encloser, Element enclosed) {
+  public static boolean elementEncloses(TypeElement encloser, Element enclosed) {
     return Iterables.contains(GET_ENCLOSED_ELEMENTS.breadthFirst(encloser), enclosed);
   }
 
   private static final Traverser<Element> GET_ENCLOSED_ELEMENTS =
       Traverser.forTree(Element::getEnclosedElements);
 
-  ImmutableSet<ExecutableElement> getUnimplementedMethods(TypeElement type) {
+  public ImmutableSet<ExecutableElement> getUnimplementedMethods(TypeElement type) {
     return FluentIterable.from(getLocalAndInheritedMethods(type, types, elements))
         .filter(hasModifiers(ABSTRACT))
         .toSet();
   }
 
   /** Returns the type element for a class. */
-  TypeElement getTypeElement(Class<?> clazz) {
+  public TypeElement getTypeElement(Class<?> clazz) {
     return getTypeElement(clazz.getCanonicalName());
   }
 
@@ -106,87 +101,12 @@
   }
 
   /** Returns the type element for a class name. */
-  TypeElement getTypeElement(ClassName className) {
+  public TypeElement getTypeElement(ClassName className) {
     return getTypeElement(className.withoutAnnotations().toString());
   }
 
-  /**
-   * Returns a useful string form for an element.
-   *
-   * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
-   *
-   * <p>Parameters are given with their enclosing executable, with other parameters elided.
-   */
-  static String elementToString(Element element) {
-    return element.accept(ELEMENT_TO_STRING, null);
-  }
-
-  private static final ElementVisitor<String, Void> ELEMENT_TO_STRING =
-      new ElementKindVisitor8<String, Void>() {
-        @Override
-        public String visitExecutable(ExecutableElement executableElement, Void aVoid) {
-          return enclosingTypeAndMemberName(executableElement)
-              .append(
-                  executableElement.getParameters().stream()
-                      .map(parameter -> parameter.asType().toString())
-                      .collect(joining(", ", "(", ")")))
-              .toString();
-        }
-
-        @Override
-        public String visitVariableAsParameter(VariableElement parameter, Void aVoid) {
-          ExecutableElement methodOrConstructor = asExecutable(parameter.getEnclosingElement());
-          return enclosingTypeAndMemberName(methodOrConstructor)
-              .append('(')
-              .append(
-                  formatArgumentInList(
-                      methodOrConstructor.getParameters().indexOf(parameter),
-                      methodOrConstructor.getParameters().size(),
-                      parameter.getSimpleName()))
-              .append(')')
-              .toString();
-        }
-
-        @Override
-        public String visitVariableAsField(VariableElement field, Void aVoid) {
-          return enclosingTypeAndMemberName(field).toString();
-        }
-
-        @Override
-        public String visitType(TypeElement type, Void aVoid) {
-          return type.getQualifiedName().toString();
-        }
-
-        @Override
-        protected String defaultAction(Element element, Void aVoid) {
-          throw new UnsupportedOperationException(
-              "Can't determine string for " + element.getKind() + " element " + element);
-        }
-
-        private StringBuilder enclosingTypeAndMemberName(Element element) {
-          StringBuilder name = new StringBuilder(element.getEnclosingElement().accept(this, null));
-          if (!element.getSimpleName().contentEquals("<init>")) {
-            name.append('.').append(element.getSimpleName());
-          }
-          return name;
-        }
-      };
-
-  /** A formatter that calls {@link #elementToString(Element)}. */
-  static Formatter<Element> elementFormatter() {
-    return ELEMENT_FORMATTER;
-  }
-
-  private static final Formatter<Element> ELEMENT_FORMATTER =
-      new Formatter<Element>() {
-        @Override
-        public String format(Element element) {
-          return elementToString(element);
-        }
-      };
-
   /** Returns the argument or the closest enclosing element that is a {@link TypeElement}. */
-  static TypeElement closestEnclosingTypeElement(Element element) {
+  public static TypeElement closestEnclosingTypeElement(Element element) {
     return element.accept(CLOSEST_ENCLOSING_TYPE_ELEMENT, null);
   }
 
@@ -207,7 +127,7 @@
    * Compares elements according to their declaration order among siblings. Only valid to compare
    * elements enclosed by the same parent.
    */
-  static final Comparator<Element> DECLARATION_ORDER =
+  public static final Comparator<Element> DECLARATION_ORDER =
       comparing(element -> siblings(element).indexOf(element));
 
   // For parameter elements, element.getEnclosingElement().getEnclosedElements() is empty. So
@@ -219,11 +139,11 @@
   }
 
   /**
-   * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose
-   * {@linkplain AnnotationMirror#getAnnotationType() annotation type} has the same canonical name
-   * as any of that of {@code annotationClasses}.
+   * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+   * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as any of
+   * that of {@code annotationClasses}.
    */
-  static boolean isAnyAnnotationPresent(
+  public static boolean isAnyAnnotationPresent(
       Element element, Iterable<? extends Class<? extends Annotation>> annotationClasses) {
     for (Class<? extends Annotation> annotation : annotationClasses) {
       if (MoreElements.isAnnotationPresent(element, annotation)) {
@@ -234,7 +154,7 @@
   }
 
   @SafeVarargs
-  static boolean isAnyAnnotationPresent(
+  public static boolean isAnyAnnotationPresent(
       Element element,
       Class<? extends Annotation> first,
       Class<? extends Annotation>... otherAnnotations) {
@@ -245,10 +165,8 @@
    * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
    * AnnotationMirror#getAnnotationType() annotation type} is equivalent to {@code annotationType}.
    */
-  static boolean isAnnotationPresent(Element element, TypeMirror annotationType) {
-    return element
-        .getAnnotationMirrors()
-        .stream()
+  public static boolean isAnnotationPresent(Element element, TypeMirror annotationType) {
+    return element.getAnnotationMirrors().stream()
         .map(AnnotationMirror::getAnnotationType)
         .anyMatch(candidate -> MoreTypes.equivalence().equivalent(candidate, annotationType));
   }
@@ -258,7 +176,7 @@
    * rest}, checking each annotation type in order.
    */
   @SafeVarargs
-  static Optional<AnnotationMirror> getAnyAnnotation(
+  public static Optional<AnnotationMirror> getAnyAnnotation(
       Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
     return getAnyAnnotation(element, asList(first, rest));
   }
@@ -267,11 +185,9 @@
    * Returns the annotation present on {@code element} whose type is in {@code annotations},
    * checking each annotation type in order.
    */
-  static Optional<AnnotationMirror> getAnyAnnotation(
+  public static Optional<AnnotationMirror> getAnyAnnotation(
       Element element, Collection<? extends Class<? extends Annotation>> annotations) {
-    return element
-        .getAnnotationMirrors()
-        .stream()
+    return element.getAnnotationMirrors().stream()
         .filter(hasAnnotationTypeIn(annotations))
         .map((AnnotationMirror a) -> a) // Avoid returning Optional<? extends AnnotationMirror>.
         .findFirst();
@@ -279,13 +195,11 @@
 
   /** Returns the annotations present on {@code element} of all types. */
   @SafeVarargs
-  static ImmutableSet<AnnotationMirror> getAllAnnotations(
+  public static ImmutableSet<AnnotationMirror> getAllAnnotations(
       Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
-    return element
-        .getAnnotationMirrors()
-        .stream()
-        .filter(hasAnnotationTypeIn(asList(first, rest)))
-        .collect(toImmutableSet());
+    return ImmutableSet.copyOf(
+        Iterables.filter(
+            element.getAnnotationMirrors(), hasAnnotationTypeIn(asList(first, rest))::test));
   }
 
   /**
@@ -294,7 +208,7 @@
    * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
    * annotation proxies.
    */
-  static Optional<AnnotationMirror> getAnnotationMirror(
+  public static Optional<AnnotationMirror> getAnnotationMirror(
       Element element, Class<? extends Annotation> annotationClass) {
     return Optional.ofNullable(MoreElements.getAnnotationMirror(element, annotationClass).orNull());
   }
@@ -308,7 +222,7 @@
             MoreTypes.asTypeElement(annotation.getAnnotationType()).getQualifiedName().toString());
   }
 
-  static ImmutableSet<String> suppressedWarnings(Element element) {
+  public static ImmutableSet<String> suppressedWarnings(Element element) {
     SuppressWarnings suppressedWarnings = element.getAnnotation(SuppressWarnings.class);
     if (suppressedWarnings == null) {
       return ImmutableSet.of();
@@ -320,7 +234,7 @@
    * Invokes {@link Elements#getTypeElement(CharSequence)}, throwing {@link TypeNotPresentException}
    * if it is not accessible in the current compilation.
    */
-  TypeElement checkTypePresent(String typeName) {
+  public TypeElement checkTypePresent(String typeName) {
     TypeElement type = elements.getTypeElement(typeName);
     if (type == null) {
       throw new TypeNotPresentException(typeName, null);
diff --git a/java/dagger/internal/codegen/DaggerTypes.java b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
similarity index 92%
rename from java/dagger/internal/codegen/DaggerTypes.java
rename to java/dagger/internal/codegen/langmodel/DaggerTypes.java
index 57f763f..e588fbd 100644
--- a/java/dagger/internal/codegen/DaggerTypes.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.langmodel;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -49,13 +49,12 @@
 import javax.lang.model.util.Types;
 
 /** Extension of {@link Types} that adds Dagger-specific methods. */
-final class DaggerTypes implements Types {
-
+public final class DaggerTypes implements Types {
   private final Types types;
   private final DaggerElements elements;
 
   @Inject
-  DaggerTypes(Types types, DaggerElements elements) {
+  public DaggerTypes(Types types, DaggerElements elements) {
     this.types = checkNotNull(types);
     this.elements = checkNotNull(elements);
   }
@@ -64,7 +63,7 @@
    * 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.
    */
-  Optional<DeclaredType> nonObjectSuperclass(DeclaredType type) {
+  public Optional<DeclaredType> nonObjectSuperclass(DeclaredType type) {
     return Optional.ofNullable(MoreTypes.nonObjectSuperclass(types, elements, type).orNull());
   }
 
@@ -72,7 +71,7 @@
    * Returns the {@linkplain #directSupertypes(TypeMirror) supertype}s of a type in breadth-first
    * order.
    */
-  Iterable<TypeMirror> supertypes(TypeMirror type) {
+  public Iterable<TypeMirror> supertypes(TypeMirror type) {
     return Traverser.<TypeMirror>forGraph(this::directSupertypes).breadthFirst(type);
   }
 
@@ -84,7 +83,7 @@
    * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
    *     than one type arguments.
    */
-  TypeMirror unwrapType(TypeMirror type) {
+  public TypeMirror unwrapType(TypeMirror type) {
     TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
     checkArgument(unwrapped != null, "%s is a raw type", type);
     return unwrapped;
@@ -98,7 +97,7 @@
    * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
    *     type argument.
    */
-  TypeMirror unwrapTypeOrObject(TypeMirror type) {
+  public TypeMirror unwrapTypeOrObject(TypeMirror type) {
     return unwrapTypeOrDefault(type, elements.getTypeElement(Object.class).asType());
   }
 
@@ -118,7 +117,7 @@
    * <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
    * Set.class}, this will return {@code Set<List<Number>>}.
    */
-  DeclaredType wrapType(TypeMirror type, Class<?> wrappingClass) {
+  public DeclaredType wrapType(TypeMirror type, Class<?> wrappingClass) {
     return types.getDeclaredType(elements.getTypeElement(wrappingClass), type);
   }
 
@@ -133,7 +132,7 @@
    *
    * @throws IllegalArgumentException if {@code} has more than one type argument.
    */
-  DeclaredType rewrapType(TypeMirror type, Class<?> wrappingClass) {
+  public DeclaredType rewrapType(TypeMirror type, Class<?> wrappingClass) {
     List<? extends TypeMirror> typeArguments = MoreTypes.asDeclared(type).getTypeArguments();
     TypeElement wrappingType = elements.getTypeElement(wrappingClass);
     switch (typeArguments.size()) {
@@ -155,7 +154,7 @@
    *   <li>Otherwise returns {@link Object}.
    * </ul>
    */
-  protected TypeMirror publiclyAccessibleType(TypeMirror type) {
+  public TypeMirror publiclyAccessibleType(TypeMirror type) {
     return accessibleType(
         type, Accessibility::isTypePubliclyAccessible, Accessibility::isRawTypePubliclyAccessible);
   }
@@ -169,7 +168,7 @@
    *   <li>Otherwise returns {@link Object}.
    * </ul>
    */
-  protected TypeMirror accessibleType(TypeMirror type, ClassName requestingClass) {
+  public TypeMirror accessibleType(TypeMirror type, ClassName requestingClass) {
     return accessibleType(
         type,
         t -> Accessibility.isTypeAccessibleFrom(t, requestingClass.packageName()),
@@ -194,7 +193,7 @@
    * Throws {@link TypeNotPresentException} if {@code type} is an {@link
    * javax.lang.model.type.ErrorType}.
    */
-  static void checkTypePresent(TypeMirror type) {
+  public static void checkTypePresent(TypeMirror type) {
     type.accept(
         // TODO(ronshapiro): Extract a base class that visits all components of a complex type
         // and put it in auto.common
@@ -221,11 +220,11 @@
   private static final ImmutableSet<Class<?>> FUTURE_TYPES =
       ImmutableSet.of(ListenableFuture.class, FluentFuture.class);
 
-  static boolean isFutureType(TypeMirror type) {
+  public static boolean isFutureType(TypeMirror type) {
     return FUTURE_TYPES.stream().anyMatch(t -> MoreTypes.isTypeOf(t, type));
   }
 
-  static boolean hasTypeVariable(TypeMirror type) {
+  public static boolean hasTypeVariable(TypeMirror type) {
     return type.accept(
         new SimpleTypeVisitor8<Boolean, Void>() {
           @Override
diff --git a/java/dagger/internal/codegen/serialization/BUILD b/java/dagger/internal/codegen/serialization/BUILD
new file mode 100644
index 0000000..2bc02b4
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/BUILD
@@ -0,0 +1,41 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+#    Serialized forms of types used in the Dagger processor.
+
+package(default_visibility = ["//:src"])
+
+proto_library(
+    name = "serialization_proto",
+    srcs = ["serialization.proto"],
+    visibility = ["//visibility:private"],
+)
+
+java_proto_library(
+    name = "serialization_java_proto",
+    visibility = ["//visibility:private"],
+    deps = [":serialization_proto"],
+)
+
+java_library(
+    name = "serialization",
+    srcs = glob(["*.java"]),
+    exports = [":serialization_java_proto"],
+    deps = [
+        "@google_bazel_common//third_party/java/guava",
+        "@google_bazel_common//third_party/java/javapoet",
+        "@google_bazel_common//third_party/java/protobuf",
+    ],
+)
diff --git a/java/dagger/internal/codegen/serialization/ProtoSerialization.java b/java/dagger/internal/codegen/serialization/ProtoSerialization.java
new file mode 100644
index 0000000..1449e9d
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/ProtoSerialization.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.serialization;
+
+import static com.google.common.io.BaseEncoding.base64;
+
+import com.google.common.io.BaseEncoding;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.squareup.javapoet.CodeBlock;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/**
+ * Serializes and deserializes {@link Message}s using {@link BaseEncoding#base64()} for use in
+ * annotation values.
+ */
+public final class ProtoSerialization {
+  /** Returns a {@link CodeBlock} of {@code message} serialized as a String. */
+  public static CodeBlock toAnnotationValue(Message message) {
+    return CodeBlock.of("$S", base64().encode(message.toByteArray()));
+  }
+
+  /**
+   * Returns a {@link Message T} from the deserialized the String {@code value}.
+   *
+   * @throws IllegalArgumentException if {@code value} represents an {@link AnnotationValue} who's
+   *     type is not {@link String}
+   */
+  public static <T extends Message> T fromAnnotationValue(
+      AnnotationValue value, T defaultInstance) {
+    byte[] bytes = base64().decode(value.accept(STRING_VALUE, null));
+    Message message;
+    try {
+      message = defaultInstance.getParserForType().parseFrom(bytes);
+    } catch (InvalidProtocolBufferException e) {
+      throw new InconsistentSerializedProtoException(e);
+    }
+    @SuppressWarnings("unchecked") // guaranteed by proto API
+    T t = (T) message;
+    return t;
+  }
+
+  private static final AnnotationValueVisitor<String, Void> STRING_VALUE =
+      new SimpleAnnotationValueVisitor8<String, Void>() {
+        @Override
+        public String visitString(String s, Void ignored) {
+          return s;
+        }
+
+        @Override
+        protected String defaultAction(Object o, Void ignored) {
+          throw new IllegalArgumentException(o + " is not a String");
+        }
+      };
+
+  /**
+   * An exception thrown when the proto that's serialized in a compiled subcomponent implementation
+   * is from a different version than the current compiler's.
+   */
+  public static final class InconsistentSerializedProtoException extends RuntimeException {
+    InconsistentSerializedProtoException(Throwable cause) {
+      super(cause);
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/serialization/serialization.proto b/java/dagger/internal/codegen/serialization/serialization.proto
new file mode 100644
index 0000000..e6c9577
--- /dev/null
+++ b/java/dagger/internal/codegen/serialization/serialization.proto
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Serialized forms of types used in the Dagger processor. The wire format of
+// these types is not guaranteed to remain compatible over time; serialization
+// is only expected to function correctly within an individual version of the
+// Dagger processor.
+
+syntax = "proto3";
+
+package dagger.internal.codegen.serialization;
+option java_package = "dagger.internal.codegen.serialization";
+option java_multiple_files = true;
+
+// TODO(ronshapiro): consider exposing some of these in
+// dagger.model.serialization
+
+// Serialized form of `dagger.internal.codegen.BindingRequest`
+message BindingRequestProto {
+  KeyProto key = 1;
+  RequestKindWrapper.RequestKind request_kind = 2;
+  FrameworkTypeWrapper.FrameworkType framework_type = 3;
+}
+
+message RequestKindWrapper {
+  // Serialized form of `dagger.model.RequestKind`
+  enum RequestKind {
+    UNKNOWN = 0;
+    INSTANCE = 1;
+    PROVIDER = 2;
+    LAZY = 3;
+    PROVIDER_OF_LAZY = 4;
+    MEMBERS_INJECTION = 5;
+    PRODUCER = 6;
+    PRODUCED = 7;
+    FUTURE = 8;
+  }
+}
+
+message FrameworkTypeWrapper {
+  // Serialized form of `dagger.internal.codegen.FrameworkType`
+  enum FrameworkType {
+    UNKNOWN = 0;
+    PROVIDER = 1;
+    PRODUCER_NODE = 2;
+  }
+}
+
+// Serialized form of `dagger.model.Key`
+message KeyProto {
+  TypeProto type = 1;
+  AnnotationProto qualifier = 2;
+  MultibindingContributionIdentifier multibinding_contribution_identifier =
+      3;
+
+  // Serialized form of `dagger.model.Key.MultibindingContributionIdentifier`
+  message MultibindingContributionIdentifier {
+    string module = 1;
+    string binding_element = 2;
+  }
+}
+
+// Serialized form of `javax.lang.model.type.TypeMirror`
+message TypeProto {
+  PrimitiveKind primitive_kind = 1;
+
+  // The qualified name of the type. Absent if this is an inner type.
+  string qualified_name = 2;
+
+  // The enclosing type if this is an inner type, otherwise absent.
+  TypeProto enclosing_type = 3;
+
+  // Simple name of the type if this is an inner type, otherwise absent.
+  string simple_name = 4;
+
+  repeated TypeProto type_arguments = 5;
+
+  message Wildcard {
+    TypeProto extends_bound = 1;
+    TypeProto super_bound = 2;
+  }
+  Wildcard wildcard = 6;
+
+  int32 array_dimensions = 7;
+
+  // Kinds of primitive types
+  enum PrimitiveKind {
+    UNKNOWN = 0;
+    BOOLEAN = 1;
+    BYTE = 2;
+    SHORT = 3;
+    CHAR = 4;
+    INT = 5;
+    FLOAT = 6;
+    LONG = 7;
+    DOUBLE = 8;
+  }
+}
+
+// Serialized form of `javax.lang.model.element.AnnotationMirror`
+message AnnotationProto {
+  TypeProto annotation_type = 1;
+  map<string, AnnotationValueProto> values = 2;
+}
+
+// Serialized form of `javax.lang.model.element.AnnotationValue`
+message AnnotationValueProto {
+  Kind kind = 1;
+  bool boolean_value = 2;
+  int32 int_value = 3;
+  int64 long_value = 4;
+  float float_value = 5;
+  double double_value = 6;
+  string string_value = 7;
+  TypeProto class_literal = 8;
+  TypeProto enum_type = 9;
+  string enum_name = 10;
+  AnnotationProto nested_annotation = 11;
+
+  repeated AnnotationValueProto array_values = 12;
+
+  // The type of annotation value
+  enum Kind {
+    UNKNOWN = 0;
+    BOOLEAN = 1;
+    BYTE = 2;
+    SHORT = 3;
+    CHAR = 4;
+    INT = 5;
+    FLOAT = 6;
+    LONG = 7;
+    DOUBLE = 8;
+    STRING = 9;
+    CLASS_LITERAL = 10;
+    ENUM = 11;
+    ANNOTATION = 12;
+    ARRAY = 13;
+  }
+}
+
+// Serialized form of `dagger.internal.codegen.ComponentRequirement`
+message ComponentRequirementProto {
+  oneof requirement {
+    TypeProto dependency = 1;
+    TypeProto module = 2;
+    BoundInstanceRequirement bound_instance = 3;
+  }
+
+  message BoundInstanceRequirement {
+    KeyProto key = 1;
+    bool nullable = 2;
+    string variable_name = 3;
+  }
+}
diff --git a/java/dagger/model/BindingGraph.java b/java/dagger/model/BindingGraph.java
index 681cfaf..748bf38 100644
--- a/java/dagger/model/BindingGraph.java
+++ b/java/dagger/model/BindingGraph.java
@@ -106,14 +106,13 @@
   }
 
   /**
-   * Returns {@code true} if this graph was constructed from a module for module binding validation.
+   * Returns {@code true} if this graph was constructed from a module for full binding graph
+   * validation.
    *
-   * @see <a href="https://google.github.io/dagger/compiler-options#module-binding-validation">Module binding
-   *     validation</a>
    * @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 will soon
-   *     generate full binding graphs for components and subcomponents as well as modules.
+   *     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 final boolean isModuleBindingGraph() {
@@ -124,6 +123,9 @@
    * 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();
 
@@ -424,6 +426,13 @@
     public final String toString() {
       return String.format("missing binding for %s in %s", key(), componentPath());
     }
+
+    @Memoized
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object o);
   }
 
   /**
diff --git a/java/dagger/model/Key.java b/java/dagger/model/Key.java
index 53ee391..03dd41c 100644
--- a/java/dagger/model/Key.java
+++ b/java/dagger/model/Key.java
@@ -27,6 +27,8 @@
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
 import com.squareup.javapoet.CodeBlock;
 import java.util.List;
 import java.util.Objects;
@@ -176,6 +178,7 @@
   }
 
   /** A builder for {@link Key}s. */
+  @CanIgnoreReturnValue
   @AutoValue.Builder
   public abstract static class Builder {
     abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType);
@@ -203,6 +206,7 @@
     public abstract Builder multibindingContributionIdentifier(
         MultibindingContributionIdentifier identifier);
 
+    @CheckReturnValue
     public abstract Key build();
   }
 
@@ -222,9 +226,27 @@
      */
     @Deprecated
     public MultibindingContributionIdentifier(
+        // TODO(ronshapiro): reverse the order of these parameters
         ExecutableElement bindingMethod, TypeElement contributingModule) {
-      this.module = contributingModule.getQualifiedName().toString();
-      this.bindingElement = bindingMethod.getSimpleName().toString();
+      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;
     }
 
     /**
diff --git a/java/dagger/model/Scope.java b/java/dagger/model/Scope.java
index 7d255e1..f48fa16 100644
--- a/java/dagger/model/Scope.java
+++ b/java/dagger/model/Scope.java
@@ -90,7 +90,7 @@
 
   /** Returns a debug representation of the scope. */
   @Override
-  public String toString() {
+  public final String toString() {
     return scopeAnnotation().toString();
   }
 }
diff --git a/java/dagger/model/testing/BindingGraphSubject.java b/java/dagger/model/testing/BindingGraphSubject.java
index f251eb5..dc17c1d 100644
--- a/java/dagger/model/testing/BindingGraphSubject.java
+++ b/java/dagger/model/testing/BindingGraphSubject.java
@@ -36,8 +36,11 @@
     return assertAbout(BindingGraphSubject::new).that(bindingGraph);
   }
 
+  private final BindingGraph actual;
+
   private BindingGraphSubject(FailureMetadata metadata, @NullableDecl BindingGraph actual) {
     super(metadata, actual);
+    this.actual = actual;
   }
 
   /**
@@ -82,21 +85,15 @@
 
   private BindingSubject bindingWithKeyString(String keyString) {
     ImmutableSet<Binding> bindings = getBindingNodes(keyString);
-    if (bindings.isEmpty()) {
-      fail("has binding with key", keyString);
-    }
     // TODO(dpb): Handle multiple bindings for the same key.
-    if (bindings.size() > 1) {
-      failWithBadResults(
-          "has only one binding with key", keyString, "has the following bindings:", bindings);
-    }
+    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()
+    return actual.bindings().stream()
         .filter(binding -> binding.key().toString().equals(keyString))
         .collect(toImmutableSet());
   }
@@ -112,8 +109,11 @@
   /** A Truth subject for a {@link Binding}. */
   public final class BindingSubject extends Subject<BindingSubject, Binding> {
 
+    private final Binding actual;
+
     BindingSubject(FailureMetadata metadata, @NullableDecl Binding actual) {
       super(metadata, actual);
+      this.actual = actual;
     }
 
     /**
@@ -137,14 +137,14 @@
     }
 
     private void dependsOnBindingWithKeyString(String keyString) {
-      if (actualBindingGraph().requestedBindings(actual()).stream()
+      if (actualBindingGraph().requestedBindings(actual).stream()
           .noneMatch(binding -> binding.key().toString().equals(keyString))) {
-        fail("depends on binding with key", keyString);
+        failWithActual("expected to depend on binding with key", keyString);
       }
     }
 
     private BindingGraph actualBindingGraph() {
-      return BindingGraphSubject.this.actual();
+      return BindingGraphSubject.this.actual;
     }
   }
 }
diff --git a/java/dagger/multibindings/ElementsIntoSet.java b/java/dagger/multibindings/ElementsIntoSet.java
index 72cc86d..5ed68c6 100644
--- a/java/dagger/multibindings/ElementsIntoSet.java
+++ b/java/dagger/multibindings/ElementsIntoSet.java
@@ -28,7 +28,7 @@
  * Set<T>} produced from the accumulation of values will be immutable. An example use is to provide
  * a default empty set binding, which is otherwise not possible using {@link IntoSet}.
  *
- * @see <a href="https://google.github.io/dagger/multibindings#set-multibindings">Set multibinding</a>
+ * @see <a href="https://dagger.dev/multibindings#set-multibindings">Set multibinding</a>
  */
 @Documented
 @Target(METHOD)
diff --git a/java/dagger/multibindings/IntoMap.java b/java/dagger/multibindings/IntoMap.java
index 3066b58..d760229 100644
--- a/java/dagger/multibindings/IntoMap.java
+++ b/java/dagger/multibindings/IntoMap.java
@@ -29,7 +29,7 @@
  * key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
  * immutable.
  *
- * @see <a href="https://google.github.io/dagger/multibindings#map-multibindings">Map multibinding</a>
+ * @see <a href="https://dagger.dev/multibindings#map-multibindings">Map multibinding</a>
  */
 @Documented
 @Target(METHOD)
diff --git a/java/dagger/multibindings/IntoSet.java b/java/dagger/multibindings/IntoSet.java
index f42860f..b4fdcc4 100644
--- a/java/dagger/multibindings/IntoSet.java
+++ b/java/dagger/multibindings/IntoSet.java
@@ -28,7 +28,7 @@
  * value is contributed to the set. The object graph will pass dependencies to the method as
  * parameters. The {@code Set<T>} produced from the accumulation of values will be immutable.
  *
- * @see <a href="https://google.github.io/dagger/multibindings#set-multibindings">Set multibinding</a>
+ * @see <a href="https://dagger.dev/multibindings#set-multibindings">Set multibinding</a>
  */
 @Documented
 @Target(METHOD)
diff --git a/java/dagger/multibindings/Multibinds.java b/java/dagger/multibindings/Multibinds.java
index 1517b26..e37b39e 100644
--- a/java/dagger/multibindings/Multibinds.java
+++ b/java/dagger/multibindings/Multibinds.java
@@ -48,7 +48,7 @@
  * <p>A given set or map multibinding can be declared any number of times without error. Dagger
  * never implements or calls any {@code @Multibinds} methods.
  *
- * @see <a href="https://google.github.io/dagger/multibindings">Multibindings</a>
+ * @see <a href="https://dagger.dev/multibindings">Multibindings</a>
  */
 @Documented
 @Target(METHOD)
diff --git a/java/dagger/multibindings/package-info.java b/java/dagger/multibindings/package-info.java
index e806d9e..d6fe1e1 100644
--- a/java/dagger/multibindings/package-info.java
+++ b/java/dagger/multibindings/package-info.java
@@ -18,6 +18,6 @@
  * This package contains the API by which Dagger allows you to bind several objects into a
  * collection that can be injected without depending directly on each of the individual bindings.
  *
- * @see <a href="https://google.github.io/dagger/multibindings">Multibindings in the Dagger User's Guide</a>
+ * @see <a href="https://dagger.dev/multibindings">Multibindings in the Dagger User's Guide</a>
  */
 package dagger.multibindings;
diff --git a/java/dagger/package-info.java b/java/dagger/package-info.java
index 99cd44b..b680a85 100644
--- a/java/dagger/package-info.java
+++ b/java/dagger/package-info.java
@@ -15,7 +15,7 @@
  */
 
 /**
- * This package contains the public API for the <a href="https://google.github.io/dagger/">Dagger 2</a> dependency
+ * This package contains the public API for the <a href="https://dagger.dev/">Dagger 2</a> dependency
  * injection framework. By building upon <a href="https://jcp.org/en/jsr/detail?id=330">JSR 330</a>,
  * Dagger 2 provides an annotation-driven API for dependency injection whose implementation is
  * entirely generated at compile time by <a
diff --git a/java/dagger/producers/monitoring/ProducerTimingRecorder.java b/java/dagger/producers/monitoring/ProducerTimingRecorder.java
index 90fe29a..f4894c9 100644
--- a/java/dagger/producers/monitoring/ProducerTimingRecorder.java
+++ b/java/dagger/producers/monitoring/ProducerTimingRecorder.java
@@ -41,11 +41,12 @@
    * <p>If the producer was skipped due to any of its inputs failing, then this will not be called.
    *
    * @param startedNanos the wall-clock time, in nanoseconds, when the producer method started
-   *     executing, measured from when the first method on the
-   *     {@linkplain ProductionComponent production component} was called.
+   *     executing, measured from when the first method on the {@linkplain ProductionComponent
+   *     production component} was called.
    * @param durationNanos the wall-clock time, in nanoseconds, that the producer method took to
    *     execute.
    */
+  @SuppressWarnings("GoodTime") // should accept a java.time.Duration x2 (?)
   public void recordMethod(long startedNanos, long durationNanos) {}
 
   /**
@@ -56,6 +57,7 @@
    * @param latencyNanos the wall-clock time, in nanoseconds, of the producer's latency, measured
    *     from when the producer method started to when the future finished.
    */
+  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
   public void recordSuccess(long latencyNanos) {}
 
   /**
@@ -65,6 +67,7 @@
    * @param latencyNanos the wall-clock time, in nanoseconds, of the producer's latency, measured
    *     from when the producer method started to when the future finished.
    */
+  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
   public void recordFailure(Throwable exception, long latencyNanos) {}
 
   /**
diff --git a/java/dagger/producers/package-info.java b/java/dagger/producers/package-info.java
index 10185ef..9693ae9 100644
--- a/java/dagger/producers/package-info.java
+++ b/java/dagger/producers/package-info.java
@@ -21,6 +21,6 @@
  * in Java.
  *
  * <p>Extended documentation on Dagger Producers can be found at <a
- * href="https://google.github.io/dagger/producers">https://google.github.io/dagger/producers</a>.
+ * href="https://dagger.dev/producers">https://dagger.dev/producers</a>.
  */
 package dagger.producers;
diff --git a/javatests/dagger/android/AndroidInjectionTest.java b/javatests/dagger/android/AndroidInjectionTest.java
index a24ff5b..19b6e84 100644
--- a/javatests/dagger/android/AndroidInjectionTest.java
+++ b/javatests/dagger/android/AndroidInjectionTest.java
@@ -122,7 +122,7 @@
     } catch (Exception e) {
       assertThat(e)
           .hasMessageThat()
-          .contains("Application does not implement dagger.android.HasActivityInjector");
+          .contains("Application does not implement dagger.android.HasAndroidInjector");
     }
   }
 
diff --git a/javatests/dagger/android/support/functional/BUILD b/javatests/dagger/android/support/functional/BUILD
index ffb9361..130b971 100644
--- a/javatests/dagger/android/support/functional/BUILD
+++ b/javatests/dagger/android/support/functional/BUILD
@@ -27,12 +27,14 @@
     manifest = "AndroidManifest.xml",
     resource_files = glob(["res/**"]),
     deps = [
+        "@androidsdk//com.android.support:support-fragment-25.0.0",
+        "@androidsdk//com.android.support:appcompat-v7-25.0.0",
+        "@google_bazel_common//third_party/java/guava",
+        "//:dagger_with_compiler",
         "//:android",
         "//:android-support",
-        "//:dagger_with_compiler",
-        "@androidsdk//com.android.support:appcompat-v7-25.0.0",
-        "@androidsdk//com.android.support:support-fragment-25.0.0",
-        "@google_bazel_common//third_party/java/guava",
+        # TODO(ronshapiro): figure out why strict deps is failing without this
+        "@google_bazel_common//third_party/java/jsr250_annotations",
     ],
 )
 
diff --git a/javatests/dagger/android/support/functional/InjectorsTest.java b/javatests/dagger/android/support/functional/InjectorsTest.java
index e66d702..c5cb150 100644
--- a/javatests/dagger/android/support/functional/InjectorsTest.java
+++ b/javatests/dagger/android/support/functional/InjectorsTest.java
@@ -232,7 +232,7 @@
     TestActivityWithScope activityWithScope =
         Robolectric.setupActivity(TestActivityWithScope.class);
     assertThat(activityWithScope.scopedStringProvider.get())
-        .isSameAs(activityWithScope.scopedStringProvider.get());
+        .isSameInstanceAs(activityWithScope.scopedStringProvider.get());
 
     OuterClass.TestInnerClassActivity innerClassActivity =
         Robolectric.setupActivity(OuterClass.TestInnerClassActivity.class);
diff --git a/javatests/dagger/functional/BasicTest.java b/javatests/dagger/functional/BasicTest.java
index 39dee30..80fc5e6 100644
--- a/javatests/dagger/functional/BasicTest.java
+++ b/javatests/dagger/functional/BasicTest.java
@@ -83,30 +83,31 @@
   }
 
   @Theory public void primitiveArrays(BasicComponent basicComponent) {
-    assertThat(basicComponent.getByteArray()).isSameAs(BOUND_BYTE_ARRAY);
-    assertThat(basicComponent.getCharArray()).isSameAs(BOUND_CHAR_ARRAY);
-    assertThat(basicComponent.getShortArray()).isSameAs(BOUND_SHORT_ARRAY);
-    assertThat(basicComponent.getIntArray()).isSameAs(BOUND_INT_ARRAY);
-    assertThat(basicComponent.getLongArray()).isSameAs(BOUND_LONG_ARRAY);
-    assertThat(basicComponent.getBooleanArray()).isSameAs(BOUND_BOOLEAN_ARRAY);
-    assertThat(basicComponent.getFloatArray()).isSameAs(BOUND_FLOAT_ARRAY);
-    assertThat(basicComponent.getDoubleArray()).isSameAs(BOUND_DOUBLE_ARRAY);
+    assertThat(basicComponent.getByteArray()).isSameInstanceAs(BOUND_BYTE_ARRAY);
+    assertThat(basicComponent.getCharArray()).isSameInstanceAs(BOUND_CHAR_ARRAY);
+    assertThat(basicComponent.getShortArray()).isSameInstanceAs(BOUND_SHORT_ARRAY);
+    assertThat(basicComponent.getIntArray()).isSameInstanceAs(BOUND_INT_ARRAY);
+    assertThat(basicComponent.getLongArray()).isSameInstanceAs(BOUND_LONG_ARRAY);
+    assertThat(basicComponent.getBooleanArray()).isSameInstanceAs(BOUND_BOOLEAN_ARRAY);
+    assertThat(basicComponent.getFloatArray()).isSameInstanceAs(BOUND_FLOAT_ARRAY);
+    assertThat(basicComponent.getDoubleArray()).isSameInstanceAs(BOUND_DOUBLE_ARRAY);
   }
 
   @Theory public void primitiveArrayProviders(BasicComponent basicComponent) {
-    assertThat(basicComponent.getByteArrayProvider().get()).isSameAs(BOUND_BYTE_ARRAY);
-    assertThat(basicComponent.getCharArrayProvider().get()).isSameAs(BOUND_CHAR_ARRAY);
-    assertThat(basicComponent.getShortArrayProvider().get()).isSameAs(BOUND_SHORT_ARRAY);
-    assertThat(basicComponent.getIntArrayProvider().get()).isSameAs(BOUND_INT_ARRAY);
-    assertThat(basicComponent.getLongArrayProvider().get()).isSameAs(BOUND_LONG_ARRAY);
-    assertThat(basicComponent.getBooleanArrayProvider().get()).isSameAs(BOUND_BOOLEAN_ARRAY);
-    assertThat(basicComponent.getFloatArrayProvider().get()).isSameAs(BOUND_FLOAT_ARRAY);
-    assertThat(basicComponent.getDoubleArrayProvider().get()).isSameAs(BOUND_DOUBLE_ARRAY);
+    assertThat(basicComponent.getByteArrayProvider().get()).isSameInstanceAs(BOUND_BYTE_ARRAY);
+    assertThat(basicComponent.getCharArrayProvider().get()).isSameInstanceAs(BOUND_CHAR_ARRAY);
+    assertThat(basicComponent.getShortArrayProvider().get()).isSameInstanceAs(BOUND_SHORT_ARRAY);
+    assertThat(basicComponent.getIntArrayProvider().get()).isSameInstanceAs(BOUND_INT_ARRAY);
+    assertThat(basicComponent.getLongArrayProvider().get()).isSameInstanceAs(BOUND_LONG_ARRAY);
+    assertThat(basicComponent.getBooleanArrayProvider().get())
+        .isSameInstanceAs(BOUND_BOOLEAN_ARRAY);
+    assertThat(basicComponent.getFloatArrayProvider().get()).isSameInstanceAs(BOUND_FLOAT_ARRAY);
+    assertThat(basicComponent.getDoubleArrayProvider().get()).isSameInstanceAs(BOUND_DOUBLE_ARRAY);
   }
 
   @Theory public void noOpMembersInjection(BasicComponent basicComponent) {
     Object object = new Object();
-    assertThat(basicComponent.noOpMembersInjection(object)).isSameAs(object);
+    assertThat(basicComponent.noOpMembersInjection(object)).isSameInstanceAs(object);
   }
 
   @Theory public void basicObject_noDeps(BasicComponent basicComponent) {
@@ -130,9 +131,9 @@
         basicComponent.lazyInjectedThingProvider();
     Lazy<InjectedThing> lazyInjectedThing1 = lazyInjectedThingProvider.get();
     Lazy<InjectedThing> lazyInjectedThing2 = lazyInjectedThingProvider.get();
-    assertThat(lazyInjectedThing2).isNotSameAs(lazyInjectedThing1);
-    assertThat(lazyInjectedThing1.get()).isSameAs(lazyInjectedThing1.get());
-    assertThat(lazyInjectedThing2.get()).isSameAs(lazyInjectedThing2.get());
-    assertThat(lazyInjectedThing2.get()).isNotSameAs(lazyInjectedThing1.get());
+    assertThat(lazyInjectedThing2).isNotSameInstanceAs(lazyInjectedThing1);
+    assertThat(lazyInjectedThing1.get()).isSameInstanceAs(lazyInjectedThing1.get());
+    assertThat(lazyInjectedThing2.get()).isSameInstanceAs(lazyInjectedThing2.get());
+    assertThat(lazyInjectedThing2.get()).isNotSameInstanceAs(lazyInjectedThing1.get());
   }
 }
diff --git a/javatests/dagger/functional/GenericTest.java b/javatests/dagger/functional/GenericTest.java
index be17446..3e4a271 100644
--- a/javatests/dagger/functional/GenericTest.java
+++ b/javatests/dagger/functional/GenericTest.java
@@ -130,26 +130,26 @@
   @Test public void singletonScopesAppliesToEachResolvedType() {
     SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
     ScopedGeneric<A> a = component.scopedGenericA();
-    assertThat(a).isSameAs(component.scopedGenericA());
+    assertThat(a).isSameInstanceAs(component.scopedGenericA());
     assertThat(a.t).isNotNull();
 
     ScopedGeneric<B> b = component.scopedGenericB();
-    assertThat(b).isSameAs(component.scopedGenericB());
+    assertThat(b).isSameInstanceAs(component.scopedGenericB());
     assertThat(b.t).isNotNull();
 
-    assertThat(a).isNotSameAs(b);
+    assertThat(a).isNotSameInstanceAs(b);
   }
 
   @Test // See https://github.com/google/dagger/issues/671
   public void scopedSimpleGenerics() {
     SingletonGenericComponent component = DaggerSingletonGenericComponent.create();
     ScopedSimpleGeneric<A> a = component.scopedSimpleGenericA();
-    assertThat(a).isSameAs(component.scopedSimpleGenericA());
+    assertThat(a).isSameInstanceAs(component.scopedSimpleGenericA());
 
     ScopedSimpleGeneric<B> b = component.scopedSimpleGenericB();
-    assertThat(b).isSameAs(component.scopedSimpleGenericB());
+    assertThat(b).isSameInstanceAs(component.scopedSimpleGenericB());
 
-    assertThat(a).isNotSameAs(b);
+    assertThat(a).isNotSameInstanceAs(b);
   }
   
   @Test public void genericModules() {
diff --git a/javatests/dagger/functional/LazyMapsTest.java b/javatests/dagger/functional/LazyMapsTest.java
index a3e289a..a441653 100644
--- a/javatests/dagger/functional/LazyMapsTest.java
+++ b/javatests/dagger/functional/LazyMapsTest.java
@@ -36,7 +36,7 @@
 
     String firstGet = laziesMap.get("key").get();
     assertThat(firstGet).isEqualTo("value-1");
-    assertThat(firstGet).isSameAs(laziesMap.get("key").get());
+    assertThat(firstGet).isSameInstanceAs(laziesMap.get("key").get());
 
     assertThat(component.mapOfLazy().get("key").get()).isEqualTo("value-2");
   }
diff --git a/javatests/dagger/functional/ReusableTest.java b/javatests/dagger/functional/ReusableTest.java
index 8149ff4..221b288 100644
--- a/javatests/dagger/functional/ReusableTest.java
+++ b/javatests/dagger/functional/ReusableTest.java
@@ -33,16 +33,16 @@
     ChildTwo childTwo = parent.childTwo();
 
     Object reusableInParent = parent.reusableInParent();
-    assertThat(parent.reusableInParent()).isSameAs(reusableInParent);
-    assertThat(childOne.reusableInParent()).isSameAs(reusableInParent);
-    assertThat(childTwo.reusableInParent()).isSameAs(reusableInParent);
+    assertThat(parent.reusableInParent()).isSameInstanceAs(reusableInParent);
+    assertThat(childOne.reusableInParent()).isSameInstanceAs(reusableInParent);
+    assertThat(childTwo.reusableInParent()).isSameInstanceAs(reusableInParent);
 
     Object reusableFromChildOne = childOne.reusableInChild();
-    assertThat(childOne.reusableInChild()).isSameAs(reusableFromChildOne);
+    assertThat(childOne.reusableInChild()).isSameInstanceAs(reusableFromChildOne);
 
     Object reusableFromChildTwo = childTwo.reusableInChild();
-    assertThat(childTwo.reusableInChild()).isSameAs(reusableFromChildTwo);
+    assertThat(childTwo.reusableInChild()).isSameInstanceAs(reusableFromChildTwo);
 
-    assertThat(reusableFromChildTwo).isNotSameAs(reusableFromChildOne);
+    assertThat(reusableFromChildTwo).isNotSameInstanceAs(reusableFromChildOne);
   }
 }
diff --git a/javatests/dagger/functional/binds/BindsTest.java b/javatests/dagger/functional/binds/BindsTest.java
index 11695bc..999c303 100644
--- a/javatests/dagger/functional/binds/BindsTest.java
+++ b/javatests/dagger/functional/binds/BindsTest.java
@@ -44,7 +44,7 @@
   @Test
   public void bindWithScope() {
     assertThat(component.qualifiedFooOfStrings())
-        .isSameAs(component.qualifiedFooOfStrings());
+        .isSameInstanceAs(component.qualifiedFooOfStrings());
   }
 
   @Test
diff --git a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
index f6c2818..b77ee3e 100644
--- a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
+++ b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
@@ -95,7 +95,7 @@
     assertThat(callCount.get()).isEqualTo(1);
     Object second = component.getObject();
     assertThat(callCount.get()).isEqualTo(1);
-    assertThat(first).isSameAs(second);
+    assertThat(first).isSameInstanceAs(second);
   }
 
   @Test
@@ -122,7 +122,7 @@
     assertThat(callCount.get()).isEqualTo(2);
     Object second = component.getReentrantObject();
     assertThat(callCount.get()).isEqualTo(2);
-    assertThat(first).isSameAs(second);
+    assertThat(first).isSameInstanceAs(second);
   }
 
   @Test
diff --git a/javatests/dagger/functional/cycle/LongCycleTest.java b/javatests/dagger/functional/cycle/LongCycleTest.java
index 4c986b2..a6244b1 100644
--- a/javatests/dagger/functional/cycle/LongCycleTest.java
+++ b/javatests/dagger/functional/cycle/LongCycleTest.java
@@ -17,6 +17,7 @@
 package dagger.functional.cycle;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.common.truth.TruthJUnit.assume;
 import static java.util.Arrays.stream;
 
@@ -50,6 +51,8 @@
         stream(DaggerLongCycle_LongCycleComponent.class.getDeclaredMethods())
             .map(Method::getName)
             .anyMatch(name -> name.equals("initialize2"));
-    assertThat(hasInitialize2).named("LongCycleComponent impl has an initialize2 method").isTrue();
+    assertWithMessage("LongCycleComponent impl has an initialize2 method")
+        .that(hasInitialize2)
+        .isTrue();
   }
 }
diff --git a/javatests/dagger/functional/factory/FactoryMixedParametersTest.java b/javatests/dagger/functional/factory/FactoryMixedParametersTest.java
index 3c8e2ca..733c673 100644
--- a/javatests/dagger/functional/factory/FactoryMixedParametersTest.java
+++ b/javatests/dagger/functional/factory/FactoryMixedParametersTest.java
@@ -66,7 +66,7 @@
     assertThat(component.getDouble()).isEqualTo(3.0);
     assertThat(component.object()).isEqualTo("bar");
     assertThat(component.getLong()).isEqualTo(2L);
-    assertThat(component.randomProvider().get()).isSameAs(random);
-    assertThat(component.randomProvider().get()).isSameAs(random);
+    assertThat(component.randomProvider().get()).isSameInstanceAs(random);
+    assertThat(component.randomProvider().get()).isSameInstanceAs(random);
   }
 }
diff --git a/javatests/dagger/functional/producers/ProducerFactoryTest.java b/javatests/dagger/functional/producers/ProducerFactoryTest.java
index 7b65203..c85e342 100644
--- a/javatests/dagger/functional/producers/ProducerFactoryTest.java
+++ b/javatests/dagger/functional/producers/ProducerFactoryTest.java
@@ -155,7 +155,7 @@
       producer.get().get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(t);
+      assertThat(e).hasCauseThat().isSameInstanceAs(t);
       order.verify(monitor).failed(t);
     }
 
diff --git a/javatests/dagger/functional/producers/binds/BindsProducersTest.java b/javatests/dagger/functional/producers/binds/BindsProducersTest.java
index a8c61fe..0949f02 100644
--- a/javatests/dagger/functional/producers/binds/BindsProducersTest.java
+++ b/javatests/dagger/functional/producers/binds/BindsProducersTest.java
@@ -46,7 +46,7 @@
   @Test
   public void bindWithScope() throws Exception {
     assertThat(component.qualifiedFooOfStrings().get())
-        .isSameAs(component.qualifiedFooOfStrings().get());
+        .isSameInstanceAs(component.qualifiedFooOfStrings().get());
   }
 
   @Test
diff --git a/javatests/dagger/functional/producers/cancellation/ProducerTester.java b/javatests/dagger/functional/producers/cancellation/ProducerTester.java
index 61ddaef..35cf8e9 100644
--- a/javatests/dagger/functional/producers/cancellation/ProducerTester.java
+++ b/javatests/dagger/functional/producers/cancellation/ProducerTester.java
@@ -17,7 +17,7 @@
 package dagger.functional.producers.cancellation;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.AbstractFuture;
@@ -93,7 +93,7 @@
   /** Asserts that no nodes in this tester have been started. */
   void assertNoStartedNodes() {
     for (TestFuture future : futures.values()) {
-      assertThat(future.isStarted()).named("%s is started", future).isFalse();
+      assertWithMessage("%s is started", future).that(future.isStarted()).isFalse();
     }
   }
 
@@ -101,7 +101,7 @@
     ImmutableSet.Builder<TestFuture> builder = ImmutableSet.builder();
     for (String node : nodes) {
       TestFuture future = getOrCreate(node);
-      assertThat(assertion.test(future)).named("%s is %s", future, assertion).isTrue();
+      assertWithMessage("%s is %s", future, assertion).that(assertion.test(future)).isTrue();
       builder.add(future);
     }
     return new Only(builder.build(), assertion);
@@ -128,7 +128,7 @@
     void only() {
       for (TestFuture future : futures.values()) {
         if (!expected.contains(future)) {
-          assertThat(assertion.test(future)).named("%s is %s", future, assertion).isFalse();
+          assertWithMessage("%s is %s", future, assertion).that(assertion.test(future)).isFalse();
         }
       }
     }
diff --git a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
index 3c01198..543835f 100644
--- a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
+++ b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
@@ -153,7 +153,7 @@
       output.get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(Throwables.getRootCause(e)).isSameAs(cause);
+      assertThat(Throwables.getRootCause(e)).isSameInstanceAs(cause);
     }
   }
 
diff --git a/javatests/dagger/functional/producers/provisions/ProvisionsTest.java b/javatests/dagger/functional/producers/provisions/ProvisionsTest.java
index 06f3074..47ed2a2 100644
--- a/javatests/dagger/functional/producers/provisions/ProvisionsTest.java
+++ b/javatests/dagger/functional/producers/provisions/ProvisionsTest.java
@@ -31,6 +31,6 @@
   public void provisionsOnlyAreHeldInOneProducer() throws Exception {
     TestComponent component = DaggerProvisions_TestComponent.create();
     Output output = component.output().get();
-    assertThat(output.injectedClass1).isSameAs(output.injectedClass2);
+    assertThat(output.injectedClass1).isSameInstanceAs(output.injectedClass2);
   }
 }
diff --git a/javatests/dagger/functional/producers/scope/ScopeTest.java b/javatests/dagger/functional/producers/scope/ScopeTest.java
index aaa4219..9cfd2c2 100644
--- a/javatests/dagger/functional/producers/scope/ScopeTest.java
+++ b/javatests/dagger/functional/producers/scope/ScopeTest.java
@@ -29,6 +29,6 @@
   public void scope() throws Exception {
     SetComponent component = DaggerSetComponent.create();
     assertThat(component.set().get()).hasSize(1);
-    assertThat(component.scopedObject()).isSameAs(component.scopedObject());
+    assertThat(component.scopedObject()).isSameInstanceAs(component.scopedObject());
   }
 }
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentTest.java b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
index 428de64..c34de0a 100644
--- a/javatests/dagger/functional/subcomponent/SubcomponentTest.java
+++ b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
@@ -53,21 +53,22 @@
   @Test
   public void scopePropagatesUpward_class() {
     assertThat(childComponent.requiresSingleton().singletonType())
-        .isSameAs(childComponent.requiresSingleton().singletonType());
+        .isSameInstanceAs(childComponent.requiresSingleton().singletonType());
     assertThat(childComponent.requiresSingleton().singletonType())
-        .isSameAs(childComponent.newGrandchildComponent().requiresSingleton().singletonType());
+        .isSameInstanceAs(
+            childComponent.newGrandchildComponent().requiresSingleton().singletonType());
   }
 
   @Test
   public void scopePropagatesUpward_provides() {
-    assertThat(childComponent
-        .requiresSingleton().unscopedTypeBoundAsSingleton())
-            .isSameAs(childComponent
-                .requiresSingleton().unscopedTypeBoundAsSingleton());
-    assertThat(childComponent
-        .requiresSingleton().unscopedTypeBoundAsSingleton())
-            .isSameAs(childComponent.newGrandchildComponent()
-                .requiresSingleton().unscopedTypeBoundAsSingleton());
+    assertThat(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton())
+        .isSameInstanceAs(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton());
+    assertThat(childComponent.requiresSingleton().unscopedTypeBoundAsSingleton())
+        .isSameInstanceAs(
+            childComponent
+                .newGrandchildComponent()
+                .requiresSingleton()
+                .unscopedTypeBoundAsSingleton());
   }
 
   @Test
@@ -88,11 +89,9 @@
   public void unscopedProviders() {
     assume().that(System.getProperty("dagger.mode")).doesNotContain("FastInit");
     assertThat(parentGetters.getUnscopedTypeProvider())
-        .isSameAs(childComponent.getUnscopedTypeProvider());
+        .isSameInstanceAs(childComponent.getUnscopedTypeProvider());
     assertThat(parentGetters.getUnscopedTypeProvider())
-        .isSameAs(childComponent
-            .newGrandchildComponent()
-            .getUnscopedTypeProvider());
+        .isSameInstanceAs(childComponent.newGrandchildComponent().getUnscopedTypeProvider());
   }
 
   @Test
diff --git a/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java b/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java
index 7b447eb..9bf3a39 100644
--- a/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java
+++ b/javatests/dagger/functional/subcomponent/repeat/RepeatedModuleTest.java
@@ -37,7 +37,7 @@
   public void repeatedModuleHasSameStateInSubcomponent() {
     SubcomponentWithRepeatedModule childComponent =
         parentComponent.newChildComponentBuilder().build();
-    assertThat(parentComponent.state()).isSameAs(childComponent.state());
+    assertThat(parentComponent.state()).isSameInstanceAs(childComponent.state());
   }
 
   @Test
@@ -46,7 +46,7 @@
         parentComponent.newChildComponentWithoutRepeatedModule();
     SubcomponentWithRepeatedModule grandchildComponent =
         childComponent.newGrandchildBuilder().build();
-    assertThat(parentComponent.state()).isSameAs(grandchildComponent.state());
+    assertThat(parentComponent.state()).isSameInstanceAs(grandchildComponent.state());
   }
 
   @Test
diff --git a/javatests/dagger/internal/DoubleCheckTest.java b/javatests/dagger/internal/DoubleCheckTest.java
index 20abbe5..e36c1bc 100644
--- a/javatests/dagger/internal/DoubleCheckTest.java
+++ b/javatests/dagger/internal/DoubleCheckTest.java
@@ -63,13 +63,13 @@
   @Test
   public void doubleWrapping_provider() {
     assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER))
-        .isSameAs(DOUBLE_CHECK_OBJECT_PROVIDER);
+        .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
   }
 
   @Test
   public void doubleWrapping_lazy() {
     assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER))
-        .isSameAs(DOUBLE_CHECK_OBJECT_PROVIDER);
+        .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
   }
 
   @Test
@@ -142,7 +142,7 @@
        return object;
      });
     doubleCheckReference.set(doubleCheck);
-    assertThat(doubleCheck.get()).isSameAs(object);
+    assertThat(doubleCheck.get()).isSameInstanceAs(object);
   }
 
   @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() {
@@ -165,6 +165,6 @@
   @Test
   public void instanceFactoryAsLazyDoesNotWrap() {
     Factory<Object> factory = InstanceFactory.create(new Object());
-    assertThat(DoubleCheck.lazy(factory)).isSameAs(factory);
+    assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory);
   }
 }
diff --git a/javatests/dagger/internal/codegen/AccessibilityTest.java b/javatests/dagger/internal/codegen/AccessibilityTest.java
deleted file mode 100644
index bfe73f3..0000000
--- a/javatests/dagger/internal/codegen/AccessibilityTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.truth.Truth.assertThat;
-import static dagger.internal.codegen.Accessibility.isElementAccessibleFrom;
-
-import com.google.testing.compile.CompilationRule;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-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)
-@SuppressWarnings("unused") // contains a variety things used by the compilation rule for testing
-public class AccessibilityTest {
-  /* test data */
-  public AccessibilityTest() {}
-  protected AccessibilityTest(Object o) {}
-  AccessibilityTest(Object o1, Object o2) {}
-  private AccessibilityTest(Object o1, Object o2, Object o3) {}
-
-  public String publicField;
-  protected String protectedField;
-  String packagePrivateField;
-  private String privateField;
-
-  public void publicMethod() {}
-  protected void protectedMethod() {}
-  void packagePrivateMethod() {}
-  private void privateMethod() {}
-
-  public static final class PublicNestedClass {}
-  protected static final class ProtectedNestedClass {}
-  static final class PackagePrivateNestedClass {}
-  private static final class PrivateNestedClass {}
-
-  @Rule
-  public final CompilationRule compilationRule = new CompilationRule();
-
-  private TypeElement testElement;
-
-  @Before
-  public void setUp() {
-    Elements elements = compilationRule.getElements();
-    testElement = elements.getTypeElement(AccessibilityTest.class.getCanonicalName());
-  }
-
-  @Test
-  public void isElementAccessibleFrom_publicType() {
-    assertThat(isElementAccessibleFrom(testElement, "literally.anything")).isTrue();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_publicMethod() {
-    Element member = getMemberNamed("publicMethod");
-    assertThat(isElementAccessibleFrom(member, "literally.anything")).isTrue();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_protectedMethod() {
-    Element member = getMemberNamed("protectedMethod");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isTrue();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_packagePrivateMethod() {
-    Element member = getMemberNamed("packagePrivateMethod");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isTrue();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_privateMethod() {
-    Element member = getMemberNamed( "privateMethod");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isFalse();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_publicField() {
-    Element member = getMemberNamed("publicField");
-    assertThat(isElementAccessibleFrom(member, "literally.anything")).isTrue();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_protectedField() {
-    Element member = getMemberNamed("protectedField");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isTrue();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_packagePrivateField() {
-    Element member = getMemberNamed("packagePrivateField");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isTrue();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  @Test
-  public void isElementAccessibleFrom_privateField() {
-    Element member = getMemberNamed("privateField");
-    assertThat(isElementAccessibleFrom(member, "dagger.internal.codegen")).isFalse();
-    assertThat(isElementAccessibleFrom(member, "not.dagger.internal.codegen")).isFalse();
-  }
-
-  private Element getMemberNamed(String memberName) {
-    for (Element enclosedElement : testElement.getEnclosedElements()) {
-      if (enclosedElement.getSimpleName().contentEquals(memberName)) {
-        return enclosedElement;
-      }
-    }
-    throw new IllegalArgumentException();
-  }
-}
-
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
index c92beb3..bb967b1 100644
--- a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
+++ b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
@@ -118,12 +118,16 @@
             "package test;",
             "",
             "import dagger.internal.GenerationOptions;",
+            "import java.util.Set;",
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATION_OPTIONS_ANNOTATION,
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Set<InAncestor> contributionsInAncestor();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -652,6 +656,9 @@
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract RequiresInAncestorSet missingWithSetDependency();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -791,6 +798,10 @@
             "    return ImmutableSet.<Multibound>of(",
             "        LeafModule_ContributionFactory.contribution());",
             "  }",
+            "",
+            "  @Override",
+            "  public abstract MissingInLeaf_WillDependOnFrameworkInstance",
+            "      willDependOnFrameworkInstance();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -903,12 +914,16 @@
             "package test;",
             "",
             "import dagger.internal.GenerationOptions;",
+            "import java.util.Set;",
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATION_OPTIONS_ANNOTATION,
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Set<Object> set();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -994,7 +1009,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -1010,7 +1025,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -1291,12 +1306,16 @@
             "package test;",
             "",
             "import dagger.internal.GenerationOptions;",
+            "import java.util.Map;",
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATION_OPTIONS_ANNOTATION,
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Map<String, InAncestor> contributionsInAncestor();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -2010,6 +2029,10 @@
             "    return ImmutableMap.<Integer, Multibound>of(",
             "        111, LeafModule_ContributionFactory.contribution());",
             "  }",
+            "",
+            "  @Override",
+            "  public abstract MissingInLeaf_WillDependOnFrameworkInstance",
+            "      willDependOnFrameworkInstance();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
index 4b51fe2..1bd221a 100644
--- a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
+++ b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
@@ -24,6 +24,7 @@
 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ObjectArrays;
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
 import javax.tools.JavaFileObject;
@@ -66,6 +67,9 @@
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract MissingInLeaf missingFromComponentMethod();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -164,6 +168,9 @@
             "  protected DaggerLeaf() {}",
             "",
             "  @Override",
+            "  public abstract MissingInLeaf missingComponentMethod();",
+            "",
+            "  @Override",
             "  public DependsOnComponentMethod dependsOnComponentMethod() {",
             "    return new DependsOnComponentMethod(missingComponentMethod());",
             "  }",
@@ -472,7 +479,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -488,7 +495,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -636,7 +643,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -652,7 +659,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -750,6 +757,9 @@
             "public abstract class DaggerAncestor implements Ancestor {",
             "  protected DaggerAncestor() {}",
             "",
+            "  @Override",
+            "  public abstract Leaf.Builder leaf();",
+            "",
             "  protected abstract class LeafImpl extends DaggerLeaf {",
             "    protected LeafImpl() {}",
             "  }",
@@ -779,7 +789,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -795,7 +805,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -938,7 +948,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -954,7 +964,7 @@
             "    return new MaybeLeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -1098,7 +1108,11 @@
             "    return Optional.<SatisfiedInAncestor>empty();",
             "  }",
             "}");
-    Compilation compilation = compile(filesToCompile.build());
+    Compilation compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerLeaf")
@@ -1154,7 +1168,11 @@
             "",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerAncestor")
@@ -1208,7 +1226,11 @@
             "    return Optional.<SatisfiedInGrandAncestor>empty();",
             "  }",
             "}");
-    Compilation compilation = compile(filesToCompile.build());
+    Compilation compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerLeaf")
@@ -1242,7 +1264,11 @@
             "    protected LeafImpl() {}",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerAncestor")
@@ -1302,7 +1328,11 @@
             "    }",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerGreatAncestor")
@@ -1371,7 +1401,11 @@
             "    return Optional.<SatisfiedInAncestor>empty();",
             "  }",
             "}");
-    Compilation compilation = compile(filesToCompile.build());
+    Compilation compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerLeaf")
@@ -1426,7 +1460,11 @@
             "    }",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerAncestor")
@@ -1455,12 +1493,17 @@
             "package test;",
             "",
             "import dagger.internal.GenerationOptions;",
+            "import java.util.Optional;",
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATION_OPTIONS_ANNOTATION,
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Optional<SatisfiedInGrandAncestor>",
+            "      boundInAncestorSatisfiedInGrandAncestor();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -1515,7 +1558,11 @@
             "    }",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerAncestor")
@@ -1575,7 +1622,11 @@
             "    }",
             "  }",
             "}");
-    compilation = compile(filesToCompile.build());
+    compilation =
+        compile(
+            filesToCompile.build()
+            , CompilerMode.JAVA7
+            );
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerGrandAncestor")
@@ -2037,7 +2088,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -2053,7 +2104,7 @@
             "    return new LeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    @Deprecated",
@@ -2191,7 +2242,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -2207,7 +2258,7 @@
             "    return new LeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    @Deprecated",
@@ -2379,7 +2430,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -2395,7 +2446,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -2630,7 +2681,7 @@
             "import javax.inject.Provider;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root, CancellationListener {",
+            "final class DaggerRoot implements Root, CancellationListener {",
             "  private Provider<Executor> productionImplementationExecutorProvider;",
             "  private Provider<Root> rootProvider;",
             "  private Provider<ProductionComponentMonitor> monitorProvider;",
@@ -3153,6 +3204,10 @@
             "    return LeafModule_FromModuleFactory.fromModule(leafModule());",
             "  }",
             "",
+            "  @Override",
+            "  public abstract InducesDependenciesOnBuilderFields",
+            "      inducesDependenciesOnBuilderFields();",
+            "",
             "  protected LeafModule leafModule() {",
             "    return leafModule;",
             "  }",
@@ -3233,6 +3288,9 @@
             "public abstract class DaggerAncestor implements Ancestor {",
             "  protected DaggerAncestor() {}",
             "",
+            "  @Override",
+            "  public abstract Leaf.Builder leaf();",
+            "",
             "  protected abstract class LeafImpl extends DaggerLeaf {",
             "    private String inducedInSubclass;",
             "",
@@ -3283,7 +3341,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -3299,7 +3357,7 @@
             "    return new AncestorImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -3477,7 +3535,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -3493,7 +3551,7 @@
             "    return new LeafBuilder();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -3632,7 +3690,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private final RepeatedModule repeatedModule;",
             "",
             "  private DaggerRoot(RepeatedModule repeatedModuleParam) {",
@@ -3652,7 +3710,7 @@
             "    return new LeafBuilder();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private RepeatedModule repeatedModule;",
             "",
             "    private Builder() {}",
@@ -3741,6 +3799,9 @@
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Object bindsWithMissingDependencyInLeaf();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -3781,7 +3842,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -3797,7 +3858,7 @@
             "    return new LeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -3915,7 +3976,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -3931,7 +3992,7 @@
             "    return new LeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -4117,7 +4178,7 @@
             "import javax.inject.Provider;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private DaggerRoot() {}",
             "",
             "  public static Builder builder() {",
@@ -4133,7 +4194,7 @@
             "    return new LeafImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public Root build() {",
@@ -4287,6 +4348,9 @@
             "    return LeafModule_DepOnFooThingFactory.depOnFooThing(getThing());",
             "  }",
             "",
+            "  @Override",
+            "  public abstract WillInduceSetOfRunnable willInduceSetOfRunnable();",
+            "",
             "  protected abstract Thing getThing();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
@@ -4434,6 +4498,9 @@
             GENERATED_ANNOTATION,
             "public abstract class DaggerMaybeLeaf implements MaybeLeaf {",
             "  protected DaggerMaybeLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract Inducer inducer();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -4567,6 +4634,9 @@
             GENERATED_ANNOTATION,
             "public abstract class DaggerLeaf implements Leaf {",
             "  protected DaggerLeaf() {}",
+            "",
+            "  @Override",
+            "  public abstract AtInjectRootScoped shouldBeEffectivelyMissingInLeaf();",
             "}");
     Compilation compilation = compile(filesToCompile.build());
     assertThat(compilation).succeededWithoutWarnings();
@@ -4595,7 +4665,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  protected final class LeafImpl extends DaggerLeaf {",
             "    @Override",
             "    public AtInjectRootScoped shouldBeEffectivelyMissingInLeaf() {",
@@ -4709,7 +4779,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  protected final class LeafImpl extends DaggerLeaf {",
             "    @Override",
             "    public Modified modified() {",
@@ -4928,7 +4998,7 @@
             "import a.Mod;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  private final class HasUnusedModuleLeafBuilder",
             "      extends DaggerHasUnusedModuleLeaf.Builder {",
             "    @Override",
@@ -5085,7 +5155,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  protected final class LeafImpl extends DaggerLeaf {",
             "    private LeafImpl() {}",
             "",
@@ -5386,7 +5456,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root {",
+            "final class DaggerRoot implements Root {",
             "  protected final class AncestorImpl extends DaggerAncestor {",
             "    protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
             "      @Override",
@@ -5536,7 +5606,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerRoot implements Root, CancellationListener {",
+            "final class DaggerRoot implements Root, CancellationListener {",
             "  private Producer<Dependency> dependencyProducer;",
             "  private Producer<Injected> replaceInjectWithProducesProducer;",
             "",
@@ -5598,7 +5668,10 @@
     }
   }
 
-  private static Compilation compile(Iterable<JavaFileObject> files) {
-    return compilerWithOptions(AHEAD_OF_TIME_SUBCOMPONENTS_MODE).compile(files);
+  private static Compilation compile(Iterable<JavaFileObject> files, CompilerMode... modes) {
+    return compilerWithOptions(
+            ObjectArrays.concat(
+                new CompilerMode[] {AHEAD_OF_TIME_SUBCOMPONENTS_MODE}, modes, CompilerMode.class))
+        .compile(files);
   }
 }
diff --git a/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java b/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
new file mode 100644
index 0000000..fda5859
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.lang.model.element.AnnotationMirror;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link TypeProtoConverter}. */
+@RunWith(JUnit4.class)
+public class AnnotationProtoConverterTest {
+  @Rule public CompilationRule compilationRule = new CompilationRule();
+
+  private DaggerElements elements;
+  private DaggerTypes types;
+  private AnnotationProtoConverter annotationProtoConverter;
+
+  @Before
+  public void setUp() {
+    this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+    this.types = new DaggerTypes(compilationRule.getTypes(), elements);
+    this.annotationProtoConverter =
+        new AnnotationProtoConverter(new TypeProtoConverter(types, elements));
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface TestAnnotation {
+    byte b();
+    boolean bool();
+    short s();
+    char c();
+    int i();
+    long l();
+    double d();
+    float f();
+
+    String string();
+    RetentionPolicy enumValue();
+    Class<?> classValue();
+    HasDefaults[] nestedAnnotations();
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface HasDefaults {
+    int value() default 2;
+  }
+
+  @TestAnnotation(
+      b = 1,
+      bool = true,
+      s = 2,
+      c = 'c',
+      i = 4,
+      l = 5,
+      d = 6.0d,
+      f = 7.0f,
+      string = "hello, world",
+      enumValue = RetentionPolicy.CLASS,
+      classValue = AnnotationProtoConverter.class,
+      nestedAnnotations = {@HasDefaults, @HasDefaults(8)})
+  static class TestSubject {}
+
+  @Test
+  public void conversion() {
+    AnnotationMirror actual =
+        getOnlyElement(elements.getTypeElement(TestSubject.class).getAnnotationMirrors());
+    AnnotationMirror translated =
+        annotationProtoConverter.fromProto(AnnotationProtoConverter.toProto(actual));
+    assertThat(AnnotationMirrors.equivalence().equivalent(actual, translated)).isTrue();
+  }
+}
diff --git a/javatests/dagger/internal/codegen/BUILD b/javatests/dagger/internal/codegen/BUILD
index bb902ba..ad214ff 100644
--- a/javatests/dagger/internal/codegen/BUILD
+++ b/javatests/dagger/internal/codegen/BUILD
@@ -33,6 +33,9 @@
         "//java/dagger/internal/codegen:processor",
         "//java/dagger/internal/codegen:validation",
         "//java/dagger/internal/codegen:writing",
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/internal/codegen/serialization",
         "//java/dagger/model",
         "//java/dagger/model/testing",
         "//java/dagger/producers",
diff --git a/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
index 063cd73..2496d8b 100644
--- a/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsInstanceValidationTest.java
@@ -152,12 +152,12 @@
     Compilation compilation = daggerCompiler().compile(bindsFrameworkType);
     assertThat(compilation).failed();
     assertThat(compilation)
-        .hadErrorContaining("@BindsInstance parameters may not be framework types")
+        .hadErrorContaining("@BindsInstance parameters must not be framework types")
         .inFile(bindsFrameworkType)
         .onLine(8);
 
     assertThat(compilation)
-        .hadErrorContaining("@BindsInstance parameters may not be framework types")
+        .hadErrorContaining("@BindsInstance parameters must not be framework types")
         .inFile(bindsFrameworkType)
         .onLine(9);
   }
diff --git a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
index c3de6c0..6ba9e3e 100644
--- a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
@@ -30,7 +30,6 @@
 import java.lang.annotation.Retention;
 import java.util.Collection;
 import javax.inject.Qualifier;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -86,7 +85,6 @@
   }
 
   @Test
-  @Ignore("TODO: @Binds methods do not check explicitly for void")
   public void returnsVoid() {
     assertThatMethod("@Binds abstract void returnsVoid(Object impl);").hasError("void");
   }
diff --git a/javatests/dagger/internal/codegen/CompilerMode.java b/javatests/dagger/internal/codegen/CompilerMode.java
index 4dcc215..23aa312 100644
--- a/javatests/dagger/internal/codegen/CompilerMode.java
+++ b/javatests/dagger/internal/codegen/CompilerMode.java
@@ -23,7 +23,11 @@
 enum CompilerMode {
   DEFAULT_MODE,
   FAST_INIT_MODE("-Adagger.fastInit=enabled"),
-  AHEAD_OF_TIME_SUBCOMPONENTS_MODE("-Adagger.experimentalAheadOfTimeSubcomponents=enabled");
+  AHEAD_OF_TIME_SUBCOMPONENTS_MODE(
+      "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
+      "-Adagger.emitModifiableMetadataAnnotations=disabled"),
+  JAVA7("-source", "7", "-target", "7"),
+  ;
 
   /** Returns the compiler modes as a list of parameters for parameterized tests */
   static final ImmutableList<Object[]> TEST_PARAMETERS =
diff --git a/javatests/dagger/internal/codegen/ComponentBuilderTest.java b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
index 66c54bd..f280bdd 100644
--- a/javatests/dagger/internal/codegen/ComponentBuilderTest.java
+++ b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
@@ -88,7 +88,7 @@
             "import dagger.internal.Preconditions;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private static final class Builder implements TestComponent.Builder {",
             "    private TestModule testModule;",
             "",
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTest.java b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
index 9571b4b..6f4ea8d 100644
--- a/javatests/dagger/internal/codegen/ComponentCreatorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
@@ -96,7 +96,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerSimpleComponent implements SimpleComponent {",
+            "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private static final class Builder implements SimpleComponent.Builder {",
             "    @Override",
             "    public SimpleComponent build() {",
@@ -150,7 +150,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private final TestModule testModule;",
             "",
             "  private DaggerTestComponent(TestModule testModuleParam) {",
@@ -348,7 +348,7 @@
                 IMPORT_GENERATED_ANNOTATION,
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private final Object object;",
                 "",
                 "  private DaggerSimpleComponent(Object objectParam) {",
@@ -449,7 +449,7 @@
                 IMPORT_GENERATED_ANNOTATION,
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private final Integer i;",
                 "",
                 "  private DaggerSimpleComponent(Integer iParam) {",
diff --git a/javatests/dagger/internal/codegen/ComponentFactoryTest.java b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
index 6674126..403498b 100644
--- a/javatests/dagger/internal/codegen/ComponentFactoryTest.java
+++ b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
@@ -87,7 +87,7 @@
             "import dagger.internal.Preconditions;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private static final class Factory implements TestComponent.Factory {",
             "    @Override",
             "    public TestComponent newTestComponent(TestModule mod) {",
diff --git a/javatests/dagger/internal/codegen/ComponentProcessorTest.java b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
index bdf8a87..2b79b6f 100644
--- a/javatests/dagger/internal/codegen/ComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
@@ -186,7 +186,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {")
+                "final class DaggerSimpleComponent implements SimpleComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
@@ -233,7 +233,7 @@
             .addLines(
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private Builder() {}",
                 "",
                 "    public SimpleComponent build() {",
@@ -303,7 +303,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {")
+                "final class DaggerSimpleComponent implements SimpleComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Object someInjectableType = new MemoizedSentinel();",
@@ -423,7 +423,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerOuterType_SimpleComponent",
+                "final class DaggerOuterType_SimpleComponent",
                 "    implements OuterType.SimpleComponent {",
                 "  private DaggerOuterType_SimpleComponent() {}",
                 "",
@@ -507,7 +507,7 @@
                 IMPORT_GENERATED_ANNOTATION,
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final TestModule testModule;",
                 "",
                 "  private DaggerTestComponent(TestModule testModuleParam) {",
@@ -523,7 +523,7 @@
                 "    return new A(getB());",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private TestModule testModule;",
                 "",
                 "    public Builder testModule(TestModule testModule) {",
@@ -611,7 +611,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private B getB() {",
                 "    return TestModule_BFactory.b(new C());",
                 "  }",
@@ -709,8 +709,8 @@
         IMPORT_GENERATED_ANNOTATION,
         "",
         GENERATED_ANNOTATION,
-        "public final class DaggerTestComponent implements TestComponent {",
-        "  public static final class Builder {",
+        "final class DaggerTestComponent implements TestComponent {",
+        "  static final class Builder {",
         "",
         "    @Deprecated",
         "    public Builder testModule(TestModule testModule) {",
@@ -894,7 +894,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "",
             "  private DaggerParent() {}",
             "",
@@ -911,7 +911,7 @@
             "    return ParentModule_NotSubcomponentFactory.notSubcomponent();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "",
             "    private Builder() {}",
             "",
@@ -1008,7 +1008,7 @@
                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  @Override",
                 "  public void inject(SomeInjectedType instance) {",
                 "    injectSomeInjectedType(instance);",
@@ -1065,7 +1065,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerSimpleComponent implements SimpleComponent {",
+            "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private Provider<SimpleComponent> simpleComponentProvider;",
             "",
             "  @SuppressWarnings(\"unchecked\")",
@@ -1130,7 +1130,7 @@
                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  @Override",
                 "  public SomeInjectedType createAndInject() {",
                 "    return injectSomeInjectedType(",
@@ -1199,7 +1199,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerBComponent implements BComponent {")
+                "final class DaggerBComponent implements BComponent {")
             .addLinesIn(
                 DEFAULT_MODE,
                 "  private Provider<A> aProvider;")
@@ -1239,7 +1239,7 @@
             .addLines(
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private AComponent aComponent;",
                 "",
                 "    public Builder aComponent(AComponent aComponent) {",
@@ -1344,7 +1344,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private final TestModule testModule;",
             "  private final other.test.TestModule testModule2;",
             "",
@@ -1365,7 +1365,7 @@
             "    return other.test.TestModule_AFactory.a(testModule2);",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private TestModule testModule;",
             "    private other.test.TestModule testModule2;",
             "",
@@ -1470,7 +1470,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerBComponent implements BComponent {",
+            "final class DaggerBComponent implements BComponent {",
             "  private final AComponent aComponent;",
             "",
             "  private DaggerBComponent(AComponent aComponentParam) {",
@@ -1557,7 +1557,7 @@
                 IMPORT_GENERATED_ANNOTATION,
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private B getB() {",
                 "    return new B(new C());",
                 "  }",
@@ -1638,7 +1638,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerSimpleComponent implements SimpleComponent {",
+            "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private DaggerSimpleComponent() {}",
             "",
             "  public static Builder builder() {",
@@ -1654,7 +1654,7 @@
             "    return new SomeInjectableType();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public SimpleComponent build() {",
@@ -1708,7 +1708,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerSimpleComponent implements SimpleComponent {",
+            "final class DaggerSimpleComponent implements SimpleComponent {",
             "  @Override",
             "  public SomeInjectableType someInjectableType() {",
             "    return new SomeInjectableType();",
@@ -2083,7 +2083,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private DaggerParent() {",
             "  }",
             "",
@@ -2095,7 +2095,7 @@
             "    return new Builder().build();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    @Deprecated",
@@ -2246,7 +2246,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public String nonNullableString() {",
                 "    return TestModule_NonNullableStringFactory.nonNullableString());",
@@ -2337,7 +2337,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public Integer nonNullableInteger() {",
                 "    return TestModule.primitiveInteger();",
@@ -2413,7 +2413,7 @@
             "test.DaggerParent",
             "package test;",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private String getString() {",
             "    return TestModule_StringFactory.string(numberProvider.get());",
             "  }",
@@ -2483,7 +2483,7 @@
             "test.DaggerParent",
             "package test;",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private final class ChildImpl implements Child {",
             "    @Override",
             "    public String string() {",
@@ -2550,7 +2550,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Injected injected() {",
             // Ensure that the qualified @Provides method is used. It's also probably more likely
@@ -2623,7 +2623,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public String unqualified() {",
             // Ensure that the unqualified @Provides method is used. It's also probably more likely
@@ -2654,6 +2654,50 @@
         .containsElementsIn(generatedComponent);
   }
 
+  @Test
+  public void publicComponentType() {
+    JavaFileObject publicComponent =
+        JavaFileObjects.forSourceLines(
+            "test.PublicComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "public interface PublicComponent {}");
+    Compilation compilation = daggerCompiler().compile(publicComponent);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerPublicComponent")
+        .hasSourceEquivalentTo(
+            JavaFileObjects.forSourceLines(
+                "test.DaggerPublicComponent",
+                "package test;",
+                "",
+                IMPORT_GENERATED_ANNOTATION,
+                "",
+                GENERATED_ANNOTATION,
+                "public final class DaggerPublicComponent implements PublicComponent {",
+                "  private DaggerPublicComponent() {}",
+                "",
+                "  public static Builder builder() {",
+                "    return new Builder();",
+                "  }",
+                "",
+                "  public static PublicComponent create() {",
+                "    return new Builder().build();",
+                "  }",
+                "",
+                "  public static final class Builder {",
+                "    private Builder() {}",
+                "",
+                "    public PublicComponent build() {",
+                "      return new DaggerPublicComponent();",
+                "    }",
+                "  }",
+                "}"));
+  }
+
   /**
    * A {@link ComponentProcessor} that excludes elements using a {@link Predicate}.
    */
diff --git a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
index 3375d7a..85e2d7b 100644
--- a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
+++ b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
@@ -77,7 +77,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final Integer i;",
                 "  private final List<String> list;",
                 "",
@@ -175,7 +175,7 @@
             "import other.OtherPackageModule_LFactory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private final ParentModule parentModule;",
             "  private final OtherPackageModule otherPackageModule;",
             "",
@@ -248,7 +248,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final Dep dep;",
                 "",
                 "  private DaggerTestComponent(Dep depParam) {",
@@ -362,7 +362,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final ParentModule parentModule;",
                 "",
                 "  private DaggerTestComponent(ParentModule parentModuleParam) {",
@@ -390,7 +390,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final ParentModule parentModule;",
                 "",
                 "  private DaggerTestComponent(ParentModule parentModuleParam) {",
diff --git a/javatests/dagger/internal/codegen/ComponentValidationTest.java b/javatests/dagger/internal/codegen/ComponentValidationTest.java
index 2208b4a..169a318 100644
--- a/javatests/dagger/internal/codegen/ComponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/ComponentValidationTest.java
@@ -23,7 +23,6 @@
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
 import javax.tools.JavaFileObject;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -139,9 +138,7 @@
     assertThat(compilation).hadErrorContaining("wildcard type").inFile(testComponent).onLine(10);
   }
 
-  // TODO(b/34107586): Fix and enable test.
   @Test
-  @Ignore
   public void invalidComponentDependencies() {
     JavaFileObject testComponent =
         JavaFileObjects.forSourceLines(
diff --git a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
index c01da44..1fcf7bc 100644
--- a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
+++ b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
@@ -74,6 +74,7 @@
     }
   }
 
+  private final String actual;
   private final ImmutableList.Builder<String> imports =
       new ImmutableList.Builder<String>()
           .add(
@@ -89,6 +90,7 @@
 
   private DaggerModuleMethodSubject(FailureMetadata failureMetadata, String subject) {
     super(failureMetadata, subject);
+    this.actual = subject;
   }
 
   /**
@@ -155,7 +157,7 @@
   }
 
   private int methodLine(String source) {
-    String beforeMethod = source.substring(0, source.indexOf(actual()));
+    String beforeMethod = source.substring(0, source.indexOf(actual));
     int methodLine = 1;
     for (int nextNewlineIndex = beforeMethod.indexOf('\n');
         nextNewlineIndex >= 0;
@@ -174,7 +176,7 @@
       writer.println(importLine);
     }
     writer.println();
-    writer.printf(declaration, "TestModule", "\n" + actual() + "\n");
+    writer.printf(declaration, "TestModule", "\n" + actual + "\n");
     writer.println();
     return stringWriter.toString();
   }
diff --git a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
index fd20350..2f4aecf 100644
--- a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
@@ -145,7 +145,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     FAST_INIT_MODE,
                     "  private volatile Object regularScoped = new MemoizedSentinel();",
@@ -224,7 +224,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     FAST_INIT_MODE,
                     "  private volatile Object regularScoped = new MemoizedSentinel();",
@@ -300,7 +300,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     FAST_INIT_MODE,
                     "  private volatile Object regularScoped = new MemoizedSentinel();",
@@ -403,7 +403,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @SuppressWarnings(\"rawtypes\")",
@@ -512,7 +512,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @SuppressWarnings(\"rawtypes\")",
@@ -603,7 +603,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @Override",
@@ -705,7 +705,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @Override",
@@ -811,7 +811,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerRequestsSubtypeAsProvider",
+                    "final class DaggerRequestsSubtypeAsProvider",
                     "    implements RequestsSubtypeAsProvider {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -904,7 +904,7 @@
                     "package test;",
                     "",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  private Provider<String> provideStringProvider;",
diff --git a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
index a24b7fe..4a4e0a5 100644
--- a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
+++ b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
@@ -18,10 +18,12 @@
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.endsWithMessage;
 import static dagger.internal.codegen.TestUtils.message;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
+import java.util.regex.Pattern;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,45 +31,46 @@
 
 @RunWith(JUnit4.class)
 public class DependencyCycleValidationTest {
-  @Test public void cyclicDependency() {
-    JavaFileObject component =
-        JavaFileObjects.forSourceLines(
-            "test.Outer",
-            "package test;",
-            "",
-            "import dagger.Binds;",
-            "import dagger.Component;",
-            "import dagger.Module;",
-            "import dagger.Provides;",
-            "import javax.inject.Inject;",
-            "",
-            "final class Outer {",
-            "  static class A {",
-            "    @Inject A(C cParam) {}",
-            "  }",
-            "",
-            "  static class B {",
-            "    @Inject B(A aParam) {}",
-            "  }",
-            "",
-            "  static class C {",
-            "    @Inject C(B bParam) {}",
-            "  }",
-            "",
-            "  @Module",
-            "  interface MModule {",
-            "    @Binds Object object(C c);",
-            "  }",
-            "",
-            "  @Component",
-            "  interface CComponent {",
-            "    C getC();",
-            "  }",
-            "}");
+  private static final JavaFileObject SIMPLE_CYCLIC_DEPENDENCY =
+      JavaFileObjects.forSourceLines(
+          "test.Outer",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Component;",
+          "import dagger.Module;",
+          "import dagger.Provides;",
+          "import javax.inject.Inject;",
+          "",
+          "final class Outer {",
+          "  static class A {",
+          "    @Inject A(C cParam) {}",
+          "  }",
+          "",
+          "  static class B {",
+          "    @Inject B(A aParam) {}",
+          "  }",
+          "",
+          "  static class C {",
+          "    @Inject C(B bParam) {}",
+          "  }",
+          "",
+          "  @Module",
+          "  interface MModule {",
+          "    @Binds Object object(C c);",
+          "  }",
+          "",
+          "  @Component",
+          "  interface CComponent {",
+          "    C getC();",
+          "  }",
+          "}");
 
-    Compilation compilation =
-        daggerCompiler().withOptions("-Adagger.moduleBindingValidation=ERROR").compile(component);
+  @Test
+  public void cyclicDependency() {
+    Compilation compilation = daggerCompiler().compile(SIMPLE_CYCLIC_DEPENDENCY);
     assertThat(compilation).failed();
+
     assertThat(compilation)
         .hadErrorContaining(
             message(
@@ -80,21 +83,44 @@
                 "        test.Outer.C(bParam)",
                 "    test.Outer.C is provided at",
                 "        test.Outer.CComponent.getC()"))
-        .inFile(component)
+        .inFile(SIMPLE_CYCLIC_DEPENDENCY)
         .onLineContaining("interface CComponent");
 
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void cyclicDependencyWithModuleBindingValidation() {
+    // Cycle errors should not show a dependency trace to an entry point when doing full binding
+    // graph validation. So ensure that the message doesn't end with "test.Outer.C is provided at
+    // test.Outer.CComponent.getC()", as the previous test's message does.
+    Pattern moduleBindingValidationError =
+        endsWithMessage(
+            "Found a dependency cycle:",
+            "    test.Outer.C is injected at",
+            "        test.Outer.A(cParam)",
+            "    test.Outer.A is injected at",
+            "        test.Outer.B(aParam)",
+            "    test.Outer.B is injected at",
+            "        test.Outer.C(bParam)");
+
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(SIMPLE_CYCLIC_DEPENDENCY);
+    assertThat(compilation).failed();
+
     assertThat(compilation)
-        .hadErrorContaining(
-            message(
-                "Found a dependency cycle:",
-                "    test.Outer.C is injected at",
-                "        test.Outer.A(cParam)",
-                "    test.Outer.A is injected at",
-                "        test.Outer.B(aParam)",
-                "    test.Outer.B is injected at",
-                "        test.Outer.C(bParam)"))
-        .inFile(component)
+        .hadErrorContainingMatch(moduleBindingValidationError)
+        .inFile(SIMPLE_CYCLIC_DEPENDENCY)
         .onLineContaining("interface MModule");
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(moduleBindingValidationError)
+        .inFile(SIMPLE_CYCLIC_DEPENDENCY)
+        .onLineContaining("interface CComponent");
+
+    assertThat(compilation).hadErrorCount(2);
   }
 
   @Test public void cyclicDependencyNotIncludingEntryPoint() {
diff --git a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
index f5863a3..14a6fb9 100644
--- a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
+++ b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
@@ -33,19 +33,19 @@
 @RunWith(Parameterized.class)
 public class DuplicateBindingsValidationTest {
 
-  @Parameters(name = "moduleBindingValidation={0}")
+  @Parameters(name = "fullBindingGraphValidation={0}")
   public static ImmutableList<Object[]> parameters() {
     return ImmutableList.copyOf(new Object[][] {{false}, {true}});
   }
 
-  private final boolean moduleBindingValidation;
+  private final boolean fullBindingGraphValidation;
 
-  public DuplicateBindingsValidationTest(boolean moduleBindingValidation) {
-    this.moduleBindingValidation = moduleBindingValidation;
+  public DuplicateBindingsValidationTest(boolean fullBindingGraphValidation) {
+    this.fullBindingGraphValidation = fullBindingGraphValidation;
   }
 
   @Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
-    assumeFalse(moduleBindingValidation);
+    assumeFalse(fullBindingGraphValidation);
 
     JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
         "package test;",
@@ -82,7 +82,7 @@
         "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -134,7 +134,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -145,7 +145,7 @@
         .inFile(component)
         .onLineContaining("interface TestComponent");
 
-    if (moduleBindingValidation) {
+    if (fullBindingGraphValidation) {
       assertThat(compilation)
           .hadErrorContaining(
               message(
@@ -157,7 +157,7 @@
     }
 
     // The duplicate bindngs are also requested from B, but we don't want to report them again.
-    assertThat(compilation).hadErrorCount(moduleBindingValidation ? 2 : 1);
+    assertThat(compilation).hadErrorCount(fullBindingGraphValidation ? 2 : 1);
   }
 
   @Test
@@ -200,7 +200,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -211,7 +211,7 @@
         .inFile(component)
         .onLineContaining("interface TestComponent");
 
-    if (moduleBindingValidation) {
+    if (fullBindingGraphValidation) {
       assertThat(compilation)
           .hadErrorContaining(
               message(
@@ -268,7 +268,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -284,7 +284,7 @@
                 "        @Provides Set<String> test.Outer.TestModule2.stringSet()"))
         .inFile(component)
         .onLineContaining(
-            moduleBindingValidation ? "class TestModule3" : "interface TestComponent");
+            fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
   }
 
   @Test
@@ -337,7 +337,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -356,7 +356,7 @@
                 "        @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
         .inFile(component)
         .onLineContaining(
-            moduleBindingValidation ? "class TestModule3" : "interface TestComponent");
+            fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
   }
 
   @Test
@@ -394,7 +394,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -407,7 +407,7 @@
                 "        @Provides Set<String> test.Outer.TestModule2.stringSet()"))
         .inFile(component)
         .onLineContaining(
-            moduleBindingValidation ? "class TestModule3" : "interface TestComponent");
+            fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
   }
 
   @Test
@@ -447,7 +447,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -461,7 +461,7 @@
                 "        @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
         .inFile(component)
         .onLineContaining(
-            moduleBindingValidation ? "class TestModule3" : "interface TestComponent");
+            fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
   }
 
   @Test public void duplicateBindings_TruncateAfterLimit() {
@@ -574,7 +574,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler().withOptions(moduleBindingValidationOption()).compile(component);
+        daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -592,7 +592,7 @@
                 "    @Provides test.Outer.A test.Outer.Module10.provideA()",
                 "    and 2 others"))
         .inFile(component)
-        .onLineContaining(moduleBindingValidation ? "class Modules" : "interface TestComponent");
+        .onLineContaining(fullBindingGraphValidation ? "class Modules" : "interface TestComponent");
   }
 
   @Test
@@ -647,7 +647,7 @@
 
     Compilation compilation =
         daggerCompiler()
-            .withOptions(moduleBindingValidationOption())
+            .withOptions(fullBindingGraphValidationOption())
             .compile(aComponent, bComponent);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -657,7 +657,7 @@
                 "    @Provides Object test.A.AModule.abConflict()",
                 "    @Provides Object test.B.BModule.abConflict()"))
         .inFile(aComponent)
-        .onLineContaining(moduleBindingValidation ? "class AModule" : "interface A {");
+        .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
   }
 
   @Test
@@ -728,7 +728,7 @@
 
     Compilation compilation =
         daggerCompiler()
-            .withOptions(moduleBindingValidationOption())
+            .withOptions(fullBindingGraphValidationOption())
             .compile(aComponent, bComponent, cComponent);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -738,7 +738,7 @@
                 "    @Provides Object test.A.AModule.acConflict()",
                 "    @Provides Object test.C.CModule.acConflict()"))
         .inFile(aComponent)
-        .onLineContaining(moduleBindingValidation ? "class AModule" : "interface A {");
+        .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
   }
 
   @Test
@@ -804,7 +804,7 @@
 
     Compilation compilation =
         daggerCompiler()
-            .withOptions(moduleBindingValidationOption())
+            .withOptions(fullBindingGraphValidationOption())
             .compile(aComponent, bComponent, cComponent);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -813,13 +813,13 @@
                 "java.lang.Object is bound multiple times:",
                 "    @Provides Object test.B.BModule.bcConflict()",
                 "    @Provides Object test.C.CModule.bcConflict()"))
-        .inFile(moduleBindingValidation ? bComponent : aComponent)
-        .onLineContaining(moduleBindingValidation ? "class BModule" : "interface A {");
+        .inFile(fullBindingGraphValidation ? bComponent : aComponent)
+        .onLineContaining(fullBindingGraphValidation ? "class BModule" : "interface A {");
   }
 
   @Test
   public void childProvidesConflictsWithParentInjects() {
-    assumeFalse(moduleBindingValidation);
+    assumeFalse(fullBindingGraphValidation);
 
     JavaFileObject foo =
         JavaFileObjects.forSourceLines(
@@ -992,7 +992,7 @@
 
     Compilation compilation =
         daggerCompiler()
-            .withOptions("-Adagger.nullableValidation=WARNING", moduleBindingValidationOption())
+            .withOptions("-Adagger.nullableValidation=WARNING", fullBindingGraphValidationOption())
             .compile(parentConflictsWithChild, child);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -1004,11 +1004,13 @@
                     + " test.ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
         .inFile(parentConflictsWithChild)
         .onLineContaining(
-            moduleBindingValidation ? "class ParentModule" : "interface ParentConflictsWithChild");
+            fullBindingGraphValidation
+                ? "class ParentModule"
+                : "interface ParentConflictsWithChild");
   }
 
-  private String moduleBindingValidationOption() {
-    return "-Adagger.moduleBindingValidation=" + (moduleBindingValidation ? "ERROR" : "NONE");
+  private String fullBindingGraphValidationOption() {
+    return "-Adagger.fullBindingGraphValidation=" + (fullBindingGraphValidation ? "ERROR" : "NONE");
   }
 
   @Test
diff --git a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
index f8a9ab5..58ddb82 100644
--- a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
+++ b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
@@ -85,7 +85,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerSimpleComponent implements SimpleComponent {",
+            "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private DaggerSimpleComponent() {}",
             "",
             "  public static Builder builder() {",
@@ -101,7 +101,7 @@
             "    return new DependsOnInjected(new InjectedType());",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {",
             "    }",
             "",
@@ -186,7 +186,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private volatile Object scopedType = new MemoizedSentinel();",
                 "  private volatile Provider<DependsOnScoped> dependsOnScopedProvider;",
                 "",
@@ -232,7 +232,7 @@
                 "    return new NeedsProvider(getDependsOnScopedProvider());",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private Builder() {}",
                 "",
                 "    public SimpleComponent build() {",
@@ -268,7 +268,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private Provider<ScopedType> scopedTypeProvider;",
                 "  private Provider<DependsOnScoped> dependsOnScopedProvider;",
                 "",
@@ -296,7 +296,7 @@
                 "    return new NeedsProvider(dependsOnScopedProvider);",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private Builder() {",
                 "    }",
                 "",
@@ -380,7 +380,7 @@
                 IMPORT_GENERATED_ANNOTATION,
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private volatile Object scopedType = new MemoizedSentinel();",
                 "",
                 "  private DaggerSimpleComponent() {}",
@@ -412,7 +412,7 @@
                 "    return new SubImpl();",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private Builder() {}",
                 "",
                 "    public SimpleComponent build() {",
@@ -441,7 +441,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerSimpleComponent implements SimpleComponent {",
+                "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private Provider<ScopedType> scopedTypeProvider;",
                 "",
                 "  private DaggerSimpleComponent() {",
@@ -466,7 +466,7 @@
                 "    return new SubImpl();",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private Builder() {}",
                 "",
                 "    public SimpleComponent build() {",
diff --git a/javatests/dagger/internal/codegen/FrameworkFieldTest.java b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
index 6905f6a..cbc8c03 100644
--- a/javatests/dagger/internal/codegen/FrameworkFieldTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
@@ -17,10 +17,10 @@
 package dagger.internal.codegen;
 
 import static com.google.common.truth.Truth.assertThat;
-import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTOR;
-import static dagger.internal.codegen.TypeNames.PROVIDER;
-import static dagger.internal.codegen.TypeNames.membersInjectorOf;
-import static dagger.internal.codegen.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTOR;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
 
 import com.google.testing.compile.CompilationRule;
 import com.squareup.javapoet.ClassName;
diff --git a/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
new file mode 100644
index 0000000..944e307
--- /dev/null
+++ b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 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 static dagger.internal.codegen.TestUtils.endsWithMessage;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.regex.Pattern;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class FullBindingGraphValidationTest {
+  private static final JavaFileObject MODULE_WITH_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.ModuleWithErrors",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module",
+          "interface ModuleWithErrors {",
+          "  @Binds Object object1(String string);",
+          "  @Binds Object object2(Long l);",
+          "  @Binds Number missingDependency(Integer i);",
+          "}");
+
+  // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+  private static final Pattern MODULE_WITH_ERRORS_MESSAGE =
+      endsWithMessage(
+          "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+          "    @Binds Object test.ModuleWithErrors.object1(String)",
+          "    @Binds Object test.ModuleWithErrors.object2(Long)");
+
+  @Test
+  public void moduleWithErrors_validationTypeNone() {
+    Compilation compilation = daggerCompiler().compile(MODULE_WITH_ERRORS);
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void moduleWithErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(MODULE_WITH_ERRORS);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithErrors");
+
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void moduleWithErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(MODULE_WITH_ERRORS);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithErrors");
+
+    assertThat(compilation).hadWarningCount(1);
+  }
+
+  private static final JavaFileObject INCLUDES_MODULE_WITH_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.IncludesModuleWithErrors",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module(includes = ModuleWithErrors.class)",
+          "interface IncludesModuleWithErrors {}");
+
+  @Test
+  public void includesModuleWithErrors_validationTypeNone() {
+    Compilation compilation =
+        daggerCompiler().compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void includesModuleWithErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithErrors");
+
+    assertThat(compilation)
+        .hadErrorContainingMatch("test.ModuleWithErrors has errors")
+        .inFile(INCLUDES_MODULE_WITH_ERRORS)
+        .onLineContaining("ModuleWithErrors.class");
+
+    assertThat(compilation).hadErrorCount(2);
+  }
+
+  @Test
+  public void includesModuleWithErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithErrors");
+
+    // TODO(b/130284666)
+    assertThat(compilation)
+        .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+        .inFile(INCLUDES_MODULE_WITH_ERRORS)
+        .onLineContaining("interface IncludesModuleWithErrors");
+
+    assertThat(compilation).hadWarningCount(2);
+  }
+
+  private static final JavaFileObject A_MODULE =
+      JavaFileObjects.forSourceLines(
+          "test.AModule",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module",
+          "interface AModule {",
+          "  @Binds Object object(String string);",
+          "}");
+
+  private static final JavaFileObject COMBINED_WITH_A_MODULE_HAS_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.CombinedWithAModuleHasErrors",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module(includes = AModule.class)",
+          "interface CombinedWithAModuleHasErrors {",
+          "  @Binds Object object(Long l);",
+          "}");
+
+  // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+  private static final Pattern COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE =
+      endsWithMessage(
+          "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+          "    @Binds Object test.AModule.object(String)",
+          "    @Binds Object test.CombinedWithAModuleHasErrors.object(Long)");
+
+  @Test
+  public void moduleIncludingModuleWithCombinedErrors_validationTypeNone() {
+    Compilation compilation = daggerCompiler().compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void moduleIncludingModuleWithCombinedErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
+        .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
+        .onLineContaining("interface CombinedWithAModuleHasErrors");
+
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void moduleIncludingModuleWithCombinedErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
+        .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
+        .onLineContaining("interface CombinedWithAModuleHasErrors");
+
+    assertThat(compilation).hadWarningCount(1);
+  }
+
+  private static final JavaFileObject SUBCOMPONENT_WITH_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.SubcomponentWithErrors",
+          "package test;",
+          "",
+          "import dagger.BindsInstance;",
+          "import dagger.Subcomponent;",
+          "",
+          "@Subcomponent(modules = AModule.class)",
+          "interface SubcomponentWithErrors {",
+          "  @Subcomponent.Builder",
+          "  interface Builder {",
+          "    @BindsInstance Builder object(Object object);",
+          "    SubcomponentWithErrors build();",
+          "  }",
+          "}");
+
+  // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+  private static final Pattern SUBCOMPONENT_WITH_ERRORS_MESSAGE =
+      endsWithMessage(
+          "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+          "    @Binds Object test.AModule.object(String)",
+          "    @BindsInstance test.SubcomponentWithErrors.Builder"
+              + " test.SubcomponentWithErrors.Builder.object(Object)");
+
+  @Test
+  public void subcomponentWithErrors_validationTypeNone() {
+    Compilation compilation = daggerCompiler().compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void subcomponentWithErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface SubcomponentWithErrors");
+
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void subcomponentWithErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface SubcomponentWithErrors");
+
+    assertThat(compilation).hadWarningCount(1);
+  }
+
+  private static final JavaFileObject MODULE_WITH_SUBCOMPONENT_WITH_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.ModuleWithSubcomponentWithErrors",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module(subcomponents = SubcomponentWithErrors.class)",
+          "interface ModuleWithSubcomponentWithErrors {}");
+
+  @Test
+  public void moduleWithSubcomponentWithErrors_validationTypeNone() {
+    Compilation compilation =
+        daggerCompiler()
+            .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void moduleWithSubcomponentWithErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithSubcomponentWithErrors");
+
+    // TODO(b/130283677)
+    assertThat(compilation)
+        .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface SubcomponentWithErrors");
+
+    assertThat(compilation).hadErrorCount(2);
+  }
+
+  @Test
+  public void moduleWithSubcomponentWithErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface ModuleWithSubcomponentWithErrors");
+
+    // TODO(b/130283677)
+    assertThat(compilation)
+        .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+        .inFile(SUBCOMPONENT_WITH_ERRORS)
+        .onLineContaining("interface SubcomponentWithErrors");
+
+    assertThat(compilation).hadWarningCount(2);
+  }
+
+  private static final JavaFileObject A_SUBCOMPONENT =
+      JavaFileObjects.forSourceLines(
+          "test.ASubcomponent",
+          "package test;",
+          "",
+          "import dagger.BindsInstance;",
+          "import dagger.Subcomponent;",
+          "",
+          "@Subcomponent(modules = AModule.class)",
+          "interface ASubcomponent {",
+          "  @Subcomponent.Builder",
+          "  interface Builder {",
+          "    ASubcomponent build();",
+          "  }",
+          "}");
+
+  private static final JavaFileObject COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS =
+      JavaFileObjects.forSourceLines(
+          "test.CombinedWithASubcomponentHasErrors",
+          "package test;",
+          "",
+          "import dagger.Binds;",
+          "import dagger.Module;",
+          "",
+          "@Module(subcomponents = ASubcomponent.class)",
+          "interface CombinedWithASubcomponentHasErrors {",
+          "  @Binds Object object(Number number);",
+          "}");
+
+  // Make sure the error doesn't show other bindings or a dependency trace afterwards.
+  private static final Pattern COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE =
+      endsWithMessage(
+          "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
+          "    @Binds Object test.AModule.object(String)",
+          "    @Binds Object test.CombinedWithASubcomponentHasErrors.object(Number)");
+
+  @Test
+  public void moduleWithSubcomponentWithCombinedErrors_validationTypeNone() {
+    Compilation compilation =
+        daggerCompiler().compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
+  @Test
+  public void moduleWithSubcomponentWithCombinedErrors_validationTypeError() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
+        .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
+        .onLineContaining("interface CombinedWithASubcomponentHasErrors");
+
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void moduleWithSubcomponentWithCombinedErrors_validationTypeWarning() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+            .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
+        .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
+        .onLineContaining("interface CombinedWithASubcomponentHasErrors");
+
+    assertThat(compilation).hadWarningCount(1);
+  }
+
+  @Test
+  public void bothAliasesDifferentValues() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions(
+                "-Adagger.moduleBindingValidation=NONE",
+                "-Adagger.fullBindingGraphValidation=ERROR")
+            .compile(MODULE_WITH_ERRORS);
+
+    assertThat(compilation).failed();
+
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Only one of the equivalent options "
+                + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
+                + " should be used; prefer -Adagger.fullBindingGraphValidation");
+
+    assertThat(compilation).hadErrorCount(1);
+  }
+
+  @Test
+  public void bothAliasesSameValue() {
+    Compilation compilation =
+        daggerCompiler()
+            .withOptions(
+                "-Adagger.moduleBindingValidation=NONE", "-Adagger.fullBindingGraphValidation=NONE")
+            .compile(MODULE_WITH_ERRORS);
+
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .hadWarningContaining(
+            "Only one of the equivalent options "
+                + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
+                + " should be used; prefer -Adagger.fullBindingGraphValidation");
+  }
+}
diff --git a/javatests/dagger/internal/codegen/GeneratedLines.java b/javatests/dagger/internal/codegen/GeneratedLines.java
index ce8fdab..2aa17ac 100644
--- a/javatests/dagger/internal/codegen/GeneratedLines.java
+++ b/javatests/dagger/internal/codegen/GeneratedLines.java
@@ -16,7 +16,7 @@
 
 package dagger.internal.codegen;
 
-import static dagger.internal.codegen.CodeBlocks.stringLiteral;
+import static dagger.internal.codegen.javapoet.CodeBlocks.stringLiteral;
 
 import com.squareup.javapoet.CodeBlock;
 
@@ -27,7 +27,7 @@
   public static final String GENERATED_ANNOTATION =
      "@Generated("
         + "value = \"dagger.internal.codegen.ComponentProcessor\", "
-        + "comments = \"https://google.github.io/dagger\")";
+        + "comments = \"https://dagger.dev\")";
 
   public static final String IMPORT_GENERATED_ANNOTATION =
       isBeforeJava9()
diff --git a/javatests/dagger/internal/codegen/KeyFactoryTest.java b/javatests/dagger/internal/codegen/KeyFactoryTest.java
index 69dfeaa..d526ec9 100644
--- a/javatests/dagger/internal/codegen/KeyFactoryTest.java
+++ b/javatests/dagger/internal/codegen/KeyFactoryTest.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 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;
@@ -25,6 +26,8 @@
 import com.google.testing.compile.CompilationRule;
 import dagger.Module;
 import dagger.Provides;
+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;
@@ -61,7 +64,9 @@
   @Before public void setUp() {
     this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
     this.types = new DaggerTypes(compilationRule.getTypes(), elements);
-    this.keyFactory = new KeyFactory(types, elements);
+    TypeProtoConverter typeProtoConverter = new TypeProtoConverter(types, elements);
+    this.keyFactory = new KeyFactory(
+        types, elements, typeProtoConverter, new AnnotationProtoConverter(typeProtoConverter));
   }
 
   @Test public void forInjectConstructorWithResolvedType() {
@@ -250,7 +255,7 @@
     assertThat(intType.getKind().isPrimitive()).isTrue();
     TypeMirror integerType = integerMethod.getReturnType();
     assertThat(integerType.getKind().isPrimitive()).isFalse();
-    assertThat(types.isSameType(intType, integerType)).named("type equality").isFalse();
+    assertWithMessage("type equality").that(types.isSameType(intType, integerType)).isFalse();
     Key intKey = keyFactory.forProvidesMethod(intMethod, primitiveHolder);
     Key integerKey = keyFactory.forProvidesMethod(integerMethod, boxedPrimitiveHolder);
     assertThat(intKey).isEqualTo(integerKey);
diff --git a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index 4cb93a9..ad48712 100644
--- a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -129,7 +129,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final MapModuleOne mapModuleOne;",
                 "  private final MapModuleTwo mapModuleTwo;",
                 "  private volatile Provider<Handler> provideAdminHandlerProvider;",
@@ -206,7 +206,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Handler> provideAdminHandlerProvider;",
                 "  private Provider<Handler> provideLoginHandlerProvider;",
                 "  private Provider<Map<PathEnum, Provider<Handler>>>",
@@ -358,7 +358,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Map<Class<?>, Integer>> mapOfClassOfAndIntegerProvider;",
                 "",
                 "  @SuppressWarnings(\"rawtypes\")",
@@ -537,7 +537,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final MapModuleOne mapModuleOne;",
                 "  private final MapModuleTwo mapModuleTwo;",
                 "  private volatile Provider<Handler> provideAdminHandlerProvider;",
@@ -614,7 +614,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Handler> provideAdminHandlerProvider;",
                 "  private Provider<Handler> provideLoginHandlerProvider;",
                 "  private Provider<Map<String, Provider<Handler>>>",
@@ -738,7 +738,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final MapModuleOne mapModuleOne;",
                 "  private final MapModuleTwo mapModuleTwo;",
                 "  private volatile Provider<Handler> provideAdminHandlerProvider;",
@@ -824,7 +824,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Handler> provideAdminHandlerProvider;",
                 "  private Provider<Handler> provideLoginHandlerProvider;",
                 "  private Provider<Map<WrappedClassKey, Provider<Handler>>>",
@@ -952,7 +952,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final MapModuleOne mapModuleOne;",
                 "  private final MapModuleTwo mapModuleTwo;",
                 "  private volatile Provider<Map<PathEnum, Handler>>",
@@ -1003,7 +1003,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Handler> provideAdminHandlerProvider;",
                 "  private Provider<Handler> provideLoginHandlerProvider;",
                 "  private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
@@ -1081,7 +1081,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private final MapModule mapModule;",
             "",
             "  @Override",
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
index 174ada8..11f6bc1 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
@@ -93,7 +93,7 @@
                 "import dagger.internal.MapBuilder;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {")
+                "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Provider<Integer> provideIntProvider;",
@@ -275,7 +275,7 @@
             "import other.UsesInaccessible_Factory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public UsesInaccessible usesInaccessible() {",
             "    return UsesInaccessible_Factory.newInstance(",
@@ -338,7 +338,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
             "  private final class ChildImpl implements Child {",
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
index 5b0ab6f..aa1a715 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
@@ -19,6 +19,7 @@
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
 import static dagger.internal.codegen.Compilers.daggerCompiler;
 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
 
@@ -128,7 +129,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {")
+                "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Provider<Integer> provideIntProvider;",
@@ -194,7 +195,7 @@
                 DEFAULT_MODE, //
                 "        0, MapModule_ProvideIntFactory.create());")
             .addLinesIn(
-                FAST_INIT_MODE,
+                FAST_INIT_MODE, //
                 "        0, getProvideIntProvider());")
             .addLines(
                 "  }",
@@ -403,7 +404,7 @@
             "import other.UsesInaccessible_Factory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public UsesInaccessible usesInaccessible() {",
             "    return UsesInaccessible_Factory.newInstance((Map) ImmutableMap.of());",
@@ -466,7 +467,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
             "  private final class ChildImpl implements Child {",
@@ -522,7 +523,7 @@
             "import dagger.producers.internal.CancellationListener;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent, "
+            "final class DaggerTestComponent implements TestComponent, "
                 + "CancellationListener {",
             "  @Override",
             "  public ListenableFuture<Map<String, String>> stringMap() {",
@@ -534,8 +535,10 @@
             "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(
+                compilerMode
+                , CompilerMode.JAVA7
+                )
             .compile(mapModuleFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
index b2ed614..04b0986 100644
--- a/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
@@ -71,7 +71,7 @@
     assertThat(compilation).hadErrorCount(1);
 
     compilation =
-        daggerCompiler().withOptions("-Adagger.moduleBindingValidation=ERROR").compile(module);
+        daggerCompiler().withOptions("-Adagger.fullBindingGraphValidation=ERROR").compile(module);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
@@ -213,7 +213,7 @@
 
     compilation =
         daggerCompiler()
-            .withOptions("-Adagger.moduleBindingValidation=ERROR")
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
             .compile(module, stringKeyTwoFile);
     assertThat(compilation).failed();
     assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/MembersInjectionTest.java b/javatests/dagger/internal/codegen/MembersInjectionTest.java
index 88a7706..edaedaf 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionTest.java
@@ -88,7 +88,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Child child() {",
             "    return new Child();",
@@ -148,7 +148,7 @@
             "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Child child() {",
             "    return injectChild(Child_Factory.newInstance());",
@@ -1277,7 +1277,7 @@
             "import other.UsesInaccessible_Factory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private Object getInaccessible() {",
             "    return injectInaccessible(Inaccessible_Factory.newInstance());",
             "  }",
@@ -1376,7 +1376,7 @@
                 "import other.UsesInaccessibles_MembersInjector;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {")
+                "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Object listOfInaccessible = new MemoizedSentinel();",
@@ -1506,7 +1506,7 @@
             "import other.Supertype_MembersInjector;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private Object getSubtype() {",
             "    return injectSubtype(Subtype_Factory.newInstance());",
             "  }",
diff --git a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
index 16db6cf..687c29a 100644
--- a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
+++ b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
@@ -23,6 +23,8 @@
 import com.google.common.collect.Iterables;
 import com.google.testing.compile.CompilationRule;
 import dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.InnerClass;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.inject.Singleton;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
diff --git a/javatests/dagger/internal/codegen/ModuleBindingValidationTest.java b/javatests/dagger/internal/codegen/ModuleBindingValidationTest.java
deleted file mode 100644
index 0121072..0000000
--- a/javatests/dagger/internal/codegen/ModuleBindingValidationTest.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.TestUtils.message;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import java.util.regex.Pattern;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class ModuleBindingValidationTest {
-  private static final JavaFileObject INCLUDING_MODULE =
-      JavaFileObjects.forSourceLines(
-          "test.IncludingModule",
-          "package test;",
-          "",
-          "import dagger.Module;",
-          "",
-          "@Module(includes = TestModule.class)",
-          "interface IncludingModule {}");
-
-  private static final JavaFileObject MODULE =
-      JavaFileObjects.forSourceLines(
-          "test.TestModule",
-          "package test;",
-          "",
-          "import dagger.Binds;",
-          "import dagger.Module;",
-          "",
-          "@Module(subcomponents = Child.class)",
-          "interface TestModule {",
-          "  @Binds Object toString(String string);",
-          "  @Binds Object toLong(Long l);",
-          "}");
-
-  private static final JavaFileObject CHILD =
-      JavaFileObjects.forSourceLines(
-          "test.Child",
-          "package test;",
-          "",
-          "import dagger.BindsInstance;",
-          "import dagger.Subcomponent;",
-          "",
-          "@Subcomponent(modules = ChildModule.class)",
-          "interface Child {",
-          "  @Subcomponent.Builder",
-          "  interface Builder {",
-          "    @BindsInstance Builder object(Object object);",
-          "    Child build();",
-          "  }",
-          "}");
-
-  private static final JavaFileObject CHILD_MODULE =
-      JavaFileObjects.forSourceLines(
-          "test.ChildModule",
-          "package test;",
-          "",
-          "import dagger.Module;",
-          "import dagger.Binds;",
-          "",
-          "@Module",
-          "interface ChildModule {",
-          "  @Binds Object toNumber(Number number);",
-          "}");
-
-  // Make sure the module-level errors don't show a dependency trace afterwards (note the $).
-  private static final String MODULE_MESSAGE =
-      Pattern.quote(
-              message(
-                  "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
-                  "    @Binds Object test.TestModule.toLong(Long)",
-                  "    @Binds Object test.TestModule.toString(String)"))
-          + "$";
-
-  // Make sure the module-level errors don't show a dependency trace afterwards (note the $).
-  private static final String CHILD_MESSAGE =
-      Pattern.quote(
-              message(
-                  "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
-                  "    @BindsInstance test.Child.Builder test.Child.Builder.object(Object)",
-                  "    @Binds Object test.ChildModule.toNumber(Number)",
-                  "    @Binds Object test.TestModule.toLong(Long)",
-                  "    @Binds Object test.TestModule.toString(String)"))
-          + "$";
-
-  private static final ImmutableList<JavaFileObject> SOURCES =
-      ImmutableList.of(MODULE, INCLUDING_MODULE, CHILD, CHILD_MODULE);
-
-  @Test
-  public void error() {
-    Compilation compilation =
-        daggerCompiler().withOptions("-Adagger.moduleBindingValidation=ERROR").compile(SOURCES);
-    assertThat(compilation).failed();
-
-    // Some javacs report only one error for each source line.
-    // Assert that one of the expected errors is reported.
-    assertThat(compilation)
-        .hadErrorContainingMatch(CHILD_MESSAGE + "|" + MODULE_MESSAGE)
-        .inFile(MODULE)
-        .onLineContaining("interface TestModule");
-
-    assertThat(compilation)
-        .hadErrorContaining("test.TestModule has errors")
-        .inFile(INCLUDING_MODULE)
-        .onLineContaining("TestModule.class");
-
-  }
-
-  @Test
-  public void warning() {
-    Compilation compilation =
-        daggerCompiler().withOptions("-Adagger.moduleBindingValidation=WARNING").compile(SOURCES);
-    assertThat(compilation).succeeded();
-
-    assertThat(compilation)
-        .hadWarningContainingMatch(MODULE_MESSAGE)
-        .inFile(MODULE)
-        .onLineContaining("interface TestModule");
-
-    assertThat(compilation)
-        .hadWarningContainingMatch(CHILD_MESSAGE)
-        .inFile(MODULE)
-        .onLineContaining("interface TestModule");
-
-    // TODO(dpb): When warning, don't repeat in including modules.
-    assertThat(compilation)
-        .hadWarningContainingMatch(MODULE_MESSAGE)
-        .inFile(INCLUDING_MODULE)
-        .onLineContaining("interface IncludingModule");
-
-    assertThat(compilation)
-        .hadWarningContainingMatch(CHILD_MESSAGE)
-        .inFile(INCLUDING_MODULE)
-        .onLineContaining("interface IncludingModule");
-
-    // If module binding validation reports warnings, the warnings occur twice:
-    // once for TestModule and once for IncludingModule, which includes it.
-    assertThat(compilation).hadWarningCount(4);
-  }
-
-  @Test
-  public void none() {
-    Compilation compilation =
-        daggerCompiler().withOptions("-Adagger.moduleBindingValidation=NONE").compile(SOURCES);
-    assertThat(compilation).succeededWithoutWarnings();
-  }
-}
diff --git a/javatests/dagger/internal/codegen/NullableBindingValidationTest.java b/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
index eb0dc23..24d5636 100644
--- a/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
@@ -407,7 +407,7 @@
 
     Compilation compilation =
         daggerCompiler()
-            .withOptions("-Adagger.moduleBindingValidation=ERROR")
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
             .compile(module, NULLABLE);
     assertThat(compilation).failed();
     assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
index 2803b21..b765166 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
@@ -19,7 +19,7 @@
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
 
 import com.google.testing.compile.Compilation;
@@ -71,7 +71,7 @@
             "",
             "public class Maybe {",
             "  @Module",
-            "  public interface MaybeModule {",
+            "  public static class MaybeModule {",
             "    @Provides static Maybe provideMaybe() { return new Maybe(); }",
             "  }",
             "}");
@@ -111,7 +111,7 @@
                 "import com.google.common.base.Optional;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {")
+                "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private volatile Provider<Maybe> provideMaybeProvider;",
@@ -135,10 +135,10 @@
                 "  public Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe() {",
                 "    return Optional.of(ProviderOfLazy.create(")
             .addLinesIn(
-                DEFAULT_MODE,
+                DEFAULT_MODE, //
                 "        Maybe_MaybeModule_ProvideMaybeFactory.create()));")
             .addLinesIn(
-                FAST_INIT_MODE,
+                FAST_INIT_MODE, //
                 "        getMaybeProvider()));")
             .addLines(
                 "  }",
@@ -176,8 +176,10 @@
                 "}")
             .build();
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(
+                compilerMode
+                , CompilerMode.JAVA7
+                )
             .compile(module, maybe, definitelyNot, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -212,7 +214,7 @@
             "",
             "public class Maybe {",
             "  @Module",
-            "  public interface MaybeModule {",
+            "  public static class MaybeModule {",
             "    @Provides static Maybe provideMaybe() { return new Maybe(); }",
             "  }",
             "}");
@@ -249,8 +251,7 @@
             "import dagger.producers.internal.CancellationListener;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent, "
-                + "CancellationListener {",
+            "final class DaggerTestComponent implements TestComponent, CancellationListener {",
             "  @Override",
             "  public ListenableFuture<Optional<Maybe>> maybe() {",
             "    return Futures.immediateFuture(",
@@ -268,8 +269,10 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(
+                compilerMode
+                , CompilerMode.JAVA7
+                )
             .compile(module, maybe, definitelyNot, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
index 8c29e50..9a852c7 100644
--- a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -167,7 +167,7 @@
 
     compilation =
         daggerCompiler()
-            .withOptions("-Adagger.moduleBindingValidation=ERROR")
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
             .compile(producerModuleFile);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -249,7 +249,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestClass_SimpleComponent",
+                "final class DaggerTestClass_SimpleComponent",
                 "    implements TestClass.SimpleComponent, CancellationListener {",
                 "  private final TestClass.BModule bModule;",
                 "  private volatile Object productionImplementationExecutor =",
@@ -374,7 +374,7 @@
                 "    Producers.cancel(bProducer, mayInterruptIfRunning);",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private TestClass.AModule aModule;",
                 "    private TestClass.BModule bModule;",
                 "",
@@ -444,7 +444,7 @@
                 "import javax.inject.Provider;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestClass_SimpleComponent",
+                "final class DaggerTestClass_SimpleComponent",
                 "    implements TestClass.SimpleComponent, CancellationListener {",
                 "  private Producer<TestClass.A> aEntryPoint;",
                 "  private Provider<Executor> executorProvider;",
@@ -506,7 +506,7 @@
                 "    Producers.cancel(bProducer, mayInterruptIfRunning);",
                 "  }",
                 "",
-                "  public static final class Builder {",
+                "  static final class Builder {",
                 "    private TestClass.AModule aModule;",
                 "    private TestClass.BModule bModule;",
                 "",
@@ -652,7 +652,7 @@
                 .addLines(
                     "package test;",
                     GENERATED_ANNOTATION,
-                    "public final class DaggerParent implements Parent, CancellationListener {",
+                    "final class DaggerParent implements Parent, CancellationListener {",
                     "  private final class ChildImpl implements Child, CancellationListener {",
                     "    @Override",
                     "    public ProductionScoped productionScoped() {")
diff --git a/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
index 7106dd9..8453e03 100644
--- a/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
+++ b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
@@ -154,7 +154,7 @@
 
     compilation =
         daggerCompiler()
-            .withOptions("-Adagger.moduleBindingValidation=ERROR")
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
             .compile(EXECUTOR_MODULE, component);
     assertThat(compilation).failed();
     assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/ScopingValidationTest.java b/javatests/dagger/internal/codegen/ScopingValidationTest.java
index c832206..9efcc2a 100644
--- a/javatests/dagger/internal/codegen/ScopingValidationTest.java
+++ b/javatests/dagger/internal/codegen/ScopingValidationTest.java
@@ -243,7 +243,7 @@
 
     compilation =
         daggerCompiler()
-            .withOptions("-Adagger.moduleBindingValidation=ERROR")
+            .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
             .compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
     // The @Inject binding for ScopedType should not appear here, but the @Singleton binding should.
     assertThat(compilation)
@@ -259,11 +259,11 @@
   }
 
   @Test
-  public void moduleBindingValidationDoesNotReportForOneScope() {
+  public void fullBindingGraphValidationDoesNotReportForOneScope() {
     Compilation compilation =
         daggerCompiler()
             .withOptions(
-                "-Adagger.moduleBindingValidation=ERROR",
+                "-Adagger.fullBindingGraphValidation=ERROR",
                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
             .compile(
                 JavaFileObjects.forSourceLines(
@@ -284,11 +284,11 @@
   }
 
   @Test
-  public void moduleBindingValidationDoesNotReportInjectBindings() {
+  public void fullBindingGraphValidationDoesNotReportInjectBindings() {
     Compilation compilation =
         daggerCompiler()
             .withOptions(
-                "-Adagger.moduleBindingValidation=ERROR",
+                "-Adagger.fullBindingGraphValidation=ERROR",
                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
             .compile(
                 JavaFileObjects.forSourceLines(
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
index 890ceb8..3fb0e9c 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -97,7 +97,7 @@
             "import dagger.internal.SetBuilder;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Set<String> strings() {",
             "    return SetBuilder.<String>newSetBuilder(2)",
@@ -192,7 +192,7 @@
             "import other.UsesInaccessible_Factory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private Set getSetOfInaccessible2() {",
             "    return SetBuilder.newSetBuilder(1)",
             "        .addAll(TestModule_EmptySetFactory.emptySet())",
@@ -267,7 +267,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private DaggerParent() {}",
             "",
             "  public static Builder builder() {",
@@ -283,7 +283,7 @@
             "    return new ChildImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    @Deprecated",
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
index d39e60b..7a47393 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
@@ -100,7 +100,7 @@
             "import com.google.common.collect.ImmutableSet;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Set<String> strings() {",
             "    return ImmutableSet.<String>builderWithExpectedSize(2)",
@@ -204,7 +204,7 @@
             "import other.UsesInaccessible_Factory;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
+            "final class DaggerTestComponent implements TestComponent {",
             "  private Set getSetOfInaccessible2() {",
             "    return ImmutableSet.copyOf(TestModule_EmptySetFactory.emptySet());",
             "  }",
@@ -275,7 +275,7 @@
             "import com.google.common.collect.ImmutableSet;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
+            "final class DaggerParent implements Parent {",
             "  private final class ChildImpl implements Child {",
             "    @Override",
             "    public Set<Object> objectSet() {",
@@ -334,7 +334,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent, "
+            "final class DaggerTestComponent implements TestComponent, "
                 + "CancellationListener {",
             "  private DaggerTestComponent() {}",
             "",
@@ -359,7 +359,7 @@
             "  @Override",
             "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    private Builder() {}",
             "",
             "    public TestComponent build() {",
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
index d02676f..de0067f 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
@@ -101,7 +101,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerC implements C {",
+            "final class DaggerC implements C {",
             "  @Override",
             "  public Sub.Builder sBuilder() {",
             "    return new SubBuilder();",
diff --git a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
index 66ae11f..ad08160 100644
--- a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -464,7 +464,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerParentComponent implements ParentComponent {")
+                "final class DaggerParentComponent implements ParentComponent {")
             .addLinesIn(
                 DEFAULT_MODE,
                 "  @SuppressWarnings(\"unchecked\")",
@@ -664,7 +664,7 @@
             "import test.subpackage.Sub;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParentComponent implements ParentComponent {",
+            "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Foo.Sub newInstanceSubcomponent() {",
             "    return new F_SubImpl();",
@@ -675,7 +675,7 @@
             "    return new NoConflictImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    public ParentComponent build() {",
             "      return new DaggerParentComponent();",
             "    }",
@@ -747,13 +747,13 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParentComponent implements ParentComponent {",
+            "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
             "    return new t_SubImpl();",
             "  }",
             "",
-            "  public static final class Builder {",
+            "  static final class Builder {",
             "    public ParentComponent build() {",
             "      return new DaggerParentComponent();",
             "    }",
@@ -812,7 +812,7 @@
             "DaggerParentComponent",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParentComponent implements ParentComponent {",
+            "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
             "    return new $_SubImpl();",
@@ -889,7 +889,7 @@
             "import top1.a.b.c.d.E;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerParentComponent implements ParentComponent {",
+            "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public E.F.Sub top1() {",
             "    return new F_SubImpl();",
@@ -946,7 +946,7 @@
             "package test;",
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerC implements C {",
+            "final class DaggerC implements C {",
             "  @Override",
             "  public Foo.C newInstanceC() {",
             "    return new F_CImpl();",
@@ -1008,7 +1008,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "",
             GENERATED_ANNOTATION,
-            "public final class DaggerC implements C {",
+            "final class DaggerC implements C {",
             "  @Override",
             "  public C.Foo.Sub.Builder fooBuilder() {",
             "    return new F_SubBuilder();",
diff --git a/javatests/dagger/internal/codegen/SwitchingProviderTest.java b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
index 932daf4..2898f2e 100644
--- a/javatests/dagger/internal/codegen/SwitchingProviderTest.java
+++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
@@ -69,7 +69,7 @@
             "test.DaggerTestComponent",
                 "package test;",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    private T get0() {",
@@ -249,7 +249,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private volatile Provider<String> sProvider;",
                 "",
                 "  private Provider<String> getStringProvider() {",
@@ -334,7 +334,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private volatile Object charSequence = new MemoizedSentinel();",
                 "  private volatile Provider<CharSequence> cProvider;",
                 "",
@@ -425,7 +425,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public Provider<Set<String>> setProvider() {",
                 "    return SetFactory.<String>empty();",
@@ -470,7 +470,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
                 "",
                 "  @SuppressWarnings(\"unchecked\")",
@@ -541,7 +541,7 @@
                 "package test;",
                 "",
                 GENERATED_ANNOTATION,
-                "public final class DaggerTestComponent implements TestComponent {",
+                "final class DaggerTestComponent implements TestComponent {",
                 "  @SuppressWarnings(\"rawtypes\")",
                 "  private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
                 "      InstanceFactory.create(Optional.empty());",
diff --git a/javatests/dagger/internal/codegen/TestUtils.java b/javatests/dagger/internal/codegen/TestUtils.java
index c02a5f9..69b27511 100644
--- a/javatests/dagger/internal/codegen/TestUtils.java
+++ b/javatests/dagger/internal/codegen/TestUtils.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import com.google.common.base.Joiner;
+import java.util.regex.Pattern;
 
 /** Utility methods useful for codegen tests. */
 final class TestUtils {
@@ -24,10 +25,19 @@
   private static final Joiner MESSAGE_JOINER = Joiner.on("\n  ");
 
   /**
-   * Returns the lines joined by newline plus two spaces. Useful for passing to {@link
+   * Returns the lines joined by {@code "\n  "}. Useful for passing to {@link
    * com.google.testing.compile.CompilationSubject#hadErrorContaining(String)}, etc.
    */
   static String message(String... lines) {
     return MESSAGE_JOINER.join(lines);
   }
+
+  /**
+   * Returns a pattern that matches strings that end with the lines joined by {@code "\n  "}. Useful
+   * for passing to {@link
+   * com.google.testing.compile.CompilationSubject#hadErrorContainingMatch(Pattern)}, etc.
+   */
+  static Pattern endsWithMessage(String... lines) {
+    return Pattern.compile(Pattern.quote(message(lines)) + "$");
+  }
 }
diff --git a/javatests/dagger/internal/codegen/TypeProtoConverterTest.java b/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
new file mode 100644
index 0000000..8c8628c
--- /dev/null
+++ b/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static javax.lang.model.util.ElementFilter.fieldsIn;
+
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.Factory;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.serialization.TypeProto;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link TypeProtoConverter}. */
+@RunWith(JUnit4.class)
+public class TypeProtoConverterTest {
+  @Rule public CompilationRule compilationRule = new CompilationRule();
+
+  private DaggerElements elements;
+  private DaggerTypes types;
+  private TypeProtoConverter typeProtoConverter;
+
+  @Before
+  public void setUp() {
+    this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+    this.types = new DaggerTypes(compilationRule.getTypes(), elements);
+    this.typeProtoConverter = new TypeProtoConverter(types, elements);
+  }
+
+  static class Outer<O> {
+    @SuppressWarnings("ClassCanBeStatic") // We want to specifically test inner classes
+    class Inner<I> {}
+  }
+
+  @SuppressWarnings({"rawtypes", "unused"})
+  static class TypeMirrorConversionSubjects {
+    private Map rawMap;
+    private List<String> listOfString;
+    private List<HashMap<String, Integer>> listOfHashMapOfStringToInteger;
+    private Map<HashMap<String, Integer>, Set<Factory>> mapOfHashMapOfStringToIntegerToSetOfFactory;
+    private Map<HashMap<String, Integer>, Set<Factory>>[][]
+        arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory;
+    private Map<HashMap<?, Integer>, ?> mapOfHashMapOfWildcardToIntegerToWildcard;
+    private List<? extends String> listOfWildcardExtendsString;
+    private List<? extends Set<? super String>> listOfWildcardExtendsSetOfWildcardSuperString;
+    private Outer<Object>.Inner<Integer> outerOfObjectDotInnerOfInteger;
+    private List<int[]> listOfIntArray;
+    private List<? extends CharSequence[]> listOfWildcardExtendsCharSequenceArray;
+  }
+
+  @Test
+  public void typeMirrorProtoConversions() {
+    assertProtoConversionEquality(fieldType("rawMap"));
+    assertProtoConversionEquality(fieldType("listOfString"));
+    assertProtoConversionEquality(fieldType("listOfHashMapOfStringToInteger"));
+    assertProtoConversionEquality(fieldType("mapOfHashMapOfStringToIntegerToSetOfFactory"));
+    assertProtoConversionEquality(
+        fieldType("arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory"));
+    assertProtoConversionEquality(fieldType("mapOfHashMapOfWildcardToIntegerToWildcard"));
+    assertProtoConversionEquality(fieldType("listOfWildcardExtendsString"));
+    assertProtoConversionEquality(fieldType("listOfWildcardExtendsSetOfWildcardSuperString"));
+    assertProtoConversionEquality(fieldType("outerOfObjectDotInnerOfInteger"));
+    assertProtoConversionEquality(fieldType("listOfIntArray"));
+    assertProtoConversionEquality(fieldType("listOfWildcardExtendsCharSequenceArray"));
+  }
+
+  private TypeMirror fieldType(String fieldName) {
+    return fieldsIn(
+            elements.getTypeElement(TypeMirrorConversionSubjects.class).getEnclosedElements())
+        .stream()
+        .filter(field -> field.getSimpleName().contentEquals(fieldName))
+        .findFirst()
+        .get()
+        .asType();
+  }
+
+  /**
+   * Converts {@link TypeMirror} to a {@link dagger.internal.codegen.serialization.TypeProto} and
+   * back to a {@link TypeMirror}. Asserts that the round-trip conversion is lossless.
+   */
+  private void assertProtoConversionEquality(TypeMirror typeMirror) {
+    TypeProto toProto = TypeProtoConverter.toProto(typeMirror);
+    TypeMirror fromProto = typeProtoConverter.fromProto(toProto);
+    assertWithMessage("expected: %s\nactual  : %s", typeMirror, fromProto)
+        .that(types.isSameType(typeMirror, fromProto))
+        .isTrue();
+  }
+}
diff --git a/javatests/dagger/internal/codegen/javapoet/BUILD b/javatests/dagger/internal/codegen/javapoet/BUILD
new file mode 100644
index 0000000..99c11bd
--- /dev/null
+++ b/javatests/dagger/internal/codegen/javapoet/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+#   Tests for dagger.internal.codegen.javapoet
+
+package(default_visibility = ["//:src"])
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+GenJavaTests(
+    name = "javapoet_tests",
+    srcs = glob(["*.java"]),
+    functional = False,
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    deps = [
+        "//java/dagger/internal/codegen/javapoet",
+        "//java/dagger/internal/codegen/langmodel",
+        "@google_bazel_common//third_party/java/auto:common",
+        "@google_bazel_common//third_party/java/compile_testing",
+        "@google_bazel_common//third_party/java/javapoet",
+        "@google_bazel_common//third_party/java/junit",
+        "@google_bazel_common//third_party/java/truth",
+    ],
+)
diff --git a/javatests/dagger/internal/codegen/CodeBlocksTest.java b/javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java
similarity index 93%
rename from javatests/dagger/internal/codegen/CodeBlocksTest.java
rename to javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java
index ec8df91..ee3681e 100644
--- a/javatests/dagger/internal/codegen/CodeBlocksTest.java
+++ b/javatests/dagger/internal/codegen/javapoet/CodeBlocksTest.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import static com.google.common.truth.Truth.assertThat;
-import static dagger.internal.codegen.CodeBlocks.javadocLinkTo;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.javadocLinkTo;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
 import static javax.lang.model.element.ElementKind.METHOD;
 
 import com.google.testing.compile.CompilationRule;
diff --git a/javatests/dagger/internal/codegen/ExpressionTest.java b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
similarity index 86%
rename from javatests/dagger/internal/codegen/ExpressionTest.java
rename to javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
index e6fe0d1..acfa460 100644
--- a/javatests/dagger/internal/codegen/ExpressionTest.java
+++ b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen;
+package dagger.internal.codegen.javapoet;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.auto.common.MoreTypes;
 import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.PrimitiveType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
@@ -53,11 +55,11 @@
 
     Expression castTo = expression.castTo(supertype);
 
-    assertThat(castTo.type()).isSameAs(supertype);
+    assertThat(castTo.type()).isSameInstanceAs(supertype);
     assertThat(castTo.codeBlock().toString())
         .isEqualTo(
-            "(dagger.internal.codegen.ExpressionTest.Supertype) "
-                + "new dagger.internal.codegen.ExpressionTest.Subtype() {}");
+            "(dagger.internal.codegen.javapoet.ExpressionTest.Supertype) "
+                + "new dagger.internal.codegen.javapoet.ExpressionTest.Subtype() {}");
   }
 
   @Test
diff --git a/javatests/dagger/producers/ProducedTest.java b/javatests/dagger/producers/ProducedTest.java
index c2028f2..5804141 100644
--- a/javatests/dagger/producers/ProducedTest.java
+++ b/javatests/dagger/producers/ProducedTest.java
@@ -35,7 +35,7 @@
     Object o = new Object();
     assertThat(Produced.successful(5).get()).isEqualTo(5);
     assertThat(Produced.successful("monkey").get()).isEqualTo("monkey");
-    assertThat(Produced.successful(o).get()).isSameAs(o);
+    assertThat(Produced.successful(o).get()).isSameInstanceAs(o);
   }
 
   @Test public void failedProduced() {
@@ -44,7 +44,7 @@
       Produced.failed(cause).get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(cause);
+      assertThat(e).hasCauseThat().isSameInstanceAs(cause);
     }
   }
 
diff --git a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
index ab09ace..b29cb3c 100644
--- a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
+++ b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
@@ -101,7 +101,7 @@
       future.get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(t);
+      assertThat(e).hasCauseThat().isSameInstanceAs(t);
     }
     verify(monitor).failed(t);
     verifyNoMoreInteractions(monitor);
diff --git a/javatests/dagger/producers/internal/MapOfProducerProducerTest.java b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
index 11fa2d3..f4be15c 100644
--- a/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
+++ b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
@@ -61,7 +61,7 @@
       map.get(42).get().get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(cause);
+      assertThat(e).hasCauseThat().isSameInstanceAs(cause);
     }
   }
 }
diff --git a/javatests/dagger/producers/internal/MapProducerTest.java b/javatests/dagger/producers/internal/MapProducerTest.java
index 72bd9eb..f74bc41 100644
--- a/javatests/dagger/producers/internal/MapProducerTest.java
+++ b/javatests/dagger/producers/internal/MapProducerTest.java
@@ -55,7 +55,7 @@
       mapProducer.get().get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e).hasCauseThat().isSameAs(cause);
+      assertThat(e).hasCauseThat().isSameInstanceAs(cause);
     }
   }
 }
diff --git a/javatests/dagger/producers/monitoring/TimingRecordersTest.java b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
index ef0f218..4e5d74f 100644
--- a/javatests/dagger/producers/monitoring/TimingRecordersTest.java
+++ b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
@@ -67,7 +67,8 @@
     ProductionComponentTimingRecorder.Factory factory =
         TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
             ImmutableList.<ProductionComponentTimingRecorder.Factory>of());
-    assertThat(factory).isSameAs(TimingRecorders.noOpProductionComponentTimingRecorderFactory());
+    assertThat(factory)
+        .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorderFactory());
   }
 
   @Test
@@ -77,7 +78,7 @@
         TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
             ImmutableList.of(mockProductionComponentTimingRecorderFactory));
     assertThat(factory.create(new Object()))
-        .isSameAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+        .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
   }
 
   @Test
@@ -88,7 +89,7 @@
         TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
             ImmutableList.of(mockProductionComponentTimingRecorderFactory));
     assertThat(factory.create(new Object()))
-        .isSameAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+        .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
   }
 
   @Test
@@ -102,7 +103,7 @@
             ImmutableList.of(mockProductionComponentTimingRecorderFactory));
     ProductionComponentTimingRecorder recorder = factory.create(new Object());
     assertThat(recorder.producerTimingRecorderFor(ProducerToken.create(Object.class)))
-        .isSameAs(ProducerTimingRecorder.noOp());
+        .isSameInstanceAs(ProducerTimingRecorder.noOp());
   }
 
   @Test
@@ -116,7 +117,7 @@
             ImmutableList.of(mockProductionComponentTimingRecorderFactory));
     ProductionComponentTimingRecorder recorder = factory.create(new Object());
     assertThat(recorder.producerTimingRecorderFor(ProducerToken.create(Object.class)))
-        .isSameAs(ProducerTimingRecorder.noOp());
+        .isSameInstanceAs(ProducerTimingRecorder.noOp());
   }
 
   @Test
@@ -192,7 +193,7 @@
                 mockProductionComponentTimingRecorderFactoryB,
                 mockProductionComponentTimingRecorderFactoryC));
     assertThat(factory.create(new Object()))
-        .isSameAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+        .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
   }
 
   @Test
@@ -210,7 +211,7 @@
                 mockProductionComponentTimingRecorderFactoryB,
                 mockProductionComponentTimingRecorderFactoryC));
     assertThat(factory.create(new Object()))
-        .isSameAs(TimingRecorders.noOpProductionComponentTimingRecorder());
+        .isSameInstanceAs(TimingRecorders.noOpProductionComponentTimingRecorder());
   }
 
   @Test
diff --git a/javatests/dagger/producers/monitoring/internal/MonitorsTest.java b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
index 5ed6b9d..47ccccb 100644
--- a/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
+++ b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
@@ -60,7 +60,7 @@
     ProductionComponentMonitor.Factory factory =
         Monitors.delegatingProductionComponentMonitorFactory(
             ImmutableList.<ProductionComponentMonitor.Factory>of());
-    assertThat(factory).isSameAs(ProductionComponentMonitor.Factory.noOp());
+    assertThat(factory).isSameInstanceAs(ProductionComponentMonitor.Factory.noOp());
   }
 
   @Test
@@ -69,7 +69,7 @@
     ProductionComponentMonitor.Factory factory =
         Monitors.delegatingProductionComponentMonitorFactory(
             ImmutableList.of(mockProductionComponentMonitorFactory));
-    assertThat(factory.create(new Object())).isSameAs(ProductionComponentMonitor.noOp());
+    assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
   }
 
   @Test
@@ -80,7 +80,7 @@
     ProductionComponentMonitor.Factory factory =
         Monitors.delegatingProductionComponentMonitorFactory(
             ImmutableList.of(mockProductionComponentMonitorFactory));
-    assertThat(factory.create(new Object())).isSameAs(ProductionComponentMonitor.noOp());
+    assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
   }
 
   @Test
@@ -94,7 +94,7 @@
             ImmutableList.of(mockProductionComponentMonitorFactory));
     ProductionComponentMonitor monitor = factory.create(new Object());
     assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
-        .isSameAs(ProducerMonitor.noOp());
+        .isSameInstanceAs(ProducerMonitor.noOp());
   }
 
   @Test
@@ -109,7 +109,7 @@
             ImmutableList.of(mockProductionComponentMonitorFactory));
     ProductionComponentMonitor monitor = factory.create(new Object());
     assertThat(monitor.producerMonitorFor(ProducerToken.create(Object.class)))
-        .isSameAs(ProducerMonitor.noOp());
+        .isSameInstanceAs(ProducerMonitor.noOp());
   }
 
   @Test
@@ -223,7 +223,7 @@
                 mockProductionComponentMonitorFactoryA,
                 mockProductionComponentMonitorFactoryB,
                 mockProductionComponentMonitorFactoryC));
-    assertThat(factory.create(new Object())).isSameAs(ProductionComponentMonitor.noOp());
+    assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
   }
 
   @Test
@@ -243,7 +243,7 @@
                 mockProductionComponentMonitorFactoryA,
                 mockProductionComponentMonitorFactoryB,
                 mockProductionComponentMonitorFactoryC));
-    assertThat(factory.create(new Object())).isSameAs(ProductionComponentMonitor.noOp());
+    assertThat(factory.create(new Object())).isSameInstanceAs(ProductionComponentMonitor.noOp());
   }
 
   @Test
diff --git a/javatests/dagger/spi/SpiPluginTest.java b/javatests/dagger/spi/SpiPluginTest.java
index e0a78b2..613a915 100644
--- a/javatests/dagger/spi/SpiPluginTest.java
+++ b/javatests/dagger/spi/SpiPluginTest.java
@@ -53,8 +53,7 @@
         javac()
             .withProcessors(new ComponentProcessor())
             .withOptions(
-                "-Aerror_on_binding=java.lang.Integer",
-                "-Adagger.moduleBindingValidation=ERROR")
+                "-Aerror_on_binding=java.lang.Integer", "-Adagger.fullBindingGraphValidation=ERROR")
             .compile(module);
     assertThat(compilation).failed();
     assertThat(compilation)
diff --git a/test_defs.bzl b/test_defs.bzl
index f72927a..15ab299 100644
--- a/test_defs.bzl
+++ b/test_defs.bzl
@@ -22,6 +22,10 @@
         "-Adagger.fastInit=enabled",
         "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
     ],
+    "AheadOfTimeSubcomponents_ForceUseSerializedComponentImplementations": [
+        "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
+        "-Adagger.forceUseSerializedComponentImplementations=enabled",
+    ],
 }
 
 # TODO(ronshapiro): convert this to use bazel_common
diff --git a/tools/BUILD b/tools/BUILD
index b0b3524..7b3cc6c 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -20,14 +20,3 @@
 exports_files([
     "pom-template.xml",
 ])
-
-sh_library(
-    name = "simple_jar",
-    srcs = ["simple_jar.sh"],
-)
-
-sh_test(
-    name = "simple_jar_test",
-    srcs = ["simple_jar_test.sh"],
-    deps = [":simple_jar"],
-)
diff --git a/tools/maven.bzl b/tools/maven.bzl
index 725c48f..2ac0359 100644
--- a/tools/maven.bzl
+++ b/tools/maven.bzl
@@ -35,4 +35,4 @@
         **kwargs
     )
 
-POM_VERSION = "2.22.1"
+POM_VERSION = "2.23.1"
diff --git a/tools/simple_jar.bzl b/tools/simple_jar.bzl
deleted file mode 100644
index 967640b..0000000
--- a/tools/simple_jar.bzl
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2018 The Dagger Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Macro for creating a jar from a set of flat files"""
-
-def simple_jar(name, srcs):
-    """Creates a jar out of a set of flat files"""
-
-    # TODO(dpb): consider creating a Fileset() under the hood to support srcs from different
-    # directories, or continually update the same zip file for each source file
-    native.genrule(
-        name = name,
-        srcs = srcs,
-        outs = ["%s.jar" % name],
-        cmd = """
-            $(location //tools:simple_jar.sh) \
-              "{package_name}" "$@" $(SRCS)
-              """.format(package_name = native.package_name()),
-        tools = ["//tools:simple_jar.sh"],
-    )
diff --git a/tools/simple_jar.sh b/tools/simple_jar.sh
deleted file mode 100755
index 043c825..0000000
--- a/tools/simple_jar.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-# Copyright (C) 2018 The Dagger Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -e
-
-find_dirname() {
-  local package_name="$1"
-  shift
-
-  local src src_dirname dirname=""
-  for src in "$@"; do
-    if [[ ! "${src}" =~ ^((.*/)?"${package_name}")/ ]]; then
-      echo "Sources must be in ${package_name}: $0" >&2
-      return 1
-    fi
-    src_dirname="${BASH_REMATCH[1]}"
-    if [[ "${dirname:=${src_dirname}}" != "${src_dirname}" ]]; then
-      echo "Sources must all be in the same directory: $@" >&2
-      return 1
-    fi
-  done
-
-  if [[ -z "${dirname}" ]]; then
-    echo "No sources provided" >&2
-    return 1
-  fi
-
-  echo "${dirname}"
-}
-
-main() {
-  local package_name="$1"
-  local out="${PWD}/$2"
-  shift 2
-
-  local dirname
-  dirname="$(find_dirname "${package_name}" "$@")"
-  cd "${dirname}"
-  zip "${out}" -r * &> /dev/null
-}
-
-if [[ -z "$TEST_SRCDIR" ]]; then
-  main "$@"
-fi
diff --git a/tools/simple_jar_test.sh b/tools/simple_jar_test.sh
deleted file mode 100755
index d03d3d0..0000000
--- a/tools/simple_jar_test.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-# Copyright (C) 2019 The Dagger Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -e
-
-fail() {
-  local message="${1:-failed}"
-  echo "${message} at $(caller)" >&2
-  return 1
-}
-
-. "tools/simple_jar.sh"
-
-[[ "$(find_dirname some/package some/package/a/b/c some/package/d/e/f)" \
-    == "some/package" ]] \
-  || fail "no prefix"
-
-[[ "$(find_dirname some/package prefix/some/package/a prefix/some/package/b)" \
-    == "prefix/some/package" ]] \
-  || fail "with prefix"
-
-! find_dirname some/package some/package/a other/package/b >/dev/null \
-  || fail "expected failure if one file is in the wrong package"
-
-! find_dirname some/package other/package/a other/package/b >/dev/null \
-  || fail "expected failure if all files are in the wrong package"
-
-! find_dirname some/package some/package/a prefix/some/package/b >/dev/null \
-  || fail "expected failure if files have different prefixes"
-
-! find_dirname some/package >/dev/null \
-  || fail "expected failure with no sources"
-
-true