Merge pull request #834 from google/moe_writing_branch_from_5b65f8c4dcf95a89d3b5ace2dbee3e54fa963722

Merge internal changes
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
index fe596a5..68610a3 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/MapBinder.java
@@ -605,7 +605,7 @@
     /**
      * Binds {@code Map<K, Set<V>>} and {{@code Map<K, Set<Provider<V>>>}.
      */
-    private static final class MultimapBinder<K, V> implements Module {
+    static final class MultimapBinder<K, V> implements Module {
 
       private final Key<Map<K, Set<V>>> multimapKey;
       private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey;
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
index 3ace927..98db1e4 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Multibinder.java
@@ -35,6 +35,7 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Module;
+import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.internal.Errors;
@@ -43,12 +44,14 @@
 import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderWithDependencies;
 import com.google.inject.spi.ProviderWithExtensionVisitor;
 import com.google.inject.spi.Toolable;
 import com.google.inject.util.Types;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -76,6 +79,8 @@
  *   public SnackMachine(Set&lt;Snack&gt; snacks) { ... }
  * }</code></pre>
  *
+ * If desired, {@link Collection}{@code <Provider<Snack>>} can also be injected.
+ *
  * <p>Contributing multibindings from different modules is supported. For
  * example, it is okay for both {@code CandyModule} and {@code ChipsModule}
  * to create their own {@code Multibinder<Snack>}, and to each contribute
@@ -117,7 +122,7 @@
   public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type) {
     binder = binder.skipSources(RealMultibinder.class, Multibinder.class);
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type,
-        Key.get(Multibinder.<T>setOf(type)));
+        Key.get(Multibinder.<T>setOf(type)), Key.get(Multibinder.<T>collectionOfProvidersOf(type)));
     binder.install(result);
     return result;
   }
@@ -138,7 +143,8 @@
       Binder binder, TypeLiteral<T> type, Annotation annotation) {
     binder = binder.skipSources(RealMultibinder.class, Multibinder.class);
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type,
-        Key.get(Multibinder.<T>setOf(type), annotation));
+        Key.get(Multibinder.<T>setOf(type), annotation),
+        Key.get(Multibinder.<T>collectionOfProvidersOf(type), annotation));
     binder.install(result);
     return result;
   }
@@ -160,7 +166,8 @@
       Class<? extends Annotation> annotationType) {
     binder = binder.skipSources(RealMultibinder.class, Multibinder.class);
     RealMultibinder<T> result = new RealMultibinder<T>(binder, type,
-        Key.get(Multibinder.<T>setOf(type), annotationType));
+        Key.get(Multibinder.<T>setOf(type), annotationType),
+        Key.get(Multibinder.<T>collectionOfProvidersOf(type), annotationType));
     binder.install(result);
     return result;
   }
@@ -180,6 +187,14 @@
     return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
   }
 
+  @SuppressWarnings("unchecked")
+  static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
+      TypeLiteral<T> elementType) {
+    Type providerType = Types.providerOf(elementType.getType());
+    Type type = Types.newParameterizedType(Collection.class, providerType);
+    return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
+  }
+
   /**
    * Configures the bound set to silently discard duplicate elements. When multiple equal values are
    * bound, the one that gets included is arbitrary. When multiple modules contribute elements to
@@ -230,6 +245,7 @@
     private final TypeLiteral<T> elementType;
     private final String setName;
     private final Key<Set<T>> setKey;
+    private final Key<Collection<Provider<T>>> collectionOfProvidersKey;
     private final Key<Boolean> permitDuplicatesKey;
 
     /* the target injector's binder. non-null until initialization, null afterwards */
@@ -242,10 +258,13 @@
     /** whether duplicates are allowed. Possibly configured by a different instance */
     private boolean permitDuplicates;
 
-    private RealMultibinder(Binder binder, TypeLiteral<T> elementType, Key<Set<T>> setKey) {
+    private RealMultibinder(Binder binder, TypeLiteral<T> elementType, Key<Set<T>> setKey,
+        Key<Collection<Provider<T>>> collectionOfProvidersKey) {
       this.binder = checkNotNull(binder, "binder");
       this.elementType = checkNotNull(elementType, "elementType");
       this.setKey = checkNotNull(setKey, "setKey");
+      this.collectionOfProvidersKey =
+          checkNotNull(collectionOfProvidersKey, "collectionOfProviders");
       this.setName = RealElement.nameOf(setKey);
       this.permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
     }
@@ -254,10 +273,11 @@
       checkConfiguration(!isInitialized(), "Multibinder was already initialized");
 
       binder.bind(setKey).toProvider(this);
+      binder.bind(collectionOfProvidersKey).toProvider(
+          new RealMultibinderCollectionOfProvidersProvider());
     }
 
-    @Override
-    public Multibinder<T> permitDuplicates() {
+    @Override public Multibinder<T> permitDuplicates() {
       binder.install(new PermitDuplicatesModule(permitDuplicatesKey));
       return this;
     }
@@ -371,7 +391,8 @@
         Binding<?> binding = (Binding<?>) element;
         return keyMatches(binding.getKey())
             || binding.getKey().equals(permitDuplicatesKey)
-            || binding.getKey().equals(setKey);
+            || binding.getKey().equals(setKey)
+            || binding.getKey().equals(collectionOfProvidersKey);
       } else {
         return false;
       }
@@ -397,6 +418,46 @@
     @Override public String toString() {
       return (setName.isEmpty() ? "" : setName + " ") + "Multibinder<" + elementType + ">";
     }
+
+    final class RealMultibinderCollectionOfProvidersProvider
+        implements ProviderWithDependencies<Collection<Provider<T>>> {
+      @Override public Collection<Provider<T>> get() {
+        checkConfiguration(isInitialized(), "Multibinder is not initialized");
+
+        ImmutableList.Builder<Provider<T>> resultBuilder = new ImmutableList.Builder<Provider<T>>();
+        for (Binding<T> binding : bindings) {
+          resultBuilder.add(binding.getProvider());
+        }
+        return resultBuilder.build();
+      }
+
+      @Override public Set<Dependency<?>> getDependencies() {
+        if (!isInitialized()) {
+          return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
+        }
+        ImmutableSet.Builder<Dependency<?>> setBuilder = ImmutableSet.builder();
+        for (Dependency<?> dependency : dependencies) {
+          Key key = dependency.getKey();
+          setBuilder.add(
+              Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
+        }
+        return setBuilder.build();
+      }
+
+      Key getCollectionKey() {
+        return RealMultibinder.this.collectionOfProvidersKey;
+      }
+
+      @Override public boolean equals(Object o) {
+        return o instanceof Multibinder.RealMultibinder.RealMultibinderCollectionOfProvidersProvider
+            && ((Multibinder.RealMultibinder.RealMultibinderCollectionOfProvidersProvider) o)
+                .getCollectionKey().equals(getCollectionKey());
+      }
+
+      @Override public int hashCode() {
+        return getCollectionKey().hashCode();
+      }
+    }
   }
 
   /**
@@ -411,8 +472,7 @@
       this.key = key;
     }
 
-    @Override
-    protected void configure() {
+    @Override protected void configure() {
       bind(key).toInstance(true);
     }
 
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
index e4144db..91cf076 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
@@ -58,6 +58,7 @@
 import com.google.inject.spi.LinkedKeyBinding;
 import com.google.inject.util.Modules;
 import com.google.inject.util.Providers;
+import com.google.inject.util.Types;
 
 import junit.framework.TestCase;
 
@@ -72,6 +73,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.reflect.Method;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -90,10 +92,14 @@
       new TypeLiteral<Map<String, String>>() {};
   final TypeLiteral<Set<String>> setOfString = new TypeLiteral<Set<String>>() {};
   final TypeLiteral<Set<Integer>> setOfInteger = new TypeLiteral<Set<Integer>>() {};
+  final TypeLiteral<Collection<Provider<Integer>>> collectionOfProvidersOfIntegers =
+      new TypeLiteral<Collection<Provider<Integer>>>() {};
   final TypeLiteral<String> stringType = TypeLiteral.get(String.class);
   final TypeLiteral<Integer> intType = TypeLiteral.get(Integer.class);
   final TypeLiteral<List<String>> listOfStrings = new TypeLiteral<List<String>>() {};
   final TypeLiteral<Set<List<String>>> setOfListOfStrings = new TypeLiteral<Set<List<String>>>() {};
+  final TypeLiteral<Collection<Provider<String>>> collectionOfProvidersOfStrings =
+      new TypeLiteral<Collection<Provider<String>>>() {};
 
   public void testMultibinderAggregatesMultipleModules() {
     Module abc = new AbstractModule() {
@@ -114,12 +120,14 @@
 
     Injector injector = Guice.createInjector(abc, de);
     Key<Set<String>> setKey = Key.get(setOfString);
+    Key<Collection<Provider<String>>> collectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings);
     Set<String> abcde = injector.getInstance(setKey);
     Set<String> results = setOf("A", "B", "C", "D", "E");
 
     assertEquals(results, abcde);
-    assertSetVisitor(setKey, stringType, setOf(abc, de), BOTH, false, 0, instance("A"),
-        instance("B"), instance("C"), instance("D"), instance("E"));
+    assertSetVisitor(setKey, collectionOfProvidersKey, stringType, setOf(abc, de), BOTH, false, 0,
+        instance("A"), instance("B"), instance("C"), instance("D"), instance("E"));
   }
 
   public void testMultibinderAggregationForAnnotationInstance() {
@@ -137,11 +145,13 @@
     Injector injector = Guice.createInjector(module);
 
     Key<Set<String>> setKey = Key.get(setOfString, Names.named("abc"));
+    Key<Collection<Provider<String>>> collectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, Names.named("abc"));
     Set<String> abc = injector.getInstance(setKey);
     Set<String> results = setOf("A", "B", "C");
     assertEquals(results, abc);
-    assertSetVisitor(setKey, stringType, setOf(module), BOTH, false, 0, instance("A"),
-        instance("B"), instance("C"));
+    assertSetVisitor(setKey, collectionOfProvidersKey, stringType, setOf(module), BOTH, false, 0,
+        instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderAggregationForAnnotationType() {
@@ -159,11 +169,13 @@
     Injector injector = Guice.createInjector(module);
 
     Key<Set<String>> setKey = Key.get(setOfString, Abc.class);
+    Key<Collection<Provider<String>>> collectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, Abc.class);
     Set<String> abcde = injector.getInstance(setKey);
     Set<String> results = setOf("A", "B", "C");
     assertEquals(results, abcde);
-    assertSetVisitor(setKey, stringType, setOf(module), BOTH, false, 0, instance("A"),
-        instance("B"), instance("C"));
+    assertSetVisitor(setKey, collectionOfProvidersKey, stringType, setOf(module), BOTH, false, 0,
+        instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderWithMultipleAnnotationValueSets() {
@@ -184,16 +196,21 @@
     Injector injector = Guice.createInjector(module);
 
     Key<Set<String>> abcSetKey = Key.get(setOfString, named("abc"));
+    Key<Collection<Provider<String>>> abcCollectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, named("abc"));
     Set<String> abc = injector.getInstance(abcSetKey);
     Key<Set<String>> deSetKey = Key.get(setOfString, named("de"));
+    Key<Collection<Provider<String>>> deCollectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, named("de"));
     Set<String> de = injector.getInstance(deSetKey);
     Set<String> abcResults = setOf("A", "B", "C");
     assertEquals(abcResults, abc);
     Set<String> deResults = setOf("D", "E");
     assertEquals(deResults, de);
-    assertSetVisitor(abcSetKey, stringType, setOf(module), BOTH, false, 1, instance("A"),
-        instance("B"), instance("C"));
-    assertSetVisitor(deSetKey, stringType, setOf(module), BOTH, false, 1, instance("D"), instance("E"));
+    assertSetVisitor(abcSetKey, abcCollectionOfProvidersKey, stringType, setOf(module), BOTH, false,
+        1, instance("A"), instance("B"), instance("C"));
+    assertSetVisitor(deSetKey, deCollectionOfProvidersKey, stringType, setOf(module), BOTH, false,
+        1, instance("D"), instance("E"));
   }
 
   public void testMultibinderWithMultipleAnnotationTypeSets() {
@@ -214,16 +231,21 @@
     Injector injector = Guice.createInjector(module);
 
     Key<Set<String>> abcSetKey = Key.get(setOfString, Abc.class);
+    Key<Collection<Provider<String>>> abcCollectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, Abc.class);
     Set<String> abc = injector.getInstance(abcSetKey);
     Key<Set<String>> deSetKey = Key.get(setOfString, De.class);
+    Key<Collection<Provider<String>>> deCollectionOfProvidersKey =
+        Key.get(collectionOfProvidersOfStrings, De.class);
     Set<String> de = injector.getInstance(deSetKey);
     Set<String> abcResults = setOf("A", "B", "C");
     assertEquals(abcResults, abc);
     Set<String> deResults = setOf("D", "E");
     assertEquals(deResults, de);
-    assertSetVisitor(abcSetKey, stringType, setOf(module), BOTH, false, 1, instance("A"),
-        instance("B"), instance("C"));
-    assertSetVisitor(deSetKey, stringType, setOf(module), BOTH, false, 1, instance("D"), instance("E"));
+    assertSetVisitor(abcSetKey, abcCollectionOfProvidersKey, stringType, setOf(module), BOTH, false,
+        1, instance("A"), instance("B"), instance("C"));
+    assertSetVisitor(deSetKey, deCollectionOfProvidersKey, stringType, setOf(module), BOTH, false,
+        1, instance("D"), instance("E"));
   }
 
   public void testMultibinderWithMultipleSetTypes() {
@@ -239,8 +261,10 @@
 
     assertEquals(setOf("A"), injector.getInstance(Key.get(setOfString)));
     assertEquals(setOf(1), injector.getInstance(Key.get(setOfInteger)));
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(module), BOTH, false, 1, instance("A"));
-    assertSetVisitor(Key.get(setOfInteger), intType, setOf(module), BOTH, false, 1, instance(1));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(module), BOTH, false, 1, instance("A"));
+    assertSetVisitor(Key.get(setOfInteger), Key.get(collectionOfProvidersOfIntegers), intType,
+        setOf(module), BOTH, false, 1, instance(1));
   }
 
   public void testMultibinderWithEmptySet() {
@@ -253,7 +277,8 @@
 
     Set<String> set = injector.getInstance(Key.get(setOfString));
     assertEquals(Collections.emptySet(), set);
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(module), BOTH, false, 0);
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(module), BOTH, false, 0);
   }
 
   public void testMultibinderSetIsUnmodifiable() {
@@ -315,7 +340,8 @@
     assertEquals(setOf(1), injector.getInstance(Key.get(setOfInteger)));
     assertEquals(setOf(2), injector.getInstance(Key.get(setOfInteger)));
     assertEquals(setOf(3), injector.getInstance(Key.get(setOfInteger)));
-    assertSetVisitor(Key.get(setOfInteger), intType, setOf(module), BOTH, false, 0, providerInstance(1));
+    assertSetVisitor(Key.get(setOfInteger), Key.get(collectionOfProvidersOfIntegers), intType,
+        setOf(module), BOTH, false, 0, providerInstance(1));
   }
 
   public void testMultibinderSetForbidsDuplicateElements() {
@@ -344,8 +370,8 @@
     }
 
     // But we can still visit the module!
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(module1, module2), MODULE, false, 0,
-        instance("A"), instance("A"));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(module1, module2), MODULE, false, 0, instance("A"), instance("A"));
   }
 
   public void testMultibinderSetShowsBothElementsIfToStringDifferent() {
@@ -390,6 +416,8 @@
 
     TypeLiteral<ValueType> valueType = TypeLiteral.get(ValueType.class);
     TypeLiteral<Set<ValueType>> setOfValueType = new TypeLiteral<Set<ValueType>>() {};
+    TypeLiteral<Collection<Provider<ValueType>>> collectionOfProvidersOfValueType =
+        new TypeLiteral<Collection<Provider<ValueType>>>() {};
     try {
       injector.getInstance(Key.get(setOfValueType));
       fail();
@@ -403,8 +431,9 @@
     }
 
     // But we can still visit the module!
-    assertSetVisitor(Key.get(setOfValueType), valueType, setOf(module1, module2), MODULE, false, 0,
-        instance(new ValueType(1, 2)), instance(new ValueType(1, 3)));
+    assertSetVisitor(Key.get(setOfValueType), Key.get(collectionOfProvidersOfValueType), valueType,
+        setOf(module1, module2), MODULE, false, 0, instance(new ValueType(1, 2)),
+        instance(new ValueType(1, 3)));
   }
 
   public void testMultibinderSetPermitDuplicateElements() {
@@ -426,8 +455,8 @@
     Injector injector = Guice.createInjector(ab, bc);
 
     assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString)));
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0,
-        instance("A"), instance("B"), instance("C"));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(ab, bc), BOTH, true, 0, instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderSetPermitDuplicateCallsToPermitDuplicates() {
@@ -450,8 +479,8 @@
     Injector injector = Guice.createInjector(ab, bc);
 
     assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString)));
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0,
-        instance("A"), instance("B"), instance("C"));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(ab, bc), BOTH, true, 0, instance("A"), instance("B"), instance("C"));
   }
 
   public void testMultibinderSetForbidsNullElements() {
@@ -632,8 +661,9 @@
     assertEquals(ImmutableSet.of("A", "B", "C", "D", "E", "F"),
         injector.getInstance(Key.get(setOfString)));
 
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(abcd, ef), BOTH, false, 0,
-        instance("A"), instance("B"), instance("C"), instance("D"), instance("E"), instance("F"));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(abcd, ef), BOTH, false, 0, instance("A"), instance("B"), instance("C"), instance("D"),
+        instance("E"), instance("F"));
   }
 
   /**
@@ -671,8 +701,9 @@
     assertEquals(ImmutableSet.of("A", "B", "C", "D", "E", "F"),
         injector.getInstance(Key.get(setOfString)));
 
-    assertSetVisitor(Key.get(setOfString), stringType, setOf(abcd, ef), BOTH, true, 0,
-        instance("A"), instance("B"), instance("C"), instance("D"), instance("E"), instance("F"));
+    assertSetVisitor(Key.get(setOfString), Key.get(collectionOfProvidersOfStrings), stringType,
+        setOf(abcd, ef), BOTH, true, 0, instance("A"), instance("B"), instance("C"), instance("D"),
+        instance("E"), instance("F"));
   }
 
   /**
@@ -1117,9 +1148,79 @@
     assertTrue(collector.setbinding.containsElement(a));
     assertFalse(collector.setbinding.containsElement(b));
     assertFalse(collector.setbinding.containsElement(c));
-    
+
     assertFalse(collector.optionalbinding.containsElement(a));
     assertFalse(collector.optionalbinding.containsElement(b));
     assertTrue(collector.optionalbinding.containsElement(c));
   }
+
+  public void testMultibinderCanInjectCollectionOfProviders() {
+    Module module = new AbstractModule() {
+      protected void configure() {
+        final Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class);
+        multibinder.addBinding().toProvider(Providers.of("A"));
+        multibinder.addBinding().toProvider(Providers.of("B"));
+        multibinder.addBinding().toInstance("C");
+      }
+    };
+
+    Injector injector = Guice.createInjector(module);
+    injector.getInstance(Key.get(collectionOfProvidersOfStrings));
+    Key<Collection<Provider<String>>> collectionKey = Key.get(collectionOfProvidersOfStrings);
+    Collection<Provider<String>> providers = injector.getInstance(collectionKey);
+    Collection<String> values = Lists.newArrayList();
+    for (Provider<String> provider : providers) {
+      values.add(provider.get());
+    }
+    Collection<String> expectedValues = ImmutableList.of("A", "B", "C");
+    assertEquals(expectedValues, values);
+  }
+
+  public void testMultibinderCanInjectCollectionOfProvidersWithAnnotation() {
+    final Annotation ann = Names.named("foo");
+    Module module = new AbstractModule() {
+      protected void configure() {
+        final Multibinder<String> multibinder =
+            Multibinder.newSetBinder(binder(), String.class, ann);
+        multibinder.addBinding().toProvider(Providers.of("A"));
+        multibinder.addBinding().toProvider(Providers.of("B"));
+        multibinder.addBinding().toInstance("C");
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    Key<Collection<Provider<String>>> collectionKey = Key.get(collectionOfProvidersOfStrings, ann);
+    Collection<Provider<String>> providers = injector.getInstance(collectionKey);
+    Collection<String> values = Lists.newArrayList();
+    for (Provider<String> provider : providers) {
+      values.add(provider.get());
+    }
+    Collection<String> expectedValues = ImmutableList.of("A", "B", "C");
+    assertEquals(expectedValues, values);
+  }
+
+  public void testMultibindingProviderDependencies() {
+    final Annotation setAnn = Names.named("foo");
+    Injector injector = Guice.createInjector(new AbstractModule() {
+        @Override protected void configure() {
+          Multibinder<String> multibinder =
+              Multibinder.newSetBinder(binder(), String.class, setAnn);
+          multibinder.addBinding().toInstance("a");
+          multibinder.addBinding().toInstance("b");
+        }
+      });
+    HasDependencies providerBinding =
+      (HasDependencies) injector.getBinding(new Key<Collection<Provider<String>>>(setAnn) {});
+    HasDependencies setBinding =
+      (HasDependencies) injector.getBinding(new Key<Set<String>>(setAnn) {});
+    // sanity check the size
+    assertEquals(setBinding.getDependencies().toString(), 2, setBinding.getDependencies().size());
+    Set<Dependency<?>> expected = Sets.newHashSet();
+    for (Dependency<?> dep : setBinding.getDependencies()) {
+      Key key = dep.getKey();
+      Dependency<?> providerDependency =
+          Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType())));
+      expected.add(providerDependency);
+    }
+    assertEquals(expected, providerBinding.getDependencies());
+  }
 }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
index 8849690..969d26e 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
@@ -21,6 +21,7 @@
 import static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfSetOfProviderOf;
+import static com.google.inject.multibindings.Multibinder.collectionOfProvidersOf;
 import static com.google.inject.multibindings.Multibinder.setOf;
 import static com.google.inject.multibindings.OptionalBinder.optionalOfJavaxProvider;
 import static com.google.inject.multibindings.OptionalBinder.optionalOfProvider;
@@ -32,6 +33,7 @@
 import static com.google.inject.multibindings.SpiUtils.VisitType.INJECTOR;
 import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -65,6 +67,7 @@
 import com.google.inject.spi.ProviderKeyBinding;
 import com.google.inject.spi.ProviderLookup;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -254,7 +257,10 @@
     Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
     Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
     Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));    
+    Key<?> collectionOfProvidersOfEntry =
+        mapKey.ofType(collectionOfProvidersOf(entryOfProviderOf(keyType, valueType)));
     boolean entrySetMatch = false;
+    boolean entryProviderCollectionMatch = false;
     boolean mapProviderMatch = false;
     boolean mapSetMatch = false; 
     boolean mapSetProviderMatch = false;
@@ -324,16 +330,20 @@
           if(b != null) {
             assertTrue(b.acceptTargetVisitor(visitor) instanceof MultibinderBinding);
           }
+        } else if(key.equals(collectionOfProvidersOfEntry)) {
+          matched = true;
+          assertTrue(contains);
+          entryProviderCollectionMatch = true;
         }
       }
       
-      if(!matched && contains) {
+      if (!matched && contains) {
         otherMatches.add(element);
       }
     }
     
     int otherMatchesSize = otherMatches.size();
-    if(allowDuplicates) {
+    if (allowDuplicates) {
       otherMatchesSize--; // allow for 1 duplicate binding
     }
     otherMatchesSize = otherMatchesSize / 3; // value, ProviderLookup per value, Map.Entry per value
@@ -341,6 +351,7 @@
         .size() + duplicates, otherMatchesSize);
 
     assertTrue(entrySetMatch);
+    assertTrue(entryProviderCollectionMatch);
     assertTrue(mapProviderMatch);
     assertEquals(allowDuplicates, mapSetMatch);
     assertEquals(allowDuplicates, mapSetProviderMatch);
@@ -356,6 +367,7 @@
    * 
    * @param <T> The type of the binding
    * @param setKey The key the set belongs to.
+   * @param collectionOfProvidersKey The key to use for Collections of Providers of Elements.
    * @param elementType the TypeLiteral of the element
    * @param modules The modules that define the multibindings
    * @param visitType The kind of test we should perform.  A live Injector, a raw Elements (Module) test, or both.
@@ -363,7 +375,8 @@
    * @param expectedMultibindings The number of other multibinders we expect to see.
    * @param results The kind of bindings contained in the multibinder.
    */
-  static <T> void assertSetVisitor(Key<T> setKey, TypeLiteral<?> elementType,
+  static <T> void assertSetVisitor(Key<Set<T>> setKey,
+      Key<Collection<Provider<T>>> collectionOfProvidersKey, TypeLiteral<?> elementType,
       Iterable<? extends Module> modules, VisitType visitType, boolean allowDuplicates,
       int expectedMultibindings, BindResult... results) {
     if(visitType == null) {
@@ -371,22 +384,26 @@
     }
     
     if(visitType == BOTH || visitType == INJECTOR) {
-      setInjectorTest(setKey, elementType, modules, allowDuplicates, expectedMultibindings, results);
+      setInjectorTest(setKey, collectionOfProvidersKey, elementType, modules, allowDuplicates,
+          expectedMultibindings, results);
     }
     
     if(visitType == BOTH || visitType == MODULE) {
-      setModuleTest(setKey, elementType, modules, allowDuplicates, expectedMultibindings, results);
+      setModuleTest(setKey, collectionOfProvidersKey, elementType, modules, allowDuplicates,
+          expectedMultibindings, results);
     }
   }
   
   @SuppressWarnings("unchecked")
-  private static <T> void setInjectorTest(Key<T> setKey, TypeLiteral<?> elementType,
+  private static <T> void setInjectorTest(Key<Set<T>> setKey,
+      Key<Collection<Provider<T>>> collectionOfProvidersKey, TypeLiteral<?> elementType,
       Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
       BindResult... results) {
     Injector injector = Guice.createInjector(modules);
-    Visitor<T> visitor = new Visitor<T>();
-    Binding<T> binding = injector.getBinding(setKey);
-    MultibinderBinding<T> multibinder = (MultibinderBinding<T>)binding.acceptTargetVisitor(visitor);
+    Visitor<Set<T>> visitor = new Visitor<Set<T>>();
+    Binding<Set<T>> binding = injector.getBinding(setKey);
+    MultibinderBinding<Set<T>> multibinder =
+        (MultibinderBinding<Set<T>>)binding.acceptTargetVisitor(visitor);
     assertNotNull(multibinder);
     assertEquals(elementType, multibinder.getElementTypeLiteral());
     assertEquals(allowDuplicates, multibinder.permitsDuplicates());
@@ -395,7 +412,7 @@
     assertEquals("wrong bind elements, expected: " + bindResults
         + ", but was: " + multibinder.getElements(),
         bindResults.size(), elements.size());
-    
+
     for(BindResult result : bindResults) {
       Binding found = null;
       for(Binding item : elements) {
@@ -424,8 +441,10 @@
 
     List<Object> otherMultibinders = Lists.newArrayList();
     List<Binding> otherContains = Lists.newArrayList();
+    boolean collectionOfProvidersMatch = false;
     for(Binding b : injector.getAllBindings().values()) {
       boolean contains = multibinder.containsElement(b);
+      Key key = b.getKey();
       Object visited = b.acceptTargetVisitor(visitor);
       if(visited != null) {
         if(visited.equals(multibinder)) {
@@ -435,13 +454,18 @@
         }
       } else if(setOfElements.contains(b)) {
         assertTrue(contains);
-      } else if(contains) {
+      } else if (key.equals(collectionOfProvidersKey)) {
+        assertTrue(contains);
+        collectionOfProvidersMatch = true;
+      } else if (contains) {
         if (!indexer.isIndexable(b) || !setOfIndexed.contains(b.acceptTargetVisitor(indexer))) {
           otherContains.add(b);
         }
       }
     }
-    
+
+    assertTrue(collectionOfProvidersMatch);
+
     if(allowDuplicates) {
       assertEquals("contained more than it should: " + otherContains, 1, otherContains.size());
     } else {
@@ -453,16 +477,17 @@
   }
   
   @SuppressWarnings("unchecked")
-  private static <T> void setModuleTest(Key<T> setKey, TypeLiteral<?> elementType,
+  private static <T> void setModuleTest(Key<Set<T>> setKey,
+      Key<Collection<Provider<T>>> collectionOfProvidersKey, TypeLiteral<?> elementType,
       Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
       BindResult... results) {
     List<BindResult> bindResults = Lists.newArrayList(results);
     List<Element> elements = Elements.getElements(modules);
     Visitor<T> visitor = new Visitor<T>();
-    MultibinderBinding<T> multibinder = null;
+    MultibinderBinding<Set<T>> multibinder = null;
     for(Element element : elements) {
       if(element instanceof Binding && ((Binding)element).getKey().equals(setKey)) {
-        multibinder = (MultibinderBinding<T>)((Binding)element).acceptTargetVisitor(visitor);
+        multibinder = (MultibinderBinding<Set<T>>)((Binding)element).acceptTargetVisitor(visitor);
         break;
       }
     }
@@ -475,19 +500,21 @@
     int duplicates = 0;
     Set<IndexedBinding> setOfIndexed = Sets.newHashSet();
     Indexer indexer = new Indexer(null);
+    boolean collectionOfProvidersMatch = false;
     for(Element element : elements) {
       boolean contains = multibinder.containsElement(element);
       if(!contains) {
         otherElements.add(element);
       }
       boolean matched = false;
+      Key key = null;
       if(element instanceof Binding) {
         Binding binding = (Binding)element;
         if (indexer.isIndexable(binding)
             && !setOfIndexed.add((IndexedBinding) binding.acceptTargetVisitor(indexer))) {
           duplicates++;
         }
-        
+        key = binding.getKey();
         Object visited = binding.acceptTargetVisitor(visitor);
         if(visited != null) {
           matched = true;
@@ -498,12 +525,16 @@
           }
         }
       }
-      
-      if(!matched && contains) {
+
+      if (collectionOfProvidersKey.equals(key)) {
+        assertTrue(contains);
+        assertFalse(matched);
+        collectionOfProvidersMatch = true;
+      } else if (!matched && contains) {
         otherContains.add(element);
       }
     }
-    
+
     if(allowDuplicates) {
       assertEquals("wrong contained elements: " + otherContains,
           bindResults.size() + 1 + duplicates, otherContains.size());
@@ -511,10 +542,11 @@
       assertEquals("wrong contained elements: " + otherContains,
           bindResults.size() + duplicates, otherContains.size());
     }
-     
+
     assertEquals("other multibindings found: " + otherMultibinders, otherMultibindings,
         otherMultibinders.size());
-    
+    assertTrue(collectionOfProvidersMatch);
+
     // Validate that we can construct an injector out of the remaining bindings.
     Guice.createInjector(Elements.getModule(otherElements));
   }