Remove almost all usages of BindingKey

A follow-up will remove the remaining usages, but I didn't want to make this CL even more unwieldy than it already is

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=179929249
diff --git a/java/dagger/internal/codegen/Binding.java b/java/dagger/internal/codegen/Binding.java
index d34aee6..68e134f 100644
--- a/java/dagger/internal/codegen/Binding.java
+++ b/java/dagger/internal/codegen/Binding.java
@@ -19,10 +19,10 @@
 import static com.google.common.base.Suppliers.memoize;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.DaggerStreams.toImmutableList;
+import static java.util.stream.Collectors.toSet;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Supplier;
-import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -169,9 +169,7 @@
                   DependencyAssociation.create(
                       FrameworkDependency.create(
                           getOnlyElement(
-                              FluentIterable.from(requests)
-                                  .transform(DependencyRequest::bindingKey)
-                                  .toSet()),
+                              requests.stream().map(DependencyRequest::key).collect(toSet())),
                           bindingTypeMapper.getBindingType(requests)),
                       requests));
             }
@@ -220,13 +218,13 @@
    * from the {@link Binding#unresolved()} binding if it exists.
    */
   private ImmutableList<Collection<DependencyRequest>> groupByUnresolvedKey() {
-    ImmutableSetMultimap.Builder<BindingKey, DependencyRequest> dependenciesByKeyBuilder =
+    ImmutableSetMultimap.Builder<Key, DependencyRequest> dependenciesByKeyBuilder =
         ImmutableSetMultimap.builder();
     Iterator<DependencyRequest> dependencies = dependencies().iterator();
     Binding unresolved = unresolved().isPresent() ? unresolved().get() : this;
     Iterator<DependencyRequest> unresolvedDependencies = unresolved.dependencies().iterator();
     while (dependencies.hasNext()) {
-      dependenciesByKeyBuilder.put(unresolvedDependencies.next().bindingKey(), dependencies.next());
+      dependenciesByKeyBuilder.put(unresolvedDependencies.next().key(), dependencies.next());
     }
     return ImmutableList.copyOf(
         dependenciesByKeyBuilder
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 45c33cd..9a3a642 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -22,6 +22,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.model.Key;
 import dagger.model.RequestKind;
 
 /** A factory of code expressions used to access a single request for a binding in a component. */
@@ -35,12 +36,12 @@
     this.requestKind = checkNotNull(requestKind);
   }
 
-  /** Returns the {@linkplain BindingKey} for this expression. */
-  final BindingKey bindingKey() {
-    return resolvedBindings.bindingKey();
+  /** Returns the {@link Key} for this expression. */
+  final Key key() {
+    return resolvedBindings.key();
   }
 
-  /** Returns the {@linkplain RequestKind} handled by this expression. */
+  /** Returns the {@link RequestKind} handled by this expression. */
   final RequestKind requestKind() {
     return requestKind;
   }
@@ -62,7 +63,7 @@
   final CodeBlock getComponentMethodImplementation(
       ComponentMethodDescriptor componentMethod, ClassName requestingClass) {
     DependencyRequest request = componentMethod.dependencyRequest().get();
-    checkArgument(request.bindingKey().equals(bindingKey()));
+    checkArgument(request.key().equals(key()));
     checkArgument(request.kind().equals(requestKind()));
     return doGetComponentMethodImplementation(componentMethod, requestingClass);
   }
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index 8b113d8..8d02ac2 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -53,6 +53,7 @@
 import dagger.Subcomponent;
 import dagger.internal.codegen.ComponentDescriptor.BuilderRequirementMethod;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodKind;
 import dagger.internal.codegen.ContributionBinding.Kind;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -66,6 +67,7 @@
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -89,7 +91,40 @@
 @AutoValue
 abstract class BindingGraph {
   abstract ComponentDescriptor componentDescriptor();
-  abstract ImmutableMap<BindingKey, ResolvedBindings> resolvedBindings();
+
+  /**
+   * The resolved bindings for all {@link ContributionBinding}s in this graph, keyed by {@link Key}.
+   */
+  // TODO(ronshapiro): when MembersInjectionBinding no longer extends Binding, rename this to
+  // bindings()
+  abstract ImmutableMap<Key, ResolvedBindings> contributionBindings();
+
+  /**
+   * The resolved bindings for all {@link MembersInjectionBinding}s in this graph, keyed by {@link
+   * Key}.
+   */
+  abstract ImmutableMap<Key, ResolvedBindings> membersInjectionBindings();
+
+  /**
+   * Returns the {@link ResolvedBindings resolved bindings} instance for {@code key}. If {@code
+   * requestKind} is {@link RequestKind#MEMBERS_INJECTION}, a {@link ResolvedBindings} with
+   * {@linkplain #membersInjectionBindings() members injection bindings} will be returned, otherwise
+   * a {@link ResolvedBindings} with {@link #contributionBindings()} will be returned.
+   */
+  final ResolvedBindings resolvedBindings(RequestKind requestKind, Key key) {
+    return requestKind.equals(RequestKind.MEMBERS_INJECTION)
+        ? membersInjectionBindings().get(key)
+        : contributionBindings().get(key);
+  }
+
+  @Memoized
+  ImmutableSet<ResolvedBindings> resolvedBindings() {
+    return ImmutableSet.<ResolvedBindings>builder()
+        .addAll(membersInjectionBindings().values())
+        .addAll(contributionBindings().values())
+        .build();
+  }
+
   abstract ImmutableSet<BindingGraph> subgraphs();
 
   /**
@@ -114,13 +149,11 @@
     return binding
         .dependencies()
         .stream()
+        .map(DependencyRequest::key)
         .map(
-            dependencyRequest ->
-                resolvedBindings()
-                    .getOrDefault(
-                        dependencyRequest.bindingKey(),
-                        ResolvedBindings.noBindings(
-                            dependencyRequest.bindingKey(), componentDescriptor())))
+            key ->
+                contributionBindings()
+                    .getOrDefault(key, ResolvedBindings.noBindings(key, componentDescriptor())))
         .collect(toImmutableSet());
   }
   /**
@@ -168,7 +201,7 @@
   ImmutableSet<ComponentRequirement> componentRequirements() {
     ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
     StreamSupport.stream(SUBGRAPH_TRAVERSER.preOrderTraversal(this).spliterator(), false)
-        .flatMap(graph -> graph.resolvedBindings().values().stream())
+        .flatMap(graph -> graph.contributionBindings().values().stream())
         .flatMap(bindings -> bindings.contributionBindings().stream())
         .filter(ContributionBinding::requiresModuleInstance)
         .map(bindingDeclaration -> bindingDeclaration.contributingModule())
@@ -321,9 +354,13 @@
               indexByKey(delegatesBuilder.build()),
               indexByKey(optionalsBuilder.build()));
       for (ComponentMethodDescriptor componentMethod : componentDescriptor.componentMethods()) {
-        Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
-        if (componentMethodRequest.isPresent()) {
-          requestResolver.resolve(componentMethodRequest.get().bindingKey());
+        if (componentMethod.kind().equals(ComponentMethodKind.MEMBERS_INJECTION)) {
+          requestResolver.resolveMembersInjectionMethod(componentMethod);
+        } else {
+          Optional<DependencyRequest> componentMethodRequest = componentMethod.dependencyRequest();
+          if (componentMethodRequest.isPresent()) {
+            requestResolver.resolve(componentMethodRequest.get().key());
+          }
         }
       }
 
@@ -341,9 +378,9 @@
         }
       }
 
-      ImmutableMap<BindingKey, ResolvedBindings> resolvedBindingsMap =
-          requestResolver.getResolvedBindings();
-      for (ResolvedBindings resolvedBindings : resolvedBindingsMap.values()) {
+      ImmutableMap<Key, ResolvedBindings> resolvedContributionBindingsMap =
+          requestResolver.getResolvedContributionBindings();
+      for (ResolvedBindings resolvedBindings : resolvedContributionBindingsMap.values()) {
         verify(
             resolvedBindings.owningComponent().equals(componentDescriptor),
             "%s is not owned by %s",
@@ -353,10 +390,11 @@
 
       return new AutoValue_BindingGraph(
           componentDescriptor,
-          resolvedBindingsMap,
+          resolvedContributionBindingsMap,
+          requestResolver.getResolvedMembersInjectionBindings(),
           subgraphs.build(),
           getScopesRequiringReleasableReferenceManagers(
-              releasableReferenceManagerBindings, resolvedBindingsMap),
+              releasableReferenceManagerBindings, resolvedContributionBindingsMap.keySet()),
           requestResolver.getOwnedModules());
     }
 
@@ -404,18 +442,18 @@
      *
      * @param releasableReferenceManagerBindings the {@link ReleasableReferenceManager} bindings for
      *     each scope
-     * @param resolvedBindingsMap the resolved bindings for the component
+     * @param resolvedContributionKeys the keys of the resolved bindings for the component
      */
     private ImmutableSet<Scope> getScopesRequiringReleasableReferenceManagers(
         ImmutableSetMultimap<Scope, ProvisionBinding> releasableReferenceManagerBindings,
-        ImmutableMap<BindingKey, ResolvedBindings> resolvedBindingsMap) {
+        ImmutableSet<Key> resolvedContributionKeys) {
       ImmutableSet.Builder<Scope> scopes = ImmutableSet.builder();
       releasableReferenceManagerBindings
           .asMap()
           .forEach(
               (scope, bindings) -> {
                 for (Binding binding : bindings) {
-                  if (resolvedBindingsMap.containsKey(BindingKey.contribution(binding.key()))) {
+                  if (resolvedContributionKeys.contains(binding.key())) {
                     scopes.add(scope);
                     return;
                   }
@@ -435,9 +473,10 @@
       final ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations;
       final ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations;
       final ImmutableSetMultimap<Key, DelegateDeclaration> delegateMultibindingDeclarations;
-      final Map<BindingKey, ResolvedBindings> resolvedBindings;
-      final Deque<BindingKey> cycleStack = new ArrayDeque<>();
-      final Map<BindingKey, Boolean> bindingKeyDependsOnLocalBindingsCache = new HashMap<>();
+      final Map<Key, ResolvedBindings> resolvedContributionBindings = new LinkedHashMap<>();
+      final Map<Key, ResolvedBindings> resolvedMembersInjectionBindings = new LinkedHashMap<>();
+      final Deque<Key> cycleStack = new ArrayDeque<>();
+      final Map<Key, Boolean> keyDependsOnLocalBindingsCache = new HashMap<>();
       final Map<Binding, Boolean> bindingDependsOnLocalBindingsCache = new HashMap<>();
       final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
 
@@ -457,7 +496,6 @@
         this.subcomponentDeclarations = checkNotNull(subcomponentDeclarations);
         this.delegateDeclarations = checkNotNull(delegateDeclarations);
         this.optionalBindingDeclarations = checkNotNull(optionalBindingDeclarations);
-        this.resolvedBindings = Maps.newLinkedHashMap();
         this.explicitMultibindings =
             multibindingContributionsByMultibindingKey(explicitBindingsSet);
         this.delegateMultibindingDeclarations =
@@ -466,9 +504,7 @@
       }
 
       /**
-       * Returns the bindings for the given {@link BindingKey}.
-       *
-       * <p>For {@link BindingKey.Kind#CONTRIBUTION} requests, returns all of:
+       * Returns the resolved contribution bindings for the given {@link Key}:
        *
        * <ul>
        * <li>All explicit bindings for:
@@ -488,95 +524,86 @@
        * <li>An implicit {@link Inject @Inject}-annotated constructor binding if there is one and
        *     there are no explicit bindings or synthetic bindings.
        * </ul>
-       *
-       * <p>For {@link BindingKey.Kind#MEMBERS_INJECTION} requests, returns the {@link
-       * MembersInjectionBinding} for the type.
        */
-      ResolvedBindings lookUpBindings(BindingKey bindingKey) {
-        Key requestKey = bindingKey.key();
-        switch (bindingKey.kind()) {
-          case CONTRIBUTION:
-            Set<ContributionBinding> contributionBindings = new LinkedHashSet<>();
-            ImmutableSet.Builder<ContributionBinding> multibindingContributionsBuilder =
-                ImmutableSet.builder();
-            ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarationsBuilder =
-                ImmutableSet.builder();
-            ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarationsBuilder =
-                ImmutableSet.builder();
-            ImmutableSet.Builder<OptionalBindingDeclaration> optionalBindingDeclarationsBuilder =
-                ImmutableSet.builder();
+      ResolvedBindings lookUpBindings(Key requestKey) {
+        Set<ContributionBinding> contributionBindings = new LinkedHashSet<>();
+        ImmutableSet.Builder<ContributionBinding> multibindingContributionsBuilder =
+            ImmutableSet.builder();
+        ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarationsBuilder =
+            ImmutableSet.builder();
+        ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarationsBuilder =
+            ImmutableSet.builder();
+        ImmutableSet.Builder<OptionalBindingDeclaration> optionalBindingDeclarationsBuilder =
+            ImmutableSet.builder();
 
-            for (Key key : keysMatchingRequest(requestKey)) {
-              contributionBindings.addAll(getExplicitBindings(key));
-              multibindingContributionsBuilder.addAll(getExplicitMultibindings(key));
-              multibindingDeclarationsBuilder.addAll(getMultibindingDeclarations(key));
-              subcomponentDeclarationsBuilder.addAll(getSubcomponentDeclarations(key));
-              optionalBindingDeclarationsBuilder.addAll(getOptionalBindingDeclarations(key));
-            }
-
-            ImmutableSet<ContributionBinding> multibindingContributions =
-                multibindingContributionsBuilder.build();
-            ImmutableSet<MultibindingDeclaration> multibindingDeclarations =
-                multibindingDeclarationsBuilder.build();
-            ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations =
-                subcomponentDeclarationsBuilder.build();
-            ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations =
-                optionalBindingDeclarationsBuilder.build();
-
-            ImmutableSet.Builder<Optional<? extends ContributionBinding>>
-                maybeContributionBindings = ImmutableSet.builder();
-            maybeContributionBindings.add(
-                syntheticMultibinding(
-                    requestKey, multibindingContributions, multibindingDeclarations));
-            syntheticSubcomponentBuilderBinding(subcomponentDeclarations)
-                .ifPresent(
-                    binding -> {
-                      contributionBindings.add(binding);
-                      addSubcomponentToOwningResolver(binding);
-                    });
-            maybeContributionBindings.add(
-                syntheticOptionalBinding(requestKey, optionalBindingDeclarations));
-
-            /* If there are no bindings, add the implicit @Inject-constructed binding if there is
-             * one. */
-            if (contributionBindings.isEmpty()) {
-              maybeContributionBindings.add(
-                  injectBindingRegistry.getOrFindProvisionBinding(requestKey));
-            }
-
-            if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
-              maybeContributionBindings.add(
-                  injectBindingRegistry.getOrFindMembersInjectorProvisionBinding(requestKey));
-            }
-
-            maybeContributionBindings
-                .build()
-                .stream()
-                .filter(Optional::isPresent)
-                .map(Optional::get)
-                .forEach(contributionBindings::add);
-
-            return ResolvedBindings.forContributionBindings(
-                bindingKey,
-                componentDescriptor,
-                indexBindingsByOwningComponent(
-                    bindingKey, ImmutableSet.copyOf(contributionBindings)),
-                multibindingDeclarations,
-                subcomponentDeclarations,
-                optionalBindingDeclarations);
-
-          case MEMBERS_INJECTION:
-            // no explicit deps for members injection, so just look it up
-            Optional<MembersInjectionBinding> binding =
-                injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
-            return binding.isPresent()
-                ? ResolvedBindings.forMembersInjectionBinding(
-                    bindingKey, componentDescriptor, binding.get())
-                : ResolvedBindings.noBindings(bindingKey, componentDescriptor);
-
-          default:
-            throw new AssertionError();
+        for (Key key : keysMatchingRequest(requestKey)) {
+          contributionBindings.addAll(getExplicitBindings(key));
+          multibindingContributionsBuilder.addAll(getExplicitMultibindings(key));
+          multibindingDeclarationsBuilder.addAll(getMultibindingDeclarations(key));
+          subcomponentDeclarationsBuilder.addAll(getSubcomponentDeclarations(key));
+          optionalBindingDeclarationsBuilder.addAll(getOptionalBindingDeclarations(key));
         }
+
+        ImmutableSet<ContributionBinding> multibindingContributions =
+            multibindingContributionsBuilder.build();
+        ImmutableSet<MultibindingDeclaration> multibindingDeclarations =
+            multibindingDeclarationsBuilder.build();
+        ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations =
+            subcomponentDeclarationsBuilder.build();
+        ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations =
+            optionalBindingDeclarationsBuilder.build();
+
+        ImmutableSet.Builder<Optional<? extends ContributionBinding>> maybeContributionBindings =
+            ImmutableSet.builder();
+        maybeContributionBindings.add(
+            syntheticMultibinding(
+                requestKey, multibindingContributions, multibindingDeclarations));
+        syntheticSubcomponentBuilderBinding(subcomponentDeclarations)
+            .ifPresent(
+                binding -> {
+                  contributionBindings.add(binding);
+                  addSubcomponentToOwningResolver(binding);
+                });
+        maybeContributionBindings.add(
+            syntheticOptionalBinding(requestKey, optionalBindingDeclarations));
+
+        /* If there are no bindings, add the implicit @Inject-constructed binding if there is
+         * one. */
+        if (contributionBindings.isEmpty()) {
+          maybeContributionBindings.add(
+              injectBindingRegistry.getOrFindProvisionBinding(requestKey));
+        }
+
+        if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
+          maybeContributionBindings.add(
+              injectBindingRegistry.getOrFindMembersInjectorProvisionBinding(requestKey));
+        }
+
+        maybeContributionBindings
+            .build()
+            .stream()
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .forEach(contributionBindings::add);
+
+        return ResolvedBindings.forContributionBindings(
+            requestKey,
+            componentDescriptor,
+            indexBindingsByOwningComponent(requestKey, ImmutableSet.copyOf(contributionBindings)),
+            multibindingDeclarations,
+            subcomponentDeclarations,
+            optionalBindingDeclarations);
+      }
+
+      /** Returns the resolved members injection bindings for the given {@link Key}. */
+      ResolvedBindings lookUpMembersInjectionBinding(Key requestKey) {
+        // no explicit deps for members injection, so just look it up
+        Optional<MembersInjectionBinding> binding =
+            injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
+        return binding.isPresent()
+            ? ResolvedBindings.forMembersInjectionBinding(
+                requestKey, componentDescriptor, binding.get())
+            : ResolvedBindings.noBindings(requestKey, componentDescriptor);
       }
 
       /**
@@ -675,7 +702,7 @@
         RequestKind requestKind =
             DependencyRequest.extractKindAndType(OptionalType.from(key).valueType()).kind();
         ResolvedBindings underlyingKeyBindings =
-            lookUpBindings(BindingKey.contribution(keyFactory.unwrapOptional(key).get()));
+            lookUpBindings(keyFactory.unwrapOptional(key).get());
         if (underlyingKeyBindings.isEmpty()) {
           return Optional.of(provisionBindingFactory.syntheticAbsentBinding(key));
         } else if (underlyingKeyBindings.bindingTypes().contains(BindingType.PRODUCTION)
@@ -704,16 +731,15 @@
        * delegate key.
        */
       private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDeclaration) {
-        BindingKey delegateBindingKey = delegateDeclaration.delegateRequest().bindingKey();
-
-        if (cycleStack.contains(delegateBindingKey)) {
+        Key delegateKey = delegateDeclaration.delegateRequest().key();
+        if (cycleStack.contains(delegateKey)) {
           return provisionBindingFactory.missingDelegate(delegateDeclaration);
         }
 
         ResolvedBindings resolvedDelegate;
         try {
-          cycleStack.push(delegateBindingKey);
-          resolvedDelegate = lookUpBindings(delegateBindingKey);
+          cycleStack.push(delegateKey);
+          resolvedDelegate = lookUpBindings(delegateKey);
         } finally {
           cycleStack.pop();
         }
@@ -745,13 +771,17 @@
         }
       }
 
+      // TODO(dpb,ronshapiro): requestKey appears to be interchangeable with each binding's .key(),
+      // but should it? We're currently conflating the two all over the place and it would be good
+      // to unify, or if it's necessary, clarify why with docs+tests. Specifically, should we also
+      // be checking these for keysMatchingRequest?
       private ImmutableSetMultimap<ComponentDescriptor, ContributionBinding>
           indexBindingsByOwningComponent(
-              BindingKey bindingKey, Iterable<? extends ContributionBinding> bindings) {
+              Key requestKey, Iterable<? extends ContributionBinding> bindings) {
         ImmutableSetMultimap.Builder<ComponentDescriptor, ContributionBinding> index =
             ImmutableSetMultimap.builder();
         for (ContributionBinding binding : bindings) {
-          index.put(getOwningComponent(bindingKey, binding), binding);
+          index.put(getOwningComponent(requestKey, binding), binding);
         }
         return index.build();
       }
@@ -766,12 +796,11 @@
        * multibinding contributions in the parent, and returns the parent-resolved {@link
        * ResolvedBindings#owningComponent(ContributionBinding)}.
        */
-      private ComponentDescriptor getOwningComponent(
-          BindingKey bindingKey, ContributionBinding binding) {
-        if (isResolvedInParent(bindingKey, binding)
+      private ComponentDescriptor getOwningComponent(Key requestKey, ContributionBinding binding) {
+        if (isResolvedInParent(requestKey, binding)
             && !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
           ResolvedBindings parentResolvedBindings =
-              parentResolver.get().resolvedBindings.get(bindingKey);
+              parentResolver.get().resolvedContributionBindings.get(requestKey);
           return parentResolvedBindings.owningComponent(binding);
         } else {
           return componentDescriptor;
@@ -780,14 +809,13 @@
 
       /**
        * Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain
-       * #resolve resolves} the {@link BindingKey} in this component's parent. Don't resolve
-       * directly in the owning component in case it depends on multibindings in any of its
-       * descendants.
+       * #resolve resolves} the {@link Key} in this component's parent. Don't resolve directly in
+       * the owning component in case it depends on multibindings in any of its descendants.
        */
-      private boolean isResolvedInParent(BindingKey bindingKey, ContributionBinding binding) {
+      private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
         Optional<Resolver> owningResolver = getOwningResolver(binding);
         if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
-          parentResolver.get().resolve(bindingKey);
+          parentResolver.get().resolve(requestKey);
           return true;
         } else {
           return false;
@@ -798,8 +826,7 @@
         if (binding.scope().isPresent() && binding.scope().get().isReusable()) {
           for (Resolver requestResolver : getResolverLineage().reverse()) {
             // If a @Reusable binding was resolved in an ancestor, use that component.
-            if (requestResolver.resolvedBindings.containsKey(
-                BindingKey.contribution(binding.key()))) {
+            if (requestResolver.resolvedContributionBindings.containsKey(binding.key())) {
               return Optional.of(requestResolver);
             }
           }
@@ -935,27 +962,40 @@
         return declarations.build();
       }
 
-      private Optional<ResolvedBindings> getPreviouslyResolvedBindings(
-          final BindingKey bindingKey) {
-        Optional<ResolvedBindings> result = Optional.ofNullable(resolvedBindings.get(bindingKey));
+      /**
+       * Returns the {@link ResolvedBindings} for {@code key} that was resolved in this resolver or
+       * an ancestor resolver. Only checks for {@link ContributionBinding}s as {@link
+       * MembersInjectionBinding}s are not inherited.
+       */
+      private Optional<ResolvedBindings> getPreviouslyResolvedBindings(Key key) {
+        Optional<ResolvedBindings> result =
+            Optional.ofNullable(resolvedContributionBindings.get(key));
         if (result.isPresent()) {
           return result;
         } else if (parentResolver.isPresent()) {
-          return parentResolver.get().getPreviouslyResolvedBindings(bindingKey);
+          return parentResolver.get().getPreviouslyResolvedBindings(key);
         } else {
           return Optional.empty();
         }
       }
 
-      void resolve(BindingKey bindingKey) {
+      private void resolveMembersInjectionMethod(ComponentMethodDescriptor componentMethod) {
+        checkArgument(componentMethod.kind().equals(ComponentMethodKind.MEMBERS_INJECTION));
+        Key key = componentMethod.dependencyRequest().get().key();
+        ResolvedBindings bindings = lookUpMembersInjectionBinding(key);
+        resolveDependencies(bindings);
+        resolvedMembersInjectionBindings.put(key, bindings);
+      }
+
+      void resolve(Key key) {
         // If we find a cycle, stop resolving. The original request will add it with all of the
         // other resolved deps.
-        if (cycleStack.contains(bindingKey)) {
+        if (cycleStack.contains(key)) {
           return;
         }
 
         // If the binding was previously resolved in this (sub)component, don't resolve it again.
-        if (resolvedBindings.containsKey(bindingKey)) {
+        if (resolvedContributionBindings.containsKey(key)) {
           return;
         }
 
@@ -969,51 +1009,72 @@
          * 2. If there are any explicit bindings in this component, they may conflict with those in
          *    the supercomponent, so resolve them here so that conflicts can be caught.
          */
-        if (getPreviouslyResolvedBindings(bindingKey).isPresent()) {
+        if (getPreviouslyResolvedBindings(key).isPresent()) {
           /* Resolve in the parent in case there are multibinding contributions or conflicts in some
            * component between this one and the previously-resolved one. */
-          parentResolver.get().resolve(bindingKey);
-          if (!new LocalDependencyChecker().dependsOnLocalBindings(bindingKey)
-              && getLocalExplicitBindings(bindingKey.key()).isEmpty()) {
+          parentResolver.get().resolve(key);
+          if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
+              && getLocalExplicitBindings(key).isEmpty()) {
             /* Cache the inherited parent component's bindings in case resolving at the parent found
              * bindings in some component between this one and the previously-resolved one. */
             ResolvedBindings inheritedBindings =
-                getPreviouslyResolvedBindings(bindingKey).get().asInheritedIn(componentDescriptor);
-            resolvedBindings.put(bindingKey, inheritedBindings);
+                getPreviouslyResolvedBindings(key).get().asInheritedIn(componentDescriptor);
+            resolvedContributionBindings.put(key, inheritedBindings);
             return;
           }
         }
 
-        cycleStack.push(bindingKey);
+        cycleStack.push(key);
         try {
-          ResolvedBindings bindings = lookUpBindings(bindingKey);
-          for (Binding binding : bindings.ownedBindings()) {
-            for (DependencyRequest dependency : binding.dependencies()) {
-              resolve(dependency.bindingKey());
-            }
-          }
-          resolvedBindings.put(bindingKey, bindings);
+          ResolvedBindings bindings = lookUpBindings(key);
+          resolveDependencies(bindings);
+          resolvedContributionBindings.put(key, bindings);
         } finally {
           cycleStack.pop();
         }
       }
 
-      ImmutableMap<BindingKey, ResolvedBindings> getResolvedBindings() {
-        ImmutableMap.Builder<BindingKey, ResolvedBindings> resolvedBindingsBuilder =
+      /**
+       * {@link #resolve(Key) Resolves} each of the dependencies of the {@link
+       * ResolvedBindings#ownedBindings() owned bindings} of {@code resolvedBindings}.
+       */
+      private void resolveDependencies(ResolvedBindings resolvedBindings) {
+        for (Binding binding : resolvedBindings.ownedBindings()) {
+          for (DependencyRequest dependency : binding.dependencies()) {
+            resolve(dependency.key());
+          }
+        }
+      }
+
+      /**
+       * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
+       * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
+       */
+      ImmutableMap<Key, ResolvedBindings> getResolvedContributionBindings() {
+        ImmutableMap.Builder<Key, ResolvedBindings> builder =
             ImmutableMap.builder();
-        resolvedBindingsBuilder.putAll(resolvedBindings);
+        builder.putAll(resolvedContributionBindings);
         if (parentResolver.isPresent()) {
+          ImmutableMap<Key, ResolvedBindings> parentBindings =
+              parentResolver.get().getResolvedContributionBindings();
           Collection<ResolvedBindings> bindingsResolvedInParent =
-              Maps.difference(parentResolver.get().getResolvedBindings(), resolvedBindings)
+              Maps.difference(parentBindings, resolvedContributionBindings)
                   .entriesOnlyOnLeft()
                   .values();
           for (ResolvedBindings resolvedInParent : bindingsResolvedInParent) {
-            resolvedBindingsBuilder.put(
-                resolvedInParent.bindingKey(),
-                resolvedInParent.asInheritedIn(componentDescriptor));
+            builder.put(
+                resolvedInParent.key(), resolvedInParent.asInheritedIn(componentDescriptor));
           }
         }
-        return resolvedBindingsBuilder.build();
+        return builder.build();
+      }
+
+      /**
+       * Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
+       * resolvers, indexed by {@link ResolvedBindings#key()}.
+       */
+      ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
+        return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
       }
 
       ImmutableSet<ModuleDescriptor> getInheritedModules() {
@@ -1034,37 +1095,33 @@
         private final Set<Object> cycleChecker = new HashSet<>();
 
         /**
-         * Returns {@code true} if any of the bindings resolved for {@code bindingKey} are
-         * multibindings with contributions declared within this component's modules or optional
-         * bindings with present values declared within this component's modules, or if any of its
-         * unscoped dependencies depend on such bindings.
+         * Returns {@code true} if any of the bindings resolved for {@code key} are multibindings
+         * with contributions declared within this component's modules or optional bindings with
+         * present values declared within this component's modules, or if any of its unscoped
+         * dependencies depend on such bindings.
          *
          * <p>We don't care about scoped dependencies because they will never depend on bindings
          * from subcomponents.
          *
-         * @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(BindingKey)} is
-         *     empty
+         * @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
          */
-        boolean dependsOnLocalBindings(BindingKey bindingKey) {
+        boolean dependsOnLocalBindings(Key key) {
           // Don't recur infinitely if there are valid cycles in the dependency graph.
           // http://b/23032377
-          if (!cycleChecker.add(bindingKey)) {
+          if (!cycleChecker.add(key)) {
             return false;
           }
           return reentrantComputeIfAbsent(
-              bindingKeyDependsOnLocalBindingsCache,
-              bindingKey,
-              this::dependsOnLocalBindingsUncached);
+              keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
         }
 
-        private boolean dependsOnLocalBindingsUncached(BindingKey bindingKey) {
+        private boolean dependsOnLocalBindingsUncached(Key key) {
           checkArgument(
-              getPreviouslyResolvedBindings(bindingKey).isPresent(),
+              getPreviouslyResolvedBindings(key).isPresent(),
               "no previously resolved bindings in %s for %s",
               Resolver.this,
-              bindingKey);
-          ResolvedBindings previouslyResolvedBindings =
-              getPreviouslyResolvedBindings(bindingKey).get();
+              key);
+          ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
           if (hasLocalMultibindingContributions(previouslyResolvedBindings)
               || hasLocallyPresentOptionalBinding(previouslyResolvedBindings)) {
             return true;
@@ -1100,7 +1157,7 @@
               // TODO(beder): Figure out what happens with production subcomponents.
               && !binding.bindingType().equals(BindingType.PRODUCTION)) {
             for (DependencyRequest dependency : binding.dependencies()) {
-              if (dependsOnLocalBindings(dependency.bindingKey())) {
+              if (dependsOnLocalBindings(dependency.key())) {
                 return true;
               }
             }
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
index ad27162..4af91c1 100644
--- a/java/dagger/internal/codegen/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -26,7 +26,6 @@
 import static com.google.common.base.Verify.verify;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.BindingType.PRODUCTION;
-import static dagger.internal.codegen.BindingType.PROVISION;
 import static dagger.internal.codegen.ComponentRequirement.Kind.BOUND_INSTANCE;
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentAnnotation;
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
@@ -923,7 +922,7 @@
         }
         StringBuilder errorMessage = requiresErrorMessageBase().append(formatDependencyTrace());
         for (String suggestion :
-            MissingBindingSuggestions.forKey(rootGraph, dependencyRequest().bindingKey())) {
+            MissingBindingSuggestions.forKey(rootGraph, dependencyRequest().key())) {
           errorMessage.append('\n').append(suggestion);
         }
         reportErrorAtEntryPoint(rootGraph, errorMessage.toString());
@@ -1041,7 +1040,7 @@
         verify(
             duplicateDeclarationsByType.keySet().size() > 1,
             "expected multiple contribution types for %s: %s",
-            dependencyRequest().bindingKey(),
+            dependencyRequest().key(),
             duplicateDeclarationsByType);
         ImmutableSortedMap.copyOf(Multimaps.asMap(duplicateDeclarationsByType))
             .forEach(
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index 78a44dc..efc38fb 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -32,6 +32,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.model.Key;
 import dagger.model.RequestKind;
 import java.util.EnumSet;
 import java.util.Optional;
@@ -49,8 +50,7 @@
   private final BindingGraph graph;
   private final DaggerTypes types;
   private final BindingExpressionFactory bindingExpressionFactory;
-  private final Table<BindingKey, RequestKind, BindingExpression> expressions =
-      HashBasedTable.create();
+  private final Table<Key, RequestKind, BindingExpression> expressions = HashBasedTable.create();
 
   ComponentBindingExpressions(
       BindingGraph graph,
@@ -130,9 +130,8 @@
    * @throws IllegalStateException if there is no binding expression that satisfies the dependency
    *     request
    */
-  Expression getDependencyExpression(
-      BindingKey bindingKey, RequestKind requestKind, ClassName requestingClass) {
-    return getBindingExpression(bindingKey, requestKind).getDependencyExpression(requestingClass);
+  Expression getDependencyExpression(Key key, RequestKind requestKind, ClassName requestingClass) {
+    return getBindingExpression(key, requestKind).getDependencyExpression(requestingClass);
   }
 
   /**
@@ -144,7 +143,7 @@
    *     request
    */
   Expression getDependencyExpression(DependencyRequest request, ClassName requestingClass) {
-    return getDependencyExpression(request.bindingKey(), request.kind(), requestingClass);
+    return getDependencyExpression(request.key(), request.kind(), requestingClass);
   }
 
   /**
@@ -158,9 +157,7 @@
   Expression getDependencyExpression(
       FrameworkDependency frameworkDependency, ClassName requestingClass) {
     return getDependencyExpression(
-        frameworkDependency.bindingKey(),
-        frameworkDependency.dependencyRequestKind(),
-        requestingClass);
+        frameworkDependency.key(), frameworkDependency.dependencyRequestKind(), requestingClass);
   }
 
   /**
@@ -197,27 +194,26 @@
   CodeBlock getComponentMethodImplementation(
       ComponentMethodDescriptor componentMethod, ClassName requestingClass) {
     return getBindingExpression(
-            componentMethod.dependencyRequest().get().bindingKey(),
+            componentMethod.dependencyRequest().get().key(),
             componentMethod.dependencyRequest().get().kind())
         .getComponentMethodImplementation(componentMethod, requestingClass);
   }
 
-  private BindingExpression getBindingExpression(
-      BindingKey bindingKey, RequestKind requestKind) {
-    if (graph.resolvedBindings().containsKey(bindingKey)
-        && !graph.resolvedBindings().get(bindingKey).ownedBindings().isEmpty()) {
-      if (!expressions.contains(bindingKey, requestKind)) {
+  private BindingExpression getBindingExpression(Key key, RequestKind requestKind) {
+    ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+    if (resolvedBindings != null && !resolvedBindings.ownedBindings().isEmpty()) {
+      if (!expressions.contains(key, requestKind)) {
         expressions.put(
-            bindingKey, requestKind, bindingExpressionFactory.create(bindingKey, requestKind));
+            key, requestKind, bindingExpressionFactory.create(resolvedBindings, requestKind));
       }
-      return expressions.get(bindingKey, requestKind);
+      return expressions.get(key, requestKind);
     }
     return parent
-        .map(p -> p.getBindingExpression(bindingKey, requestKind))
+        .map(p -> p.getBindingExpression(key, requestKind))
         .orElseThrow(
             () ->
                 new IllegalStateException(
-                    String.format("no expression found for %s-%s", bindingKey, requestKind)));
+                    String.format("no expression found for %s-%s", key, requestKind)));
   }
 
   /** Factory for building a {@link BindingExpression}. */
@@ -266,8 +262,7 @@
     }
 
     /** Creates a binding expression. */
-    BindingExpression create(BindingKey bindingKey, RequestKind requestKind) {
-      ResolvedBindings resolvedBindings = graph.resolvedBindings().get(bindingKey);
+    BindingExpression create(ResolvedBindings resolvedBindings, RequestKind requestKind) {
       switch (resolvedBindings.bindingType()) {
         case MEMBERS_INJECTION:
           return membersInjectionBindingExpression(resolvedBindings);
@@ -405,7 +400,7 @@
           return new SubcomponentBuilderBindingExpression(
               bindingExpression,
               provisionBinding,
-              subcomponentNames.get(bindingExpression.bindingKey()),
+              subcomponentNames.get(bindingExpression.key()),
               types);
 
         case SYNTHETIC_MULTIBOUND_SET:
diff --git a/java/dagger/internal/codegen/ComponentTreeTraverser.java b/java/dagger/internal/codegen/ComponentTreeTraverser.java
index aebe580..36433f4 100644
--- a/java/dagger/internal/codegen/ComponentTreeTraverser.java
+++ b/java/dagger/internal/codegen/ComponentTreeTraverser.java
@@ -25,7 +25,6 @@
 import static com.google.common.collect.Iterables.indexOf;
 import static com.google.common.collect.Iterables.skip;
 import static com.google.common.collect.Multimaps.asMap;
-import static dagger.internal.codegen.BindingKey.contribution;
 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
 import static java.util.Spliterator.ORDERED;
 import static java.util.Spliterator.SIZED;
@@ -262,14 +261,12 @@
      * @param resolvedBindings the object returned by {@link #resolvedBindings()}
      */
     protected void visitResolvedBindings(ResolvedBindings resolvedBindings) {
-      switch (resolvedBindings.bindingKey().kind()) {
-        case MEMBERS_INJECTION:
-          visitMembersInjectionBindings(resolvedBindings);
-          break;
+      if (resolvedBindings.membersInjectionBinding().isPresent()) {
+        visitMembersInjectionBindings(resolvedBindings);
+      }
 
-        case CONTRIBUTION:
-          visitContributionBindings(resolvedBindings);
-          break;
+      if (!resolvedBindings.contributionBindings().isEmpty()) {
+        visitContributionBindings(resolvedBindings);
       }
     }
 
@@ -390,7 +387,7 @@
       }
 
       ResolvedBindings resolvedBindings =
-          bindingGraph.resolvedBindings().get(dependencyRequest.bindingKey());
+          bindingGraph.resolvedBindings(dependencyRequest.kind(), dependencyRequest.key());
       dependencyRequestPath.addLast(dependencyRequest);
       resolvedBindingsPath.addLast(resolvedBindings);
       bindingKeysInPath.add(dependencyRequest.bindingKey());
@@ -422,7 +419,7 @@
       ImmutableSet.Builder<ComponentDescriptor> owningComponents = ImmutableSet.builder();
       for (ContributionBinding binding : bindings) {
         ResolvedBindings resolvedBindings =
-            currentGraph().resolvedBindings().get(contribution(binding.key()));
+            currentGraph().contributionBindings().get(binding.key());
         owningComponents.add(resolvedBindings.owningComponent(binding));
       }
       return componentTreePath.rootmostGraph(owningComponents.build());
diff --git a/java/dagger/internal/codegen/DaggerKythePlugin.java b/java/dagger/internal/codegen/DaggerKythePlugin.java
index c8176ac..fa9eab7 100644
--- a/java/dagger/internal/codegen/DaggerKythePlugin.java
+++ b/java/dagger/internal/codegen/DaggerKythePlugin.java
@@ -70,7 +70,7 @@
   }
 
   private void addNodesForGraph(BindingGraph graph) {
-    for (ResolvedBindings resolvedBindings : graph.resolvedBindings().values()) {
+    for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
       for (Binding binding : resolvedBindings.bindings()) {
         addBindingDeclarationNode(binding);
 
@@ -81,9 +81,7 @@
       resolvedBindings.subcomponentDeclarations().forEach(this::addBindingDeclarationNode);
       resolvedBindings
           .optionalBindingDeclarations()
-          .forEach(
-              declaration ->
-                  addBindingDeclarationNode(declaration, resolvedBindings.bindingKey().key()));
+          .forEach(declaration -> addBindingDeclarationNode(declaration, resolvedBindings.key()));
     }
 
     for (ComponentDescriptor.ComponentMethodDescriptor componentMethod :
diff --git a/java/dagger/internal/codegen/DelegateBindingExpression.java b/java/dagger/internal/codegen/DelegateBindingExpression.java
index 0a52a32..55fb86d 100644
--- a/java/dagger/internal/codegen/DelegateBindingExpression.java
+++ b/java/dagger/internal/codegen/DelegateBindingExpression.java
@@ -56,10 +56,7 @@
     ResolvedBindings resolvedBindings = bindingExpression.resolvedBindings();
     ContributionBinding binding = resolvedBindings.contributionBinding();
     Binding delegateBinding =
-        graph
-            .resolvedBindings()
-            .get(getOnlyElement(binding.dependencies()).bindingKey())
-            .binding();
+        graph.contributionBindings().get(getOnlyElement(binding.dependencies()).key()).binding();
     ScopeKind bindsScope = ScopeKind.get(binding, graph, elements);
     ScopeKind delegateScope = ScopeKind.get(delegateBinding, graph, elements);
     if (bindsScope.isSimilarOrWeakerScopeThan(delegateScope)) {
@@ -74,7 +71,7 @@
   Expression getDependencyExpression(ClassName requestingClass) {
     Expression delegateExpression =
         componentBindingExpressions.getDependencyExpression(
-            getOnlyElement(binding.dependencies()).bindingKey(), requestKind(), requestingClass);
+            getOnlyElement(binding.dependencies()).key(), requestKind(), requestingClass);
 
     TypeMirror contributedType = binding.contributedType();
     switch (requestKind()) {
diff --git a/java/dagger/internal/codegen/FactoryGenerator.java b/java/dagger/internal/codegen/FactoryGenerator.java
index 7e92de7..0c5a26c 100644
--- a/java/dagger/internal/codegen/FactoryGenerator.java
+++ b/java/dagger/internal/codegen/FactoryGenerator.java
@@ -57,6 +57,7 @@
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
+import dagger.model.Key;
 import java.util.List;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
@@ -156,19 +157,19 @@
     return Optional.empty();
   }
 
-  private ImmutableMap<BindingKey, FieldSpec> frameworkFields(ProvisionBinding binding) {
+  private ImmutableMap<Key, FieldSpec> frameworkFields(ProvisionBinding binding) {
     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
     // TODO(user, dpb): Add a test for the case when a Factory parameter is named "module".
     if (binding.requiresModuleInstance()) {
       uniqueFieldNames.claim("module");
     }
-    ImmutableMap.Builder<BindingKey, FieldSpec> fields = ImmutableMap.builder();
+    ImmutableMap.Builder<Key, FieldSpec> fields = ImmutableMap.builder();
     generateBindingFieldsForDependencies(binding)
         .forEach(
-            (bindingKey, frameworkField) -> {
+            (key, frameworkField) -> {
               TypeName type = frameworkField.type();
               String name = uniqueFieldNames.getUniqueName(frameworkField.name());
-              fields.put(bindingKey, FieldSpec.builder(type, name, PRIVATE, FINAL).build());
+              fields.put(key, FieldSpec.builder(type, name, PRIVATE, FINAL).build());
             });
     return fields.build();
   }
@@ -220,7 +221,7 @@
             .addAnnotation(Override.class)
             .addModifiers(PUBLIC);
 
-    ImmutableMap<BindingKey, FieldSpec> frameworkFields = frameworkFields(binding);
+    ImmutableMap<Key, FieldSpec> frameworkFields = frameworkFields(binding);
     CodeBlock parametersCodeBlock =
         makeParametersCodeBlock(
             frameworkFieldUsages(binding.provisionDependencies(), frameworkFields).values());
diff --git a/java/dagger/internal/codegen/FrameworkDependency.java b/java/dagger/internal/codegen/FrameworkDependency.java
index f45fa97..6b69e0e 100644
--- a/java/dagger/internal/codegen/FrameworkDependency.java
+++ b/java/dagger/internal/codegen/FrameworkDependency.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import com.google.auto.value.AutoValue;
+import dagger.model.Key;
 import dagger.model.RequestKind;
 import javax.inject.Provider;
 
@@ -27,13 +28,13 @@
  * <p>In the following example, the binding {@code provideFoo()} has two dependency requests:
  *
  * <ol>
- * <li>{@code Bar bar}
- * <li>{@code Provider<Bar> barProvider}
+ *   <li>{@code Bar bar}
+ *   <li>{@code Provider<Bar> barProvider}
  * </ol>
  *
  * But they both can be satisfied with the same instance of {@code Provider<Bar>}. So one instance
- * of {@code FrameworkDependency} will be used for both. Its {@link #bindingKey()} will be for
- * {@code Bar}, and its {@link #frameworkClass()} will be {@link Provider}.
+ * of {@code FrameworkDependency} will be used for both. Its {@link #key()} will be for {@code Bar},
+ * and its {@link #frameworkClass()} will be {@link Provider}.
  *
  * <pre><code>
  *   {@literal @Provides} static Foo provideFoo(Bar bar, {@literal Provider<Bar>} barProvider) {
@@ -44,10 +45,8 @@
 @AutoValue
 abstract class FrameworkDependency {
 
-  /**
-   * The fully-resolved binding key shared by all the dependency requests.
-   */
-  abstract BindingKey bindingKey();
+  /** The fully-resolved key shared by all the dependency requests. */
+  abstract Key key();
 
   /** The binding type of the framework dependency. */
   abstract BindingType bindingType();
@@ -72,7 +71,7 @@
   }
 
   /** Returns a new instance with the given key and type. */
-  static FrameworkDependency create(BindingKey bindingKey, BindingType bindingType) {
-    return new AutoValue_FrameworkDependency(bindingKey, bindingType);
+  static FrameworkDependency create(Key key, BindingType bindingType) {
+    return new AutoValue_FrameworkDependency(key, bindingType);
   }
 }
diff --git a/java/dagger/internal/codegen/FrameworkField.java b/java/dagger/internal/codegen/FrameworkField.java
index 92d7b9f..9c220ba 100644
--- a/java/dagger/internal/codegen/FrameworkField.java
+++ b/java/dagger/internal/codegen/FrameworkField.java
@@ -93,7 +93,7 @@
   }
 
   private static String frameworkFieldName(ResolvedBindings resolvedBindings) {
-    if (resolvedBindings.bindingKey().kind().equals(BindingKey.Kind.CONTRIBUTION)) {
+    if (!resolvedBindings.contributionBindings().isEmpty()) {
       ContributionBinding binding = resolvedBindings.contributionBinding();
       if (binding.bindingElement().isPresent()) {
         String name = BINDING_ELEMENT_NAME.visit(binding.bindingElement().get(), binding);
diff --git a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
index cd9d16e..20a5f47 100644
--- a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
@@ -77,7 +77,7 @@
     return frameworkType.to(
         requestKind(),
         componentBindingExpressions.getDependencyExpression(
-            bindingKey(), frameworkRequestKind(), requestingClass),
+            key(), frameworkRequestKind(), requestingClass),
         types);
   }
 
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
index d2949fb..17aa1da 100644
--- a/java/dagger/internal/codegen/MapBindingExpression.java
+++ b/java/dagger/internal/codegen/MapBindingExpression.java
@@ -59,7 +59,7 @@
     this.dependencies =
         Maps.toMap(
             binding.dependencies(),
-            dep -> graph.resolvedBindings().get(dep.bindingKey()).contributionBinding());
+            dep -> graph.contributionBindings().get(dep.key()).contributionBinding());
   }
 
   @Override
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
index 46294d9..cd440d5 100644
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -25,8 +25,6 @@
 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.MEMBERS_INJECTOR;
-import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTORS;
 import static dagger.internal.codegen.TypeNames.PRODUCER;
 import static dagger.internal.codegen.TypeNames.PRODUCERS;
 import static dagger.internal.codegen.TypeNames.PROVIDER;
@@ -38,9 +36,7 @@
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeVariableName;
-import dagger.MembersInjector;
 import java.util.List;
-import java.util.NoSuchElementException;
 import java.util.Optional;
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeMirror;
@@ -84,66 +80,52 @@
    * injector.
    */
   static Optional<MemberSelect> staticMemberSelect(ResolvedBindings resolvedBindings) {
-    BindingKey bindingKey = resolvedBindings.bindingKey();
-    switch (bindingKey.kind()) {
-      case CONTRIBUTION:
-        ContributionBinding contributionBinding;
-        try {
-          contributionBinding = resolvedBindings.contributionBinding();
-        } catch (NoSuchElementException e) {
-          throw new AssertionError(
-              "Expected a contribution binding, but none found. *THIS IS A DAGGER BUG* - please "
-                  + "report it on Github with as much context as you can provide. Thanks!"
-                  + "\n\nBinding Key: " + resolvedBindings.bindingKey()
-                  + "\nMultibinding declarations: " + resolvedBindings.multibindingDeclarations()
-                  + "\nSubcomponent declarations: " + resolvedBindings.subcomponentDeclarations()
-                  + "\nOptional binding declarations: "
-                  + resolvedBindings.optionalBindingDeclarations());
-        }
-        if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)
-            && !contributionBinding.scope().isPresent()) {
-          switch (contributionBinding.bindingKind()) {
-            case SYNTHETIC_MULTIBOUND_MAP:
-              return Optional.of(emptyMapFactory(contributionBinding));
-
-            case SYNTHETIC_MULTIBOUND_SET:
-              return Optional.of(emptySetFactory(contributionBinding));
-
-            case INJECTION:
-            case PROVISION:
-              if (bindingKey.key().type().getKind().equals(DECLARED)) {
-                ImmutableList<TypeVariableName> typeVariables =
-                    bindingTypeElementTypeVariableNames(contributionBinding);
-                if (!typeVariables.isEmpty()) {
-                  List<? extends TypeMirror> typeArguments =
-                      ((DeclaredType) bindingKey.key().type()).getTypeArguments();
-                  return Optional.of(
-                      MemberSelect.parameterizedFactoryCreateMethod(
-                          generatedClassNameForBinding(contributionBinding), typeArguments));
-                }
-              }
-              // fall through
-
-            default:
-              return Optional.of(
-                  new StaticMethod(
-                      generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()")));
-          }
-        }
-        break;
-
-      case MEMBERS_INJECTION:
-        Optional<MembersInjectionBinding> membersInjectionBinding =
-            resolvedBindings.membersInjectionBinding();
-        if (membersInjectionBinding.isPresent()
-            && membersInjectionBinding.get().injectionSites().isEmpty()) {
-          return Optional.of(noOpMembersInjector(membersInjectionBinding.get().key().type()));
-        }
-        break;
-
-      default:
-        throw new AssertionError();
+    if (resolvedBindings.contributionBindings().isEmpty()) {
+      throw new AssertionError(
+          "Expected a contribution binding, but none found. *THIS IS A DAGGER BUG* - please "
+              + "report it on Github with as much context as you can provide. Thanks!"
+              + "\n\nKey: "
+              + resolvedBindings.key()
+              + "\nMultibinding declarations: "
+              + resolvedBindings.multibindingDeclarations()
+              + "\nSubcomponent declarations: "
+              + resolvedBindings.subcomponentDeclarations()
+              + "\nOptional binding declarations: "
+              + resolvedBindings.optionalBindingDeclarations());
     }
+    ContributionBinding contributionBinding = resolvedBindings.contributionBinding();
+    if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)
+        && !contributionBinding.scope().isPresent()) {
+      switch (contributionBinding.bindingKind()) {
+        case SYNTHETIC_MULTIBOUND_MAP:
+          return Optional.of(emptyMapFactory(contributionBinding));
+
+        case SYNTHETIC_MULTIBOUND_SET:
+          return Optional.of(emptySetFactory(contributionBinding));
+
+        case INJECTION:
+        case PROVISION:
+          TypeMirror keyType = resolvedBindings.key().type();
+          if (keyType.getKind().equals(DECLARED)) {
+            ImmutableList<TypeVariableName> typeVariables =
+                bindingTypeElementTypeVariableNames(contributionBinding);
+            if (!typeVariables.isEmpty()) {
+              List<? extends TypeMirror> typeArguments =
+                  ((DeclaredType) keyType).getTypeArguments();
+              return Optional.of(
+                  MemberSelect.parameterizedFactoryCreateMethod(
+                      generatedClassNameForBinding(contributionBinding), typeArguments));
+            }
+          }
+          // fall through
+
+        default:
+          return Optional.of(
+              new StaticMethod(
+                  generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()")));
+      }
+    }
+
     return Optional.empty();
   }
 
@@ -173,15 +155,6 @@
     }
   }
 
-  /** Returns the {@link MemberSelect} for a no-op {@link MembersInjector} for the given type. */
-  private static MemberSelect noOpMembersInjector(TypeMirror type) {
-    return new ParameterizedStaticMethod(
-        MEMBERS_INJECTORS,
-        ImmutableList.of(type),
-        CodeBlock.of("noOp()"),
-        MEMBERS_INJECTOR);
-  }
-
   /** A {@link MemberSelect} for a factory of an empty map. */
   private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
     BindingType bindingType = contributionBinding.bindingType();
diff --git a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
index 5450b76..8b0bb31 100644
--- a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
+++ b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
@@ -16,7 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static javax.lang.model.type.TypeKind.VOID;
 
@@ -38,7 +37,6 @@
   MembersInjectionBindingExpression(
       ResolvedBindings resolvedBindings, MembersInjectionMethods membersInjectionMethods) {
     super(resolvedBindings, RequestKind.MEMBERS_INJECTION);
-    checkArgument(bindingKey().kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
     this.binding = resolvedBindings().membersInjectionBinding().get();
     this.membersInjectionMethods = membersInjectionMethods;
   }
diff --git a/java/dagger/internal/codegen/MembersInjectionMethods.java b/java/dagger/internal/codegen/MembersInjectionMethods.java
index 3b785c9..9d40f48 100644
--- a/java/dagger/internal/codegen/MembersInjectionMethods.java
+++ b/java/dagger/internal/codegen/MembersInjectionMethods.java
@@ -16,7 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
@@ -71,11 +70,9 @@
   }
 
   private MethodSpec membersInjectionMethod(Key key) {
-    Binding binding =
-        firstNonNull(
-                graph.resolvedBindings().get(BindingKey.membersInjection(key)),
-                graph.resolvedBindings().get(BindingKey.contribution(key)))
-            .binding();
+    ResolvedBindings resolvedBindings =
+        graph.membersInjectionBindings().getOrDefault(key, graph.contributionBindings().get(key));
+    Binding binding = resolvedBindings.binding();
     TypeMirror keyType = binding.key().type();
     TypeMirror membersInjectedType =
         isTypeAccessibleFrom(keyType, generatedComponentModel.name().packageName())
diff --git a/java/dagger/internal/codegen/MembersInjectorGenerator.java b/java/dagger/internal/codegen/MembersInjectorGenerator.java
index fff1c63..e945f5a 100644
--- a/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ b/java/dagger/internal/codegen/MembersInjectorGenerator.java
@@ -50,6 +50,7 @@
 import dagger.MembersInjector;
 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
+import dagger.model.Key;
 import java.util.Map.Entry;
 import java.util.Optional;
 import javax.annotation.processing.Filer;
@@ -109,9 +110,9 @@
             .addAnnotation(Override.class)
             .addParameter(injectedTypeName, "instance");
 
-    ImmutableMap<BindingKey, FrameworkField> fields = generateBindingFieldsForDependencies(binding);
+    ImmutableMap<Key, FrameworkField> fields = generateBindingFieldsForDependencies(binding);
 
-    ImmutableMap.Builder<BindingKey, FieldSpec> dependencyFieldsBuilder = ImmutableMap.builder();
+    ImmutableMap.Builder<Key, FieldSpec> dependencyFieldsBuilder = ImmutableMap.builder();
 
     MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC);
 
@@ -130,14 +131,14 @@
 
     boolean usesRawFrameworkTypes = false;
     UniqueNameSet fieldNames = new UniqueNameSet();
-    for (Entry<BindingKey, FrameworkField> fieldEntry : fields.entrySet()) {
-      BindingKey dependencyBindingKey = fieldEntry.getKey();
+    for (Entry<Key, FrameworkField> fieldEntry : fields.entrySet()) {
+      Key dependencyKey = fieldEntry.getKey();
       FrameworkField bindingField = fieldEntry.getValue();
 
       // If the dependency type is not visible to this members injector, then use the raw framework
       // type for the field.
       boolean useRawFrameworkType =
-          !isTypeAccessibleFrom(dependencyBindingKey.key().type(), generatedTypeName.packageName());
+          !isTypeAccessibleFrom(dependencyKey.type(), generatedTypeName.packageName());
 
       String fieldName = fieldNames.getUniqueName(bindingField.name());
       TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
@@ -158,7 +159,7 @@
       FieldSpec field = fieldBuilder.build();
       injectorTypeBuilder.addField(field);
       constructorBuilder.addStatement("this.$1N = $1N", field);
-      dependencyFieldsBuilder.put(dependencyBindingKey, field);
+      dependencyFieldsBuilder.put(dependencyKey, field);
       constructorInvocationParameters.add(CodeBlock.of("$N", field));
     }
 
@@ -169,7 +170,7 @@
     injectorTypeBuilder.addMethod(constructorBuilder.build());
     injectorTypeBuilder.addMethod(createMethodBuilder.build());
 
-    ImmutableMap<BindingKey, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
+    ImmutableMap<Key, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
 
     injectMembersBuilder.addCode(
         InjectionSiteMethod.invokeAll(
diff --git a/java/dagger/internal/codegen/MissingBindingSuggestions.java b/java/dagger/internal/codegen/MissingBindingSuggestions.java
index a5311b8..9380863 100644
--- a/java/dagger/internal/codegen/MissingBindingSuggestions.java
+++ b/java/dagger/internal/codegen/MissingBindingSuggestions.java
@@ -17,6 +17,7 @@
 package dagger.internal.codegen;
 
 import com.google.common.collect.ImmutableList;
+import dagger.model.Key;
 import java.util.ArrayDeque;
 import java.util.Deque;
 
@@ -31,14 +32,14 @@
    * Searches the entire binding graph from the top-level graph for a binding matching
    * {@code key}.
    */
-  static ImmutableList<String> forKey(BindingGraph topLevelGraph, BindingKey key) {
+  static ImmutableList<String> forKey(BindingGraph topLevelGraph, Key key) {
     ImmutableList.Builder<String> resolutions = new ImmutableList.Builder<>();
     Deque<BindingGraph> graphsToTry = new ArrayDeque<>();
 
     graphsToTry.add(topLevelGraph);
     do {
       BindingGraph graph = graphsToTry.removeLast();
-      ResolvedBindings bindings = graph.resolvedBindings().get(key);
+      ResolvedBindings bindings = graph.contributionBindings().get(key);
       if ((bindings == null) || bindings.bindings().isEmpty()) {
         graphsToTry.addAll(graph.subgraphs());
       } else {
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
index c71e91d..29f874f 100644
--- a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
@@ -172,7 +172,7 @@
       ComponentMethodDescriptor componentMethod) {
     return componentMethod
         .dependencyRequest()
-        .filter(request -> request.bindingKey().equals(bindingKey()))
+        .filter(request -> request.key().equals(key()))
         .filter(request -> request.kind().equals(requestKind()))
         .isPresent();
   }
@@ -274,8 +274,7 @@
                 .addStatement(
                     "return $L",
                     componentBindingExpressions
-                        .getDependencyExpression(
-                            bindingKey(), RequestKind.INSTANCE, componentName())
+                        .getDependencyExpression(key(), RequestKind.INSTANCE, componentName())
                         .codeBlock())
                 .build())
         .build();
diff --git a/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
index d702e9d..bc2f494 100644
--- a/java/dagger/internal/codegen/ProducerFactoryGenerator.java
+++ b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
@@ -57,6 +57,7 @@
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
+import dagger.model.Key;
 import dagger.model.RequestKind;
 import dagger.producers.Producer;
 import java.util.Map;
@@ -107,7 +108,7 @@
             .superclass(abstractProducerOf(providedTypeName));
 
     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
-    ImmutableMap.Builder<BindingKey, FieldSpec> fieldsBuilder = ImmutableMap.builder();
+    ImmutableMap.Builder<Key, FieldSpec> fieldsBuilder = ImmutableMap.builder();
 
     MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC);
 
@@ -121,9 +122,9 @@
                     TypeName.get(binding.bindingTypeElement().get().asType())))
             : Optional.empty();
 
-    for (Map.Entry<BindingKey, FrameworkField> entry :
+    for (Map.Entry<Key, FrameworkField> entry :
         generateBindingFieldsForDependencies(binding).entrySet()) {
-      BindingKey bindingKey = entry.getKey();
+      Key key = entry.getKey();
       FrameworkField bindingField = entry.getValue();
       FieldSpec field =
           addFieldAndConstructorParameter(
@@ -131,13 +132,13 @@
               constructorBuilder,
               uniqueFieldNames.getUniqueName(bindingField.name()),
               bindingField.type());
-      fieldsBuilder.put(bindingKey, field);
+      fieldsBuilder.put(key, field);
     }
-    ImmutableMap<BindingKey, FieldSpec> fields = fieldsBuilder.build();
+    ImmutableMap<Key, FieldSpec> fields = fieldsBuilder.build();
 
     constructorBuilder.addStatement(
         "super($N, $L)",
-        fields.get(binding.monitorRequest().get().bindingKey()),
+        fields.get(binding.monitorRequest().get().key()),
         producerTokenConstruction(generatedTypeName, binding));
 
     if (binding.requiresModuleInstance()) {
@@ -157,7 +158,7 @@
     ImmutableList<DependencyRequest> asyncDependencies = asyncDependencies(binding);
     for (DependencyRequest dependency : asyncDependencies) {
       TypeName futureType = listenableFutureOf(asyncDependencyType(dependency));
-      CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency.bindingKey()));
+      CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency.key()));
       computeMethodBuilder.addStatement(
           "$T $L = $L",
           futureType,
@@ -272,10 +273,10 @@
 
   /** Represents the transformation of an input future by a producer method. */
   abstract static class FutureTransform {
-    protected final ImmutableMap<BindingKey, FieldSpec> fields;
+    protected final ImmutableMap<Key, FieldSpec> fields;
     protected final ProductionBinding binding;
 
-    FutureTransform(ImmutableMap<BindingKey, FieldSpec> fields, ProductionBinding binding) {
+    FutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
       this.fields = fields;
       this.binding = binding;
     }
@@ -298,7 +299,7 @@
     }
 
     static FutureTransform create(
-        ImmutableMap<BindingKey, FieldSpec> fields,
+        ImmutableMap<Key, FieldSpec> fields,
         ProductionBinding binding,
         ImmutableList<DependencyRequest> asyncDependencies) {
       if (asyncDependencies.isEmpty()) {
@@ -313,7 +314,7 @@
   }
 
   static final class NoArgFutureTransform extends FutureTransform {
-    NoArgFutureTransform(ImmutableMap<BindingKey, FieldSpec> fields, ProductionBinding binding) {
+    NoArgFutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
       super(fields, binding);
     }
 
@@ -338,7 +339,7 @@
       for (DependencyRequest dependency : binding.explicitDependencies()) {
         parameterCodeBlocks.add(
             frameworkTypeUsageStatement(
-                CodeBlock.of("$N", fields.get(dependency.bindingKey())), dependency.kind()));
+                CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
       }
       return parameterCodeBlocks.build();
     }
@@ -348,7 +349,7 @@
     private final DependencyRequest asyncDependency;
 
     SingleArgFutureTransform(
-        ImmutableMap<BindingKey, FieldSpec> fields,
+        ImmutableMap<Key, FieldSpec> fields,
         ProductionBinding binding,
         DependencyRequest asyncDependency) {
       super(fields, binding);
@@ -382,7 +383,7 @@
           parameterCodeBlocks.add(
               // TODO(ronshapiro) extract this into a method shared by FutureTransform subclasses
               frameworkTypeUsageStatement(
-                  CodeBlock.of("$N", fields.get(dependency.bindingKey())), dependency.kind()));
+                  CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
         }
       }
       return parameterCodeBlocks.build();
@@ -393,7 +394,7 @@
     private final ImmutableList<DependencyRequest> asyncDependencies;
 
     MultiArgFutureTransform(
-        ImmutableMap<BindingKey, FieldSpec> fields,
+        ImmutableMap<Key, FieldSpec> fields,
         ProductionBinding binding,
         ImmutableList<DependencyRequest> asyncDependencies) {
       super(fields, binding);
@@ -456,7 +457,7 @@
   }
 
   private static ImmutableList<CodeBlock> getParameterCodeBlocks(
-      ProductionBinding binding, ImmutableMap<BindingKey, FieldSpec> fields, String listArgName) {
+      ProductionBinding binding, ImmutableMap<Key, FieldSpec> fields, String listArgName) {
     int argIndex = 0;
     ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
     for (DependencyRequest dependency : binding.explicitDependencies()) {
@@ -468,7 +469,7 @@
       } else {
         codeBlocks.add(
             frameworkTypeUsageStatement(
-                CodeBlock.of("$N", fields.get(dependency.bindingKey())), dependency.kind()));
+                CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind()));
       }
     }
     return codeBlocks.build();
diff --git a/java/dagger/internal/codegen/ProducerFromProviderFieldInitializer.java b/java/dagger/internal/codegen/ProducerFromProviderFieldInitializer.java
index 0b3c9e6..2559fe2 100644
--- a/java/dagger/internal/codegen/ProducerFromProviderFieldInitializer.java
+++ b/java/dagger/internal/codegen/ProducerFromProviderFieldInitializer.java
@@ -47,7 +47,7 @@
         RequestKind.PRODUCER,
         componentBindingExpressions
             .getDependencyExpression(
-                FrameworkDependency.create(resolvedBindings.bindingKey(), PROVISION),
+                FrameworkDependency.create(resolvedBindings.key(), PROVISION),
                 generatedComponentModel.name())
             .codeBlock());
   }
diff --git a/java/dagger/internal/codegen/ProviderOrProducerFieldInitializer.java b/java/dagger/internal/codegen/ProviderOrProducerFieldInitializer.java
index 6c69332..45abc23 100644
--- a/java/dagger/internal/codegen/ProviderOrProducerFieldInitializer.java
+++ b/java/dagger/internal/codegen/ProviderOrProducerFieldInitializer.java
@@ -25,7 +25,6 @@
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.BindingKey.Kind.CONTRIBUTION;
 import static dagger.internal.codegen.CodeBlocks.makeParametersCodeBlock;
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
 import static dagger.internal.codegen.GeneratedComponentModel.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
@@ -103,7 +102,7 @@
       BindingGraph graph,
       OptionalFactories optionalFactories) {
     super(generatedComponentModel, componentBindingExpressions, resolvedBindings);
-    checkArgument(resolvedBindings.bindingKey().kind().equals(CONTRIBUTION));
+    checkArgument(resolvedBindings.contributionBindings().size() == 1);
     this.subcomponentNames = checkNotNull(subcomponentNames);
     this.componentRequirementFields = checkNotNull(componentRequirementFields);
     this.referenceReleasingManagerFields = checkNotNull(referenceReleasingManagerFields);
@@ -378,7 +377,7 @@
     CodeBlock.Builder builderMethodCalls = CodeBlock.builder();
     for (FrameworkDependency frameworkDependency : binding.frameworkDependencies()) {
       ContributionType contributionType =
-          graph.resolvedBindings().get(frameworkDependency.bindingKey()).contributionType();
+          graph.contributionBindings().get(frameworkDependency.key()).contributionType();
       String methodName;
       String methodNameSuffix = frameworkDependency.frameworkClass().getSimpleName();
       switch (contributionType) {
@@ -436,9 +435,8 @@
     codeBlocks.add(builderCall.build());
 
     for (FrameworkDependency frameworkDependency : frameworkDependencies) {
-      BindingKey bindingKey = frameworkDependency.bindingKey();
       ContributionBinding contributionBinding =
-          graph.resolvedBindings().get(bindingKey).contributionBinding();
+          graph.contributionBindings().get(frameworkDependency.key()).contributionBinding();
       CodeBlock value =
           potentiallyCast(
               useRawTypes,
diff --git a/java/dagger/internal/codegen/ResolvedBindings.java b/java/dagger/internal/codegen/ResolvedBindings.java
index 938ca1a..52142fa 100644
--- a/java/dagger/internal/codegen/ResolvedBindings.java
+++ b/java/dagger/internal/codegen/ResolvedBindings.java
@@ -34,8 +34,14 @@
 import java.util.Optional;
 
 /**
- * The collection of bindings that have been resolved for a binding key. For valid graphs, contains
- * exactly one binding.
+ * The collection of bindings that have been resolved for a key. For valid graphs, contains exactly
+ * one binding.
+ *
+ * <p>Separate {@link ResolvedBindings} instances should be used if a {@link
+ * MembersInjectionBinding} and a {@link ProvisionBinding} for the same key exist in the same
+ * component. (this will only happen if a type has an {@code @Inject} constructor and members, the
+ * component has a {@link ComponentDescriptor.ComponentMethodKind#MEMBERS_INJECTION members
+ * injection method}, and the type is also requested normally.
  *
  * @author Gregory Kick
  */
@@ -44,69 +50,45 @@
   /**
    * The binding key for which the {@link #bindings()} have been resolved.
    */
-  abstract BindingKey bindingKey();
+  abstract Key key();
 
   /** The component in which the bindings in {@link #ownedBindings()}, were resolved. */
   abstract ComponentDescriptor owningComponent();
 
   /**
-   * The contribution bindings for {@link #bindingKey()} that were resolved in {@link
-   * #owningComponent()} or its ancestor components, indexed by the component in which the binding
-   * was resolved. If {@link #bindingKey()}'s kind is not {@link BindingKey.Kind#CONTRIBUTION}, this
-   * is empty.
+   * The contribution bindings for {@link #key()} that were resolved in {@link #owningComponent()}
+   * or its ancestor components, indexed by the component in which the binding was resolved.
    */
   abstract ImmutableSetMultimap<ComponentDescriptor, ContributionBinding> allContributionBindings();
 
   /**
-   * The members-injection bindings for {@link #bindingKey()} that were resolved in {@link
+   * The members-injection bindings for {@link #key()} that were resolved in {@link
    * #owningComponent()} or its ancestor components, indexed by the component in which the binding
-   * was resolved. If {@link #bindingKey()}'s kind is not {@link BindingKey.Kind#MEMBERS_INJECTION},
-   * this is empty.
+   * was resolved.
    */
   abstract ImmutableMap<ComponentDescriptor, MembersInjectionBinding> allMembersInjectionBindings();
 
-  final Key key() {
-    return bindingKey().key();
-  }
-  
-  /**
-   * The multibinding declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is not
-   * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
-   */
+  /** The multibinding declarations for {@link #key()}. */
   abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
 
-  /**
-   * The subcomponent declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is not
-   * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
-   */
+  /** The subcomponent declarations for {@link #key()}. */
   abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
 
   /**
-   * The optional binding declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is
-   * not {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
+   * The optional binding declarations for {@link #key()}.
    */
   abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
 
   /**
-   * All bindings for {@link #bindingKey()}, indexed by the component in which the binding was
-   * resolved.
+   * All bindings for {@link #key()}, indexed by the component in which the binding was resolved.
    */
   private ImmutableSetMultimap<ComponentDescriptor, ? extends Binding> allBindings() {
-    switch (bindingKey().kind()) {
-      case CONTRIBUTION:
-        return allContributionBindings();
-
-      case MEMBERS_INJECTION:
-        return allMembersInjectionBindings().asMultimap();
-
-      default:
-        throw new AssertionError(bindingKey());
-    }
+    return !allMembersInjectionBindings().isEmpty()
+        ? allMembersInjectionBindings().asMultimap()
+        : allContributionBindings();
   }
-  
-  /**
-   * All bindings for {@link #bindingKey()}, regardless of in which component they were resolved.
-   */
+
+  /** All bindings for {@link #key()}, regardless of in which component they were resolved. */
   ImmutableSet<? extends Binding> bindings() {
     return ImmutableSet.copyOf(allBindings().values());
   }
@@ -132,9 +114,7 @@
         && subcomponentDeclarations().isEmpty();
   }
 
-  /**
-   * All bindings for {@link #bindingKey()} that were resolved in {@link #owningComponent()}.
-   */
+  /** All bindings for {@link #key()} that were resolved in {@link #owningComponent()}. */
   ImmutableSet<? extends Binding> ownedBindings() {
     return allBindings().get(owningComponent());
   }
@@ -152,7 +132,7 @@
     checkArgument(
         contributionBindings().contains(binding),
         "binding is not resolved for %s: %s",
-        bindingKey(),
+        key(),
         binding);
     return getOnlyElement(allContributionBindings().inverse().get(binding));
   }
@@ -171,15 +151,14 @@
 
   /** Creates a {@link ResolvedBindings} for contribution bindings. */
   static ResolvedBindings forContributionBindings(
-      BindingKey bindingKey,
+      Key key,
       ComponentDescriptor owningComponent,
       Multimap<ComponentDescriptor, ? extends ContributionBinding> contributionBindings,
       Iterable<MultibindingDeclaration> multibindings,
       Iterable<SubcomponentDeclaration> subcomponentDeclarations,
       Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
-    checkArgument(bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION));
     return new AutoValue_ResolvedBindings(
-        bindingKey,
+        key,
         owningComponent,
         ImmutableSetMultimap.copyOf(contributionBindings),
         ImmutableMap.of(),
@@ -192,12 +171,11 @@
    * Creates a {@link ResolvedBindings} for members injection bindings.
    */
   static ResolvedBindings forMembersInjectionBinding(
-      BindingKey bindingKey,
+      Key key,
       ComponentDescriptor owningComponent,
       MembersInjectionBinding ownedMembersInjectionBinding) {
-    checkArgument(bindingKey.kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
     return new AutoValue_ResolvedBindings(
-        bindingKey,
+        key,
         owningComponent,
         ImmutableSetMultimap.of(),
         ImmutableMap.of(owningComponent, ownedMembersInjectionBinding),
@@ -209,9 +187,9 @@
   /**
    * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
    */
-  static ResolvedBindings noBindings(BindingKey bindingKey, ComponentDescriptor owningComponent) {
+  static ResolvedBindings noBindings(Key key, ComponentDescriptor owningComponent) {
     return new AutoValue_ResolvedBindings(
-        bindingKey,
+        key,
         owningComponent,
         ImmutableSetMultimap.of(),
         ImmutableMap.of(),
@@ -221,12 +199,12 @@
   }
 
   /**
-   * Returns a {@code ResolvedBindings} with the same {@link #bindingKey()} and {@link #bindings()}
+   * Returns a {@code ResolvedBindings} with the same {@link #key()} and {@link #bindings()}
    * as this one, but no {@link #ownedBindings()}.
    */
   ResolvedBindings asInheritedIn(ComponentDescriptor owningComponent) {
     return new AutoValue_ResolvedBindings(
-        bindingKey(),
+        key(),
         owningComponent,
         allContributionBindings(),
         allMembersInjectionBindings(),
@@ -260,7 +238,7 @@
    * @throws IllegalStateException if {@link #isEmpty()} or the binding types conflict
    */
   final BindingType bindingType() {
-    checkState(!isEmpty(), "empty bindings for %s", bindingKey());
+    checkState(!isEmpty(), "empty bindings for %s", key());
     if (bindings().isEmpty()
         && (!multibindingDeclarations().isEmpty() || !subcomponentDeclarations().isEmpty())) {
       // Only multibinding declarations, so assume provision.
diff --git a/java/dagger/internal/codegen/SetBindingExpression.java b/java/dagger/internal/codegen/SetBindingExpression.java
index 55816e8..a885d8c 100644
--- a/java/dagger/internal/codegen/SetBindingExpression.java
+++ b/java/dagger/internal/codegen/SetBindingExpression.java
@@ -135,8 +135,8 @@
 
   private boolean isSingleValue(DependencyRequest dependency) {
     return graph
-        .resolvedBindings()
-        .get(dependency.bindingKey())
+        .contributionBindings()
+        .get(dependency.key())
         .contributionBinding()
         .contributionType()
         .equals(ContributionType.SET);
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index 17ca38b..b354a9f 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -54,6 +54,7 @@
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeVariableName;
 import dagger.internal.SetFactory;
+import dagger.model.Key;
 import dagger.model.RequestKind;
 import dagger.producers.Produced;
 import dagger.producers.Producer;
@@ -107,18 +108,18 @@
    *
    * @param binding must be an unresolved binding (type parameters must match its type element's)
    */
-  static ImmutableMap<BindingKey, FrameworkField> generateBindingFieldsForDependencies(
+  static ImmutableMap<Key, FrameworkField> generateBindingFieldsForDependencies(
       Binding binding) {
     checkArgument(!binding.unresolved().isPresent(), "binding must be unresolved: %s", binding);
 
-    ImmutableMap.Builder<BindingKey, FrameworkField> bindingFields = ImmutableMap.builder();
+    ImmutableMap.Builder<Key, FrameworkField> bindingFields = ImmutableMap.builder();
     for (Binding.DependencyAssociation dependencyAssociation : binding.dependencyAssociations()) {
       FrameworkDependency frameworkDependency = dependencyAssociation.frameworkDependency();
       bindingFields.put(
-          frameworkDependency.bindingKey(),
+          frameworkDependency.key(),
           FrameworkField.create(
               ClassName.get(frameworkDependency.frameworkClass()),
-              TypeName.get(frameworkDependency.bindingKey().key().type()),
+              TypeName.get(frameworkDependency.key().type()),
               fieldNameForDependency(dependencyAssociation.dependencyRequests())));
     }
     return bindingFields.build();
@@ -170,12 +171,11 @@
    * #frameworkTypeUsageStatement(CodeBlock, RequestKind) use them}.
    */
   static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
-      ImmutableSet<DependencyRequest> dependencies, ImmutableMap<BindingKey, FieldSpec> fields) {
+      ImmutableSet<DependencyRequest> dependencies, ImmutableMap<Key, FieldSpec> fields) {
     return Maps.toMap(
         dependencies,
         dep ->
-            frameworkTypeUsageStatement(
-                CodeBlock.of("$N", fields.get(dep.bindingKey())), dep.kind()));
+            frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep.key())), dep.kind()));
   }
 
   /**
diff --git a/java/dagger/internal/codegen/SubcomponentNames.java b/java/dagger/internal/codegen/SubcomponentNames.java
index 451f5e5..2fbc5d3 100644
--- a/java/dagger/internal/codegen/SubcomponentNames.java
+++ b/java/dagger/internal/codegen/SubcomponentNames.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimaps;
+import dagger.model.Key;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -40,11 +41,11 @@
 final class SubcomponentNames {
   private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
   private final ImmutableMap<ComponentDescriptor, String> namesByDescriptor;
-  private final ImmutableMap<BindingKey, String> namesByBindingKey;
+  private final ImmutableMap<Key, String> namesByKey;
 
   SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) {
     this.namesByDescriptor = namesByDescriptor(graph);
-    this.namesByBindingKey = namesByBindingKey(keyFactory, namesByDescriptor);
+    this.namesByKey = namesByKey(keyFactory, namesByDescriptor);
   }
 
   /** Returns the simple component name for the given {@link ComponentDescriptor}. */
@@ -52,9 +53,9 @@
     return namesByDescriptor.get(componentDescriptor);
   }
 
-  /** Returns the simple component name for the given subcomponent builder {@link BindingKey}. */
-  String get(BindingKey bindingKey) {
-    return namesByBindingKey.get(bindingKey);
+  /** Returns the simple component name for the given subcomponent builder {@link Key}. */
+  String get(Key key) {
+    return namesByKey.get(key);
   }
 
   private static ImmutableMap<ComponentDescriptor, String> namesByDescriptor(BindingGraph graph) {
@@ -77,15 +78,14 @@
     return ImmutableMap.copyOf(subcomponentImplSimpleNames);
   }
 
-  private static ImmutableMap<BindingKey, String> namesByBindingKey(
+  private static ImmutableMap<Key, String> namesByKey(
       KeyFactory keyFactory, ImmutableMap<ComponentDescriptor, String> subcomponentNames) {
-    ImmutableMap.Builder<BindingKey, String> builder = ImmutableMap.builder();
+    ImmutableMap.Builder<Key, String> builder = ImmutableMap.builder();
     subcomponentNames.forEach(
         (component, name) -> {
           if (component.builderSpec().isPresent()) {
             TypeMirror builderType = component.builderSpec().get().builderDefinitionType().asType();
-            builder.put(
-                BindingKey.contribution(keyFactory.forSubcomponentBuilder(builderType)), name);
+            builder.put(keyFactory.forSubcomponentBuilder(builderType), name);
           }
         });
     return builder.build();