Automated rollback of commit 1a5123d0

*** Original change description ***

Inline requests for map bindings

Under the hood, remove ContributionBinding.Kind.SYNTHETIC_MAP. Map<K,V> and Map<K, Produced<V>> bindings now act just like Map<K, FrameworkType<V>>, and have dependencies on their contributions directly instead of wrapping a another FrameworkType<Map<K, FrameworkType<V>>>

***

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166785831
diff --git a/java/dagger/internal/MapBuilder.java b/java/dagger/internal/MapBuilder.java
deleted file mode 100644
index 1560491..0000000
--- a/java/dagger/internal/MapBuilder.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * A fluent builder class that returns a {@link Map}. Used in component implementations where a map
- * must be created in one fluent statement for inlined request fulfillments.
- */
-public final class MapBuilder<K, V> {
-  private final Map<K, V> contributions;
-
-  private MapBuilder(int size) {
-    contributions = newLinkedHashMapWithExpectedSize(size);
-  }
-
-  /**
-   * Creates a new {@link MapBuilder} with {@code size} elements.
-   */
-  public static <K, V> MapBuilder<K, V> newMapBuilder(int size) {
-    return new MapBuilder<>(size);
-  }
-
-  public MapBuilder<K, V> put(K key, V value) {
-    contributions.put(key, value);
-    return this;
-  }
-
-  public Map<K, V> build() {
-    switch (contributions.size()) {
-      case 0:
-        return Collections.emptyMap();
-      default:
-        return Collections.unmodifiableMap(contributions);
-    }
-  }
-}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
index b6f925a..d4b9189 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -17,11 +17,8 @@
 package dagger.internal;
 
 import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-import static dagger.internal.Preconditions.checkNotNull;
 import static java.util.Collections.unmodifiableMap;
 
-import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import javax.inject.Provider;
@@ -35,31 +32,21 @@
  *
  */
 public final class MapFactory<K, V> implements Factory<Map<K, V>> {
-  private static final Provider<Map<Object, Object>> EMPTY =
-      InstanceFactory.create(Collections.emptyMap());
-
   private final Map<K, Provider<V>> contributingMap;
 
-  /**
-   * Returns a new {@link Builder}
-   */
-  public static <K, V> Builder<K, V> builder(int size) {
-    return new Builder<>(size);
-  }
-
-  /**
-   * Returns a factory of an empty map.
-   */
-  @SuppressWarnings("unchecked") // safe contravariant cast
-  public static <K, V> Provider<Map<K, V>> emptyMapProvider() {
-    return (Provider<Map<K, V>>) (Provider) EMPTY;
-  }
-
   private MapFactory(Map<K, Provider<V>> map) {
     this.contributingMap = unmodifiableMap(map);
   }
 
   /**
+   * Returns a new MapFactory.
+   */
+  public static <K, V> MapFactory<K, V> create(Provider<Map<K, Provider<V>>> mapProviderFactory) {
+    Map<K, Provider<V>> map = mapProviderFactory.get();
+    return new MapFactory<K, V>(map);
+  }
+
+  /**
    * Returns a {@code Map<K, V>} whose iteration order is that of the elements
    * given by each of the providers, which are invoked in the order given at creation.
    */
@@ -71,25 +58,4 @@
     }
     return unmodifiableMap(result);
   }
-
-  // TODO(ronshapiro): can we merge the builders? Or maybe just use a (Immutable)MapBuilder?
-  /** A builder for {@link MapFactory}. */
-  public static final class Builder<K, V> {
-    private final LinkedHashMap<K, Provider<V>> map;
-
-    private Builder(int size) {
-      this.map = newLinkedHashMapWithExpectedSize(size);
-    }
-
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
-      return this;
-    }
-
-    /** Returns a new {@link MapProviderFactory}. */
-    public MapFactory<K, V> build() {
-      return new MapFactory<>(map);
-    }
-  }
 }
diff --git a/java/dagger/internal/MapProviderFactory.java b/java/dagger/internal/MapProviderFactory.java
index 2458a3c..c27beb2 100644
--- a/java/dagger/internal/MapProviderFactory.java
+++ b/java/dagger/internal/MapProviderFactory.java
@@ -17,10 +17,10 @@
 package dagger.internal;
 
 import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-import static dagger.internal.Preconditions.checkNotNull;
 import static java.util.Collections.unmodifiableMap;
 
 import dagger.Lazy;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import javax.inject.Provider;
@@ -34,13 +34,24 @@
  */
 public final class MapProviderFactory<K, V>
     implements Factory<Map<K, Provider<V>>>, Lazy<Map<K, Provider<V>>> {
+  private static final MapProviderFactory<Object, Object> EMPTY =
+      new MapProviderFactory<Object, Object>(Collections.<Object, Provider<Object>>emptyMap());
+
   private final Map<K, Provider<V>> contributingMap;
 
   /**
    * Returns a new {@link Builder}
    */
   public static <K, V> Builder<K, V> builder(int size) {
-    return new Builder<>(size);
+    return new Builder<K, V>(size);
+  }
+
+  /**
+   * Returns a factory of an empty map.
+   */
+  @SuppressWarnings("unchecked") // safe contravariant cast
+  public static <K, V> MapProviderFactory<K, V> empty() {
+    return (MapProviderFactory<K, V>) EMPTY;
   }
 
   private MapProviderFactory(Map<K, Provider<V>> contributingMap) {
@@ -57,23 +68,37 @@
     return this.contributingMap;
   }
 
-  /** A builder for {@link MapProviderFactory}. */
+  /**
+   * A builder to help build the {@link MapProviderFactory}
+   */
   public static final class Builder<K, V> {
-    private final LinkedHashMap<K, Provider<V>> map;
+    private final LinkedHashMap<K, Provider<V>> mapBuilder;
 
     private Builder(int size) {
-      this.map = newLinkedHashMapWithExpectedSize(size);
+      // TODO(user): consider which way to initialize mapBuilder is better
+      this.mapBuilder = newLinkedHashMapWithExpectedSize(size);
     }
 
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
-      return this;
-    }
-
-    /** Returns a new {@link MapProviderFactory}. */
+    /**
+     * Returns a new {@link MapProviderFactory}
+     */
     public MapProviderFactory<K, V> build() {
-      return new MapProviderFactory<>(map);
+      return new MapProviderFactory<K, V>(this.mapBuilder);
+    }
+
+    /**
+     * Associate k with providerOfValue in {@code Builder}
+     */
+    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+      if (key == null) {
+        throw new NullPointerException("The key is null");
+      }
+      if (providerOfValue == null) {
+        throw new NullPointerException("The provider of the value is null");
+      }
+
+      this.mapBuilder.put(key, providerOfValue);
+      return this;
     }
   }
 }
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 06c8e64..093e5c5 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -198,9 +198,6 @@
         case SYNTHETIC_MULTIBOUND_SET:
           return new SetBindingExpression(
               provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
-        case SYNTHETIC_MULTIBOUND_MAP:
-          return new MapBindingExpression(
-              provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
         case SYNTHETIC_OPTIONAL_BINDING:
           return new OptionalBindingExpression(
               provisionBinding, bindingExpression, componentBindingExpressions);
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index 14b9c7d..110d08e 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -35,6 +35,7 @@
 import com.google.auto.common.MoreTypes;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.VerifyException;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -518,6 +519,7 @@
 
             ImmutableSet.Builder<Optional<ContributionBinding>> maybeContributionBindings =
                 ImmutableSet.builder();
+            maybeContributionBindings.add(syntheticMapOfValuesBinding(requestKey));
             maybeContributionBindings.add(
                 syntheticMultibinding(
                     requestKey, multibindingContributions, multibindingDeclarations));
@@ -583,17 +585,71 @@
             owningResolver.componentDescriptor.subcomponentsByBuilderType().get(builderType));
       }
 
-      private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
+      private Iterable<Key> keysMatchingRequest(Key requestKey) {
         ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
         keys.add(requestKey);
         keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
         keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
         keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
-        keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
         return keys.build();
       }
 
       /**
+       * If {@code key} is a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, and there are any
+       * multibinding contributions or declarations that apply to that map, returns a synthetic
+       * binding for the {@code key} that depends on an {@linkplain #syntheticMultibinding(Key,
+       * Iterable, Iterable) underlying synthetic multibinding}.
+       *
+       * <p>The returned binding has the same {@link BindingType} as the underlying synthetic
+       * multibinding.
+       */
+      private Optional<ContributionBinding> syntheticMapOfValuesBinding(final Key key) {
+        return syntheticMultibinding(
+                key,
+                multibindingContributionsForValueMap(key),
+                multibindingDeclarationsForValueMap(key))
+            .map(
+                syntheticMultibinding -> {
+                  switch (syntheticMultibinding.bindingType()) {
+                    case PROVISION:
+                      return provisionBindingFactory.syntheticMapOfValuesBinding(key);
+
+                    case PRODUCTION:
+                      return productionBindingFactory.syntheticMapOfValuesOrProducedBinding(key);
+
+                    default:
+                      throw new VerifyException(syntheticMultibinding.toString());
+                  }
+                });
+      }
+
+      /**
+       * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
+       * multibinding contributions whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
+       * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
+       */
+      private ImmutableSet<ContributionBinding> multibindingContributionsForValueMap(Key key) {
+        return keyFactory
+            .implicitFrameworkMapKeys(key)
+            .stream()
+            .flatMap(mapKey -> getExplicitMultibindings(mapKey).stream())
+            .collect(toImmutableSet());
+      }
+
+      /**
+       * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
+       * multibinding declarations whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
+       * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
+       */
+      private ImmutableSet<MultibindingDeclaration> multibindingDeclarationsForValueMap(Key key) {
+        return keyFactory
+            .implicitFrameworkMapKeys(key)
+            .stream()
+            .flatMap(mapKey -> getMultibindingDeclarations(mapKey).stream())
+            .collect(toImmutableSet());
+      }
+
+      /**
        * Returns a synthetic binding that depends on individual multibinding contributions.
        *
        * <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
@@ -1109,9 +1165,7 @@
                   .stream()
                   .map(ContributionBinding::bindingKind)
                   .anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)
-              && keysMatchingRequest(resolvedBindings.key())
-                  .stream()
-                  .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
+              && !getLocalExplicitMultibindings(resolvedBindings.key()).isEmpty();
         }
 
         /**
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
index 32143d0..1bbcbdc 100644
--- a/java/dagger/internal/codegen/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -30,6 +30,7 @@
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentAnnotation;
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MAP;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_KINDS;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
 import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByAnnotationType;
@@ -993,7 +994,8 @@
             .stream()
             .map(ContributionBinding::bindingKind)
             // TODO(dpb): Kill with fire.
-            .anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)) {
+            .anyMatch(
+                kind -> SYNTHETIC_MULTIBOUND_KINDS.contains(kind) || SYNTHETIC_MAP.equals(kind))) {
           reportMultipleContributionTypes();
           return;
         }
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index 6706757..8891e67 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -73,6 +73,12 @@
    */
   enum Kind {
     /**
+     * The synthetic binding for {@code Map<K, V>} that depends on either
+     * {@code Map<K, Provider<V>>} or {@code Map<K, Producer<V>>}.
+     */
+    SYNTHETIC_MAP,
+
+    /**
      * A synthetic binding for a multibound set that depends on the individual multibinding
      * {@link Provides @Provides} or {@link Produces @Produces} methods.
      */
diff --git a/java/dagger/internal/codegen/DependencyRequest.java b/java/dagger/internal/codegen/DependencyRequest.java
index 5a4b4b6..bbd899a 100644
--- a/java/dagger/internal/codegen/DependencyRequest.java
+++ b/java/dagger/internal/codegen/DependencyRequest.java
@@ -344,26 +344,43 @@
      * Creates a synthetic dependency request for one individual {@code multibindingContribution}.
      */
     private DependencyRequest forMultibindingContribution(
-        ContributionBinding multibindingContribution, Kind requestKind) {
+        ContributionBinding multibindingContribution) {
       checkArgument(
           multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
           "multibindingContribution's key must have a multibinding contribution identifier: %s",
           multibindingContribution);
       return DependencyRequest.builder()
-          .kind(requestKind)
+          .kind(multibindingContributionRequestKind(multibindingContribution))
           .key(multibindingContribution.key())
           .build();
     }
 
+    private Kind multibindingContributionRequestKind(ContributionBinding multibindingContribution) {
+      switch (multibindingContribution.contributionType()) {
+        case MAP:
+          return multibindingContribution.bindingType().equals(BindingType.PRODUCTION)
+              ? Kind.PRODUCER
+              : Kind.PROVIDER;
+        case SET:
+        case SET_VALUES:
+          return Kind.INSTANCE;
+        case UNIQUE:
+          throw new IllegalArgumentException(
+              "multibindingContribution must be a multibinding: " + multibindingContribution);
+        default:
+          throw new AssertionError(multibindingContribution.toString());
+      }
+    }
+
     /**
      * Creates synthetic dependency requests for each individual multibinding contribution in {@code
      * multibindingContributions}.
      */
     ImmutableSet<DependencyRequest> forMultibindingContributions(
-        Iterable<ContributionBinding> multibindingContributions, Kind requestKind) {
+        Iterable<ContributionBinding> multibindingContributions) {
       ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
       for (ContributionBinding multibindingContribution : multibindingContributions) {
-        requests.add(forMultibindingContribution(multibindingContribution, requestKind));
+        requests.add(forMultibindingContribution(multibindingContribution));
       }
       return requests.build();
     }
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
index 0b22230..a8b5b16 100644
--- a/java/dagger/internal/codegen/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
@@ -29,6 +29,7 @@
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
 import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
 import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeValue;
+import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
@@ -47,7 +48,6 @@
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
@@ -328,6 +328,14 @@
               makeParametersCodeBlock(arguments));
         }
 
+      case SYNTHETIC_MAP:
+        FrameworkDependency frameworkDependency = getOnlyElement(binding.frameworkDependencies());
+        return CodeBlock.of(
+            "$T.create($L)",
+            mapFactoryClassName(binding),
+            componentBindingExpressions.getDependencyExpression(
+                frameworkDependency, componentName));
+
       case SYNTHETIC_MULTIBOUND_SET:
         return factoryForSetMultibindingInitialization(binding);
 
@@ -402,27 +410,16 @@
 
     ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
     MapType mapType = MapType.from(binding.key().type());
-    CodeBlock.Builder builderCall = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
+    CodeBlock.Builder builderCall =
+        CodeBlock.builder().add("$T.", frameworkMapFactoryClassName(binding.bindingType()));
     boolean useRawTypes = useRawType();
     if (!useRawTypes) {
-      // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
-      // mapType.unwrappedValueType() method that doesn't require a framework type
-      TypeMirror valueType = mapType.valueType();
-      for (Class<?> frameworkClass :
-          ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
-        if (mapType.valuesAreTypeOf(frameworkClass)) {
-          valueType = mapType.unwrappedValueType(frameworkClass);
-          break;
-        }
-      }
-      builderCall.add("<$T, $T>", mapType.keyType(), valueType);
+      builderCall.add(
+          "<$T, $T>",
+          mapType.keyType(),
+          mapType.unwrappedValueType(binding.bindingType().frameworkClass()));
     }
-
-    if (binding.bindingType().equals(BindingType.PROVISION)) {
-      builderCall.add("builder($L)", frameworkDependencies.size());
-    } else {
-      builderCall.add("builder()");
-    }
+    builderCall.add("builder($L)", frameworkDependencies.size());
     codeBlocks.add(builderCall.build());
 
     for (FrameworkDependency frameworkDependency : frameworkDependencies) {
diff --git a/java/dagger/internal/codegen/Key.java b/java/dagger/internal/codegen/Key.java
index 4871e4b..3c4cecc 100644
--- a/java/dagger/internal/codegen/Key.java
+++ b/java/dagger/internal/codegen/Key.java
@@ -546,15 +546,12 @@
     }
 
     /**
-     * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
-     * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
-     * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
-     * returned.
+     * Optionally extract a {@link Key} for the underlying provision binding(s) if such a
+     * valid key can be inferred from the given key.  Specifically, if the key represents a
+     * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned.
      */
     Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
-      return firstPresent(
-          rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
-          wrapMapKey(possibleMapKey, Provider.class));
+      return wrapMapKey(possibleMapKey, Provider.class);
     }
 
     /**
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
deleted file mode 100644
index 8e5746c..0000000
--- a/java/dagger/internal/codegen/MapBindingExpression.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
-import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.MapBuilder;
-import java.util.Collections;
-import java.util.Map;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-/** A {@link BindingExpression} for multibound maps. */
-final class MapBindingExpression extends SimpleInvocationBindingExpression {
-  /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
-  private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
-
-  private final ProvisionBinding binding;
-  private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
-  private final ComponentBindingExpressions componentBindingExpressions;
-  private final Elements elements;
-
-  MapBindingExpression(
-      ProvisionBinding binding,
-      BindingGraph graph,
-      ComponentBindingExpressions componentBindingExpressions,
-      BindingExpression delegate,
-      Elements elements) {
-    super(delegate);
-    ContributionBinding.Kind bindingKind = binding.bindingKind();
-    checkArgument(bindingKind.equals(SYNTHETIC_MULTIBOUND_MAP), bindingKind);
-    this.binding = binding;
-    this.componentBindingExpressions = componentBindingExpressions;
-    this.elements = elements;
-    this.dependencies =
-        Maps.toMap(
-            binding.dependencies(),
-            dep -> graph.resolvedBindings().get(dep.bindingKey()).contributionBinding());
-  }
-
-  @Override
-  CodeBlock getInstanceDependencyExpression(
-      DependencyRequest.Kind requestKind, ClassName requestingClass) {
-    // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
-    boolean isImmutableMapAvailable = isImmutableMapAvailable();
-    // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
-    if (isImmutableMapAvailable && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS) {
-      return CodeBlock.builder()
-          .add("$T.", ImmutableMap.class)
-          .add(maybeTypeParameters(requestingClass))
-          .add(
-              "of($L)",
-              dependencies
-                  .keySet()
-                  .stream()
-                  .map(dependency -> keyAndValueExpression(dependency, requestingClass))
-                  .collect(toParametersCodeBlock()))
-          .build();
-    }
-    switch (dependencies.size()) {
-      case 0:
-        return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
-      case 1:
-        return collectionsStaticFactoryInvocation(
-            requestingClass,
-            CodeBlock.of(
-                "singletonMap($L)",
-                keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
-      default:
-        CodeBlock.Builder instantiation = CodeBlock.builder();
-        instantiation
-            .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
-            .add(maybeTypeParameters(requestingClass));
-        if (isImmutableMapAvailable) {
-          // TODO(ronshapiro): builderWithExpectedSize
-          instantiation.add("builder()");
-        } else {
-          instantiation.add("newMapBuilder($L)", dependencies.size());
-        }
-        for (DependencyRequest dependency : dependencies.keySet()) {
-          instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
-        }
-        return instantiation.add(".build()").build();
-    }
-  }
-
-  private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
-    return CodeBlock.of(
-        "$L, $L",
-        getMapKeyExpression(dependencies.get(dependency).mapKey().get()),
-        componentBindingExpressions.getDependencyExpression(dependency, requestingClass));
-  }
-
-  private CodeBlock collectionsStaticFactoryInvocation(
-      ClassName requestingClass, CodeBlock methodInvocation) {
-    return CodeBlock.builder()
-        .add("$T.", Collections.class)
-        .add(maybeTypeParameters(requestingClass))
-        .add(methodInvocation)
-        .build();
-  }
-
-  private CodeBlock maybeTypeParameters(ClassName requestingClass) {
-    TypeMirror bindingKeyType = binding.key().type();
-    MapType mapType = MapType.from(binding.key());
-    return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
-        ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
-        : CodeBlock.of("");
-  }
-
-  private boolean isImmutableMapAvailable() {
-    return elements.getTypeElement(ImmutableMap.class.getCanonicalName()) != null;
-  }
-
-  @Override
-  protected CodeBlock explicitTypeParameter(ClassName requestingClass) {
-    if (isImmutableMapAvailable()) {
-      TypeMirror keyType = binding.key().type();
-      return CodeBlock.of(
-          "<$T>",
-          isTypeAccessibleFrom(keyType, requestingClass.packageName()) ? keyType : Map.class);
-    }
-    return CodeBlock.of("");
-  }
-}
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
index fc0217a..5466292 100644
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -20,19 +20,17 @@
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.CodeBlocks.toTypeNamesCodeBlock;
 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
 import static dagger.internal.codegen.TypeNames.FACTORY;
-import static dagger.internal.codegen.TypeNames.MAP_FACTORY;
 import static dagger.internal.codegen.TypeNames.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;
 import static javax.lang.model.type.TypeKind.DECLARED;
 
-import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
@@ -90,7 +88,7 @@
             && !contributionBinding.scope().isPresent()) {
           switch (contributionBinding.bindingKind()) {
             case SYNTHETIC_MULTIBOUND_MAP:
-              return Optional.of(emptyMapFactory(contributionBinding));
+              return Optional.of(emptyFrameworkMapFactory(contributionBinding));
 
             case SYNTHETIC_MULTIBOUND_SET:
               return Optional.of(emptySetFactory(contributionBinding));
@@ -168,19 +166,20 @@
         MEMBERS_INJECTOR);
   }
 
-  /** A {@link MemberSelect} for a factory of an empty map. */
-  private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
+  /**
+   * A {@link MemberSelect} for a factory of an empty map of factory types, where a factory can be
+   * either a {@link javax.inject.Provider} or {@link dagger.producers.Producer}.
+   */
+  private static MemberSelect emptyFrameworkMapFactory(ContributionBinding contributionBinding) {
     BindingType bindingType = contributionBinding.bindingType();
-    ImmutableList<TypeMirror> typeParameters =
-        ImmutableList.copyOf(
-            MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
-    if (bindingType.equals(BindingType.PRODUCTION)) {
-      return new ParameterizedStaticMethod(
-          PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
-    } else {
-      return new ParameterizedStaticMethod(
-          MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
-    }
+    MapType mapType = MapType.from(contributionBinding.key());
+
+    return new ParameterizedStaticMethod(
+        frameworkMapFactoryClassName(bindingType),
+        ImmutableList.of(
+            mapType.keyType(), mapType.unwrappedValueType(bindingType.frameworkClass())),
+        CodeBlock.of("empty()"),
+        ClassName.get(bindingType.frameworkClass()));
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
index 91eb94d..4a71dea 100644
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.DependencyRequest.Kind.PRODUCER;
 import static dagger.internal.codegen.MapKeys.getMapKey;
 import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
 import static dagger.internal.codegen.Util.toImmutableSet;
@@ -180,6 +179,28 @@
     }
 
     /**
+     * A synthetic binding of {@code Map<K, V>} or {@code Map<K, Produced<V>>} that depends on
+     * {@code Map<K, Producer<V>>}.
+     */
+    ProductionBinding syntheticMapOfValuesOrProducedBinding(Key mapOfValuesOrProducedKey) {
+      checkNotNull(mapOfValuesOrProducedKey);
+      Optional<Key> mapOfProducersKey =
+          keyFactory.implicitMapProducerKeyFrom(mapOfValuesOrProducedKey);
+      checkArgument(
+          mapOfProducersKey.isPresent(),
+          "%s is not a key for of Map<K, V> or Map<K, Produced<V>>",
+          mapOfValuesOrProducedKey);
+      DependencyRequest requestForMapOfProducers =
+          dependencyRequestFactory.producerForImplicitMapBinding(mapOfProducersKey.get());
+      return ProductionBinding.builder()
+          .contributionType(ContributionType.UNIQUE)
+          .key(mapOfValuesOrProducedKey)
+          .explicitDependencies(requestForMapOfProducers)
+          .bindingKind(Kind.SYNTHETIC_MAP)
+          .build();
+    }
+
+    /**
      * A synthetic binding that depends explicitly on a set of individual provision or production
      * multibinding contribution methods.
      *
@@ -191,8 +212,7 @@
           .contributionType(ContributionType.UNIQUE)
           .key(key)
           .explicitDependencies(
-              dependencyRequestFactory.forMultibindingContributions(
-                  multibindingContributions, PRODUCER))
+              dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
           .bindingKind(Kind.forMultibindingKey(key))
           .build();
     }
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
index 6774255..6d40e97 100644
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -235,6 +235,21 @@
           .build();
     }
 
+    /** A synthetic binding of {@code Map<K, V>} that depends on {@code Map<K, Provider<V>>}. */
+    ProvisionBinding syntheticMapOfValuesBinding(Key mapOfValuesKey) {
+      checkNotNull(mapOfValuesKey);
+      Optional<Key> mapOfProvidersKey = keyFactory.implicitMapProviderKeyFrom(mapOfValuesKey);
+      checkArgument(mapOfProvidersKey.isPresent(), "%s is not a key for Map<K, V>", mapOfValuesKey);
+      DependencyRequest requestForMapOfProviders =
+          dependencyRequestFactory.providerForImplicitMapBinding(mapOfProvidersKey.get());
+      return ProvisionBinding.builder()
+          .contributionType(ContributionType.UNIQUE)
+          .key(mapOfValuesKey)
+          .provisionDependencies(requestForMapOfProviders)
+          .bindingKind(Kind.SYNTHETIC_MAP)
+          .build();
+    }
+
     /**
      * A synthetic binding that depends explicitly on a set of individual provision multibinding
      * contribution methods.
@@ -243,16 +258,11 @@
      */
     ProvisionBinding syntheticMultibinding(
         Key key, Iterable<ContributionBinding> multibindingContributions) {
-      DependencyRequest.Kind dependencyKind =
-          MapType.isMap(key) && MapType.from(key).valuesAreTypeOf(Provider.class)
-              ? DependencyRequest.Kind.PROVIDER
-              : DependencyRequest.Kind.INSTANCE;
       return ProvisionBinding.builder()
           .contributionType(ContributionType.UNIQUE)
           .key(key)
           .provisionDependencies(
-              dependencyRequestFactory.forMultibindingContributions(
-                  multibindingContributions, dependencyKind))
+              dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
           .bindingKind(Kind.forMultibindingKey(key))
           .build();
     }
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index 858f184..ded5f6f 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -19,10 +19,8 @@
 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
-import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_SET;
 import static dagger.internal.codegen.Optionals.optionalComparator;
 import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
@@ -53,15 +51,17 @@
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.MapFactory;
+import dagger.internal.MapProviderFactory;
 import dagger.internal.SetFactory;
 import dagger.producers.Produced;
-import dagger.producers.Producer;
+import dagger.producers.internal.MapOfProducerProducer;
+import dagger.producers.internal.MapProducer;
 import dagger.producers.internal.SetOfProducedProducer;
 import dagger.producers.internal.SetProducer;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
-import javax.inject.Provider;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
@@ -264,24 +264,41 @@
     }
   }
 
-  /** The {@link java.util.Map} factory class name appropriate for map bindings. */
+  /**
+   * The {@link java.util.Map}-of-value factory class name appropriate for map bindings.
+   *
+   * <ul>
+   * <li>{@link MapFactory} for provision bindings.
+   * <li>{@link MapProducer} for production bindings.
+   * </ul>
+   */
   static ClassName mapFactoryClassName(ContributionBinding binding) {
-    checkState(binding.bindingKind().equals(SYNTHETIC_MULTIBOUND_MAP), binding.bindingKind());
-    MapType mapType = MapType.from(binding.key());
     switch (binding.bindingType()) {
-      case PROVISION:
-        return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
       case PRODUCTION:
-        return mapType.valuesAreFrameworkType()
-            ? mapType.valuesAreTypeOf(Producer.class)
-                ? MAP_OF_PRODUCER_PRODUCER
-                : MAP_OF_PRODUCED_PRODUCER
-            : MAP_PRODUCER;
+        return MapType.from(binding.key()).valuesAreTypeOf(Produced.class)
+            ? MAP_OF_PRODUCED_PRODUCER : MAP_PRODUCER;
+
+      case PROVISION:
+        return MAP_FACTORY;
+
       default:
-        throw new IllegalArgumentException(binding.bindingType().toString());
+        throw new AssertionError(binding.toString());
     }
   }
 
+  /**
+   * The {@link java.util.Map}-of-framework factory class name appropriate for map bindings.
+   *
+   * <ul>
+   * <li>{@link MapProviderFactory} for provision bindings.
+   * <li>{@link MapOfProducerProducer} for production bindings.
+   * </ul>
+   */
+  static ClassName frameworkMapFactoryClassName(BindingType bindingType) {
+    return bindingType.equals(BindingType.PRODUCTION)
+        ? MAP_OF_PRODUCER_PRODUCER : MAP_PROVIDER_FACTORY;
+  }
+
   private static String factoryPrefix(ContributionBinding binding) {
     switch (binding.bindingKind()) {
       case INJECTION: