Implement hashCode and equals in FactoryProvider/FactoryProvider2, with the aim of allowing Guice to dedupe bindings. Reroll of earlier change with a bug fixed.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=65611245
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
index 2d7084d..7ea0498 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
@@ -42,9 +42,13 @@
   private final Constructor<T> constructor;
   private final ParameterListKey assistedParameters;
   private final List<Parameter> allParameters;
+  
+  public static <T> AssistedConstructor<T> create(
+      Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
+    return new AssistedConstructor<T>(constructor, parameterTypes);
+  }
 
-  @SuppressWarnings("unchecked")
-  public AssistedConstructor(Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
+  private AssistedConstructor(Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
     this.constructor = constructor;
 
     Annotation[][] annotations = constructor.getParameterAnnotations();
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/BindingCollector.java b/extensions/assistedinject/src/com/google/inject/assistedinject/BindingCollector.java
index 837c5c1..a90494b 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/BindingCollector.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/BindingCollector.java
@@ -48,4 +48,14 @@
   public Map<Key<?>, TypeLiteral<?>> getBindings() {
     return Collections.unmodifiableMap(bindings);
   }
+
+  @Override
+  public int hashCode() {
+    return bindings.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return (obj instanceof BindingCollector) && bindings.equals(((BindingCollector) obj).bindings);
+  }
 }
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModuleBuilder.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModuleBuilder.java
index 884aeb1..f6860bc 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModuleBuilder.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryModuleBuilder.java
@@ -331,7 +331,7 @@
   public <F> Module build(final Key<F> factoryInterface) {
     return new AbstractModule() {
       @Override protected void configure() {
-        Provider<F> provider = new FactoryProvider2<F>(factoryInterface, bindings);  
+        Provider<F> provider = new FactoryProvider2<F>(factoryInterface, bindings);
         bind(factoryInterface).toProvider(provider);
       }
     };
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
index 677252d..39e167c 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
@@ -18,6 +18,7 @@
 
 import static com.google.inject.internal.Annotations.getKey;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -159,6 +160,7 @@
   private Injector injector;
 
   private final TypeLiteral<F> factoryType;
+  private final TypeLiteral<?> implementationType;
   private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
 
   public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType){
@@ -171,7 +173,7 @@
         = createMethodMapping(factoryType, implementationType);
 
     if (!factoryMethodToConstructor.isEmpty()) {
-      return new FactoryProvider<F>(factoryType, factoryMethodToConstructor);
+      return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
     } else {
       BindingCollector collector = new BindingCollector();
 
@@ -180,18 +182,16 @@
       Errors errors = new Errors();
       Key<?> implementationKey = Key.get(implementationType);
 
-      if (implementationType != null) {
-        try {
-          for (Method method : factoryType.getRawType().getMethods()) {
-            Key<?> returnType = getKey(factoryType.getReturnType(method), method,
-                method.getAnnotations(), errors);
-            if (!implementationKey.equals(returnType)) {
-              collector.addBinding(returnType, implementationType);
-            }
+      try {
+        for (Method method : factoryType.getRawType().getMethods()) {
+          Key<?> returnType = getKey(factoryType.getReturnType(method), method,
+              method.getAnnotations(), errors);
+          if (!implementationKey.equals(returnType)) {
+            collector.addBinding(returnType, implementationType);
           }
-        } catch (ErrorsException e) {
-          throw new ConfigurationException(e.getErrors().getMessages());
-        }
+      }
+      } catch (ErrorsException e) {
+        throw new ConfigurationException(e.getErrors().getMessages());
       }
 
       return new FactoryProvider2<F>(Key.get(factoryType), collector);
@@ -199,8 +199,10 @@
   }
 
   private FactoryProvider(TypeLiteral<F> factoryType,
+      TypeLiteral<?> implementationType,
       Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
     this.factoryType = factoryType;
+    this.implementationType = implementationType;
     this.factoryMethodToConstructor = factoryMethodToConstructor;
     checkDeclaredExceptionsMatch();
   }
@@ -253,8 +255,7 @@
 
     for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
       if (constructor.getAnnotation(AssistedInject.class) != null) {
-        @SuppressWarnings("unchecked") // the constructor type and implementation type agree
-        AssistedConstructor assistedConstructor = new AssistedConstructor(
+        AssistedConstructor<?> assistedConstructor = AssistedConstructor.create(
             constructor, implementationType.getParameterTypes(constructor));
         constructors.add(assistedConstructor);
       }
@@ -272,9 +273,9 @@
           constructors.size(), factoryType, factoryMethods.length);
     }
 
-    Map<ParameterListKey, AssistedConstructor> paramsToConstructor = Maps.newHashMap();
+    Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
 
-    for (AssistedConstructor c : constructors) {
+    for (AssistedConstructor<?> c : constructors) {
       if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
         throw new RuntimeException("Duplicate constructor, " + c);
       }
@@ -312,7 +313,7 @@
         }
       }
 
-      AssistedConstructor matchingConstructor = paramsToConstructor.remove(methodParams);
+      AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
 
       result.put(method, matchingConstructor);
     }
@@ -336,7 +337,13 @@
       public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
         // pass methods from Object.class to the proxy
         if (method.getDeclaringClass().equals(Object.class)) {
-          return method.invoke(this, creationArgs);
+          if ("equals".equals(method.getName())) {
+            return proxy == creationArgs[0];
+          } else if ("hashCode".equals(method.getName())) {
+            return System.identityHashCode(proxy);
+          } else {
+            return method.invoke(this, creationArgs);
+          }
         }
 
         AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
@@ -367,11 +374,26 @@
     };
 
     @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
-    Class<F> factoryRawType = (Class) factoryType.getRawType();
+    Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
     return factoryRawType.cast(Proxy.newProxyInstance(BytecodeGen.getClassLoader(factoryRawType),
         new Class[] { factoryRawType }, invocationHandler));
   }
 
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(factoryType, implementationType);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof FactoryProvider)) {
+      return false;
+    }
+    FactoryProvider<?> other = (FactoryProvider<?>) obj;
+    return factoryType.equals(other.factoryType)
+        && implementationType.equals(other.implementationType);
+  }
+
   private static ConfigurationException newConfigurationException(String format, Object... args) {
     return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
   }
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
index a77f1f3..971f252 100644
--- a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider2.java
@@ -128,7 +128,7 @@
     /** The factory method associated with this data*/
     final Method factoryMethod;
 
-    /** true if {@link #validForOptimizedAssistedInject} returned true. */
+    /** true if {@link #isValidForOptimizedAssistedInject} returned true. */
     final boolean optimized;
     /** the list of optimized providers, empty if not optimized. */
     final List<ThreadLocalProvider> providers;
@@ -192,20 +192,24 @@
   
   /** The key that this is bound to. */
   private final Key<F> factoryKey;
+  
+  /** The binding collector, for equality/hashing purposes. */
+  private final BindingCollector collector;
 
   /**
-   * @param factoryType a Java interface that defines one or more create methods.
+   * @param factoryKey a key for a Java interface that defines one or more create methods.
    * @param collector binding configuration that maps method return types to
    *    implementation types.
    */
   FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
     this.factoryKey = factoryKey;
+    this.collector = collector;
 
     TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
     Errors errors = new Errors();
 
     @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
-    Class<F> factoryRawType = (Class) factoryType.getRawType();
+    Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
 
     try {
       if(!factoryRawType.isInterface()) {
@@ -269,7 +273,7 @@
           continue;
         }
 
-        Constructor<?> constructor = (Constructor)ctorInjectionPoint.getMember();
+        Constructor<?> constructor = (Constructor<?>) ctorInjectionPoint.getMember();
         List<ThreadLocalProvider> providers = Collections.emptyList();
         Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
         boolean optimized = false;
@@ -321,10 +325,11 @@
     return factoryKey;
   }
 
-  // safe cast because values are typed to AssistedData, which is an AssistedMethod
+  // Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
+  // the collection is immutable.
   @SuppressWarnings("unchecked")
   public Collection<AssistedMethod> getAssistedMethods() {
-    return (Collection)assistDataByMethod.values();
+    return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
   }
 
   @SuppressWarnings("unchecked")
@@ -350,7 +355,7 @@
    * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
    * specified.
    */
-  private boolean isTypeNotSpecified(TypeLiteral typeLiteral, ConfigurationException ce) {
+  private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
     Collection<Message> messages = ce.getErrorMessages();
     if (messages.size() == 1) {
       Message msg = Iterables.getOnlyElement(
@@ -368,8 +373,8 @@
    * {@link AssistedInject} constructors exist, this will default to looking for an
    * {@literal @}{@link Inject} constructor.
    */
-  private InjectionPoint findMatchingConstructorInjectionPoint(
-      Method method, Key<?> returnType, TypeLiteral<?> implementation, List<Key<?>> paramList)
+  private <T> InjectionPoint findMatchingConstructorInjectionPoint(
+      Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
       throws ErrorsException {
     Errors errors = new Errors(method);
     if(returnType.getTypeLiteral().equals(implementation)) {
@@ -405,7 +410,8 @@
             errors
                 .addMessage(
                     "%s has more than one constructor annotated with @AssistedInject"
-                        + " that matches the parameters in method %s.  Unable to create AssistedInject factory.",
+                        + " that matches the parameters in method %s.  Unable to create "
+                        + "AssistedInject factory.",
                     implementation, method);
             throw errors.toException();
           } else {
@@ -429,7 +435,7 @@
           // safe because we got the constructor from this implementation.
           @SuppressWarnings("unchecked")
           InjectionPoint ip = InjectionPoint.forConstructor(
-              (Constructor)matchingConstructor, implementation);
+              (Constructor<? super T>) matchingConstructor, implementation);
           return ip;
       } else {
         errors.addMessage(
@@ -492,7 +498,7 @@
   private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
     ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
     for(Dependency<?> dep : deps) {
-      Class annotationType = dep.getKey().getAnnotationType();
+      Class<?> annotationType = dep.getKey().getAnnotationType();
       if (annotationType == null || !annotationType.equals(Assisted.class)) {
         builder.add(dep);
       }
@@ -534,7 +540,7 @@
    * is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}.
    */
   private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
-    Class annotationType = dependency.getKey().getAnnotationType();
+    Class<?> annotationType = dependency.getKey().getAnnotationType();
     if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
       if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider...
         return true;
@@ -593,7 +599,8 @@
   /**
    * Creates a child injector that binds the args, and returns the binding for the method's result.
    */
-  public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args, final AssistData data) {
+  public Binding<?> getBindingFromNewInjector(
+      final Method method, final Object[] args, final AssistData data) {
     checkState(injector != null,
         "Factories.create() factories cannot be used until they're initialized by Guice.");
 
@@ -603,7 +610,9 @@
     final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
 
     Module assistedModule = new AbstractModule() {
-      @Override @SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
+      @Override
+      @SuppressWarnings({
+        "unchecked", "rawtypes"}) // raw keys are necessary for the args array and return value
       protected void configure() {
         Binder binder = binder().withSource(method);
 
@@ -633,7 +642,7 @@
     };
 
     Injector forCreate = injector.createChildInjector(assistedModule);
-    Binding binding = forCreate.getBinding(returnKey);
+    Binding<?> binding = forCreate.getBinding(returnKey);
     // If we have providers cached in data, cache the binding for future optimizations.
     if(data.optimized) {
       data.cachedBinding = binding;
@@ -646,8 +655,14 @@
    * use that to get an instance of the return type.
    */
   public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
-    if (method.getDeclaringClass() == Object.class) {
-      return method.invoke(this, args);
+    if (method.getDeclaringClass().equals(Object.class)) {
+      if ("equals".equals(method.getName())) {
+        return proxy == args[0];
+      } else if ("hashCode".equals(method.getName())) {
+        return System.identityHashCode(proxy);
+      } else {
+        return method.invoke(this, args);
+      }
     }
 
     AssistData data = assistDataByMethod.get(method);
@@ -683,9 +698,18 @@
   @Override public String toString() {
     return factory.getClass().getInterfaces()[0].getName();
   }
+  
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(factoryKey, collector);
+  }
 
-  @Override public boolean equals(Object o) {
-    return o == this || o == factory;
+  @Override public boolean equals(Object obj) {
+    if (!(obj instanceof FactoryProvider2)) {
+      return false;
+    }
+    FactoryProvider2<?> other = (FactoryProvider2<?>) obj;
+    return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
   }
 
   /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
index 0a2836f..e047495 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java
@@ -251,6 +251,47 @@
     assertEquals(Color.BLUE, mustang.color);
   }
 
+  public void testDuplicateBindings() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        install(new FactoryModuleBuilder()
+            .implement(Car.class, Mustang.class)
+            .build(ColoredCarFactory.class));
+        install(new FactoryModuleBuilder()
+            .implement(Car.class, Mustang.class)
+            .build(ColoredCarFactory.class));
+      }
+    });
+
+    ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class);
+
+    Mustang mustang = (Mustang) factory.create(Color.BLUE);
+    assertEquals(Color.BLUE, mustang.color);
+  }
+
+  public void testSimilarBindingsWithConflictingImplementations() {
+    try {
+      Injector injector = Guice.createInjector(new AbstractModule() {
+        @Override
+        protected void configure() {
+          install(new FactoryModuleBuilder()
+              .implement(Car.class, Mustang.class)
+              .build(ColoredCarFactory.class));
+          install(new FactoryModuleBuilder()
+              .implement(Car.class, Golf.class)
+              .build(ColoredCarFactory.class));
+        }
+      });
+      injector.getInstance(ColoredCarFactory.class);
+      fail();
+    } catch (CreationException ce) {
+      assertContains(ce.getMessage(),
+          "A binding to " + ColoredCarFactory.class.getName() + " was already configured");
+      assertEquals(1, ce.getErrorMessages().size());
+    }
+  }
+
   public void testMultipleReturnTypes() {
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
@@ -344,10 +385,12 @@
     static interface Factory<E> {
       Foo<E> create(Bar bar);
     }
+    @SuppressWarnings("unused")
     @Inject Foo(@Assisted Bar bar, Baz<E> baz) {}
   }
   
   public static class Bar {}
+  @SuppressWarnings("unused")
   public static class Baz<E> {}
   
   abstract static class AbstractCar implements Car {}  
@@ -397,7 +440,7 @@
     boolean found = false;
     for(Element element : elements) {
       if(element instanceof Binding) {
-        Binding binding = (Binding)element;
+        Binding<?> binding = (Binding<?>) element;
         if(binding.getKey().equals(Key.get(AnimalHouse.class))) {
           found = true;
           validateDependencies(expectedKeys, binding);
@@ -411,7 +454,7 @@
   private void validateDependencies(Set<Key<?>> expectedKeys, Binding<?> binding) {
     Set<Dependency<?>> dependencies = ((HasDependencies)binding).getDependencies();
     Set<Key<?>> actualKeys = new HashSet<Key<?>>();
-    for(Dependency dependency : dependencies) {
+    for (Dependency<?> dependency : dependencies) {
       actualKeys.add(dependency.getKey());
     }
     assertEquals(expectedKeys, actualKeys);
@@ -424,11 +467,13 @@
   }
   
   interface Animal {}
+  @SuppressWarnings("unused")
   private static class Dog implements Animal {
     @Inject int a;
     @Inject Dog(@Assisted String a, double b) {}
     @Inject void register(@Named("dog") String a) {}
   }
+  @SuppressWarnings("unused")
   private static class Cat implements Animal {
     @Inject float a;
     @AssistedInject Cat(@Assisted String a, @Named("cat1") String b) {}
@@ -488,7 +533,7 @@
   @Singleton
   static class AssistedSingleton {
     @Inject
-    public AssistedSingleton(@Assisted String string) {      
+    public AssistedSingleton(@SuppressWarnings("unused") @Assisted String string) {
     }
   }
   
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
index c7fccc3..8903042 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProvider2Test.java
@@ -29,6 +29,8 @@
 import com.google.inject.Provider;
 import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryProvider2Test.Equals.ComparisonMethod;
+import com.google.inject.assistedinject.FactoryProvider2Test.Equals.Impl;
 import com.google.inject.matcher.Matchers;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
@@ -151,6 +153,7 @@
     private Color color;
     private float maxMph;
 
+    @SuppressWarnings("unused")
     public Corvette(Color color, boolean isConvertable) {
       throw new IllegalStateException("Not an @AssistedInject constructor");
     }
@@ -165,6 +168,7 @@
 
   public void testConstructorDoesntNeedAllFactoryMethodArguments() {
     Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         bind(SummerCarFactory.class).toProvider(
             FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class));
@@ -363,7 +367,7 @@
 
   public static class ExplodingCar implements Car {
     @Inject
-    public ExplodingCar(@Assisted Color color) {
+    public ExplodingCar(@SuppressWarnings("unused") @Assisted Color color) {
       throw new IllegalStateException("kaboom!");
     }
   }
@@ -386,7 +390,7 @@
 
   public static class DefectiveCar implements Car {
     @Inject
-    public DefectiveCar() throws ExplosionException, FireException {
+    public DefectiveCar() throws ExplosionException {
       throw new ExplosionException();
     }
   }
@@ -430,7 +434,7 @@
     }
 
     @Inject
-    public WildcardCollection(@Assisted Collection<?> items) { }
+    public WildcardCollection(@SuppressWarnings("unused") @Assisted Collection<?> items) { }
   }
 
   public void testWildcardGenerics() {
@@ -657,6 +661,7 @@
 
   public void testFactoryBuildingConcreteTypes() {
     Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
       protected void configure() {
         bind(double.class).toInstance(5.0d);
         // note there is no 'thatMakes()' call here:
@@ -864,13 +869,14 @@
     assertEquals(250, redCamaro.horsePower);
   }
 
+  @SuppressWarnings("unused")
   public interface Insurance<T extends Car> {
   }
 
   public static class MustangInsurance implements Insurance<Mustang> {
     private final double premium;
     private final double limit;
-    private Mustang car;
+    @SuppressWarnings("unused") private Mustang car;
 
     @Inject
     public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,
@@ -886,7 +892,7 @@
   public static class CamaroInsurance implements Insurance<Camaro> {
     private final double premium;
     private final double limit;
-    private Camaro car;
+    @SuppressWarnings("unused") private Camaro car;
 
     @Inject
     public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car,
@@ -1009,7 +1015,8 @@
         injector.getInstance(Key.get(camaroInsuranceFactoryType));
 
     Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
-    AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    AutoInsurance<?> camaroPolicy =
+        (AutoInsurance<?>) camaroInsuranceFactory.create(camaro, 800.0d);
     assertEquals(800.0d, camaroPolicy.premium);
     assertEquals(50000.0d, camaroPolicy.limit);
     assertEquals(camaro, camaroPolicy.car);
@@ -1034,6 +1041,63 @@
     assertSame(Color.GREEN, green.getColor());
   }
   
+  public void testDuplicateAssistedFactoryBinding() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(5.0d);
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueMustang.color);
+    assertEquals(5.0d, blueMustang.engineSize);
+
+    Mustang redMustang = (Mustang) carFactory.create(Color.RED);
+    assertEquals(Color.RED, redMustang.color);
+    assertEquals(5.0d, redMustang.engineSize);
+  }
+
+  public interface Equals {
+
+    enum ComparisonMethod { SHALLOW, DEEP; }
+
+    interface Factory {
+      Equals equals(Equals.ComparisonMethod comparisonMethod);
+    }
+
+    public static class Impl implements Equals {
+      private final double sigma;
+      private final ComparisonMethod comparisonMethod;
+
+      @AssistedInject
+      public Impl(double sigma, @Assisted ComparisonMethod comparisonMethod) {
+        this.sigma = sigma;
+        this.comparisonMethod = comparisonMethod;
+      }
+    }
+  }
+
+  public void testFactoryMethodCalledEquals() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(0.01d);
+        bind(Equals.Factory.class).toProvider(
+            FactoryProvider.newFactory(Equals.Factory.class, Equals.Impl.class));
+      }
+    });
+    Equals.Factory equalsFactory = injector.getInstance(Equals.Factory.class);
+    Equals.Impl shallowEquals = (Impl) equalsFactory.equals(ComparisonMethod.SHALLOW);
+    assertEquals(ComparisonMethod.SHALLOW, shallowEquals.comparisonMethod);
+    assertEquals(0.01d, shallowEquals.sigma);
+  }
+
   static class Segway implements Car {
     @Inject Injector injector;
 
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
index 1bfeeb5..c8f4b12 100644
--- a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -29,6 +29,8 @@
 import com.google.inject.Key;
 import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryProviderTest.Equals.ComparisonMethod;
+import com.google.inject.assistedinject.FactoryProviderTest.Equals.Impl;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
 import com.google.inject.spi.Dependency;
@@ -44,6 +46,7 @@
  * @author jmourits@google.com (Jerome Mourits)
  * @author jessewilson@google.com (Jesse Wilson)
  */
+@SuppressWarnings("deprecation")
 public class FactoryProviderTest extends TestCase {
   
   private enum Color { BLUE, GREEN, RED, GRAY, BLACK, ORANGE, PINK }
@@ -180,6 +183,7 @@
       this(color, 100f, true);
     }
 
+    @SuppressWarnings("unused")
     public Corvette(@Assisted Color color, @Assisted boolean isConvertable) {
       throw new IllegalStateException("Not an @AssistedInject constructor");
     }
@@ -203,14 +207,17 @@
 
   public static class Beetle implements Car {
     @AssistedInject
+    @SuppressWarnings("unused")
     public Beetle(@Assisted Color color) {
       throw new IllegalStateException("Conflicting constructors");
     }
     @AssistedInject
+    @SuppressWarnings("unused")
     public Beetle(@Assisted Color color, @Assisted boolean isConvertable) {
       throw new IllegalStateException("Conflicting constructors");
     }
     @AssistedInject
+    @SuppressWarnings("unused")
     public Beetle(@Assisted Color color, @Assisted boolean isConvertable, float maxMph) {
       throw new IllegalStateException("Conflicting constructors");
     }
@@ -357,12 +364,12 @@
             .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class));
       }
     });
-    Car car = injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
+    injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
   }
 
   public static class ExplodingCar implements Car {
     @AssistedInject
-    public ExplodingCar(@Assisted Color color) {
+    public ExplodingCar(@SuppressWarnings("unused") @Assisted Color color) {
       throw new IllegalStateException("kaboom!");
     }
   }
@@ -385,7 +392,7 @@
   
   public static class DefectiveCar implements Car {
     @AssistedInject
-    public DefectiveCar() throws ExplosionException, FireException {
+    public DefectiveCar() throws ExplosionException {
       throw new ExplosionException();
     }
   }
@@ -439,7 +446,8 @@
     }
 
     @AssistedInject
-    public MultipleConstructorDefectiveCar(@Assisted Color c) throws FireException {
+    public MultipleConstructorDefectiveCar(@SuppressWarnings("unused") @Assisted Color c)
+        throws FireException {
       throw new FireException();
     }
   }
@@ -481,7 +489,7 @@
     }
 
     @AssistedInject
-    public WildcardCollection(@Assisted Collection<?> items) { }
+    public WildcardCollection(@SuppressWarnings("unused") @Assisted Collection<?> items) { }
   }
   
   public void testWildcardGenerics() {
@@ -620,13 +628,14 @@
     assertEquals(250, redCamaro.horsePower);
   }
 
+  @SuppressWarnings("unused")
   public interface Insurance<T extends Car> {
   }
 
   public static class MustangInsurance implements Insurance<Mustang> {
     private final double premium;
     private final double limit;
-    private Mustang car;
+    @SuppressWarnings("unused") private Mustang car;
 
     @AssistedInject
     public MustangInsurance(@Named("lowLimit") double limit, @Assisted Mustang car,
@@ -642,7 +651,7 @@
   public static class CamaroInsurance implements Insurance<Camaro> {
     private final double premium;
     private final double limit;
-    private Camaro car;
+    @SuppressWarnings("unused") private Camaro car;
 
     @AssistedInject
     public CamaroInsurance(@Named("highLimit") double limit, @Assisted Camaro car,
@@ -765,9 +774,67 @@
         injector.getInstance(Key.get(camaroInsuranceFactoryType));
 
     Camaro camaro = new Camaro(3000, 1967, Color.BLUE);
-    AutoInsurance camaroPolicy = (AutoInsurance) camaroInsuranceFactory.create(camaro, 800.0d);
+    AutoInsurance<?> camaroPolicy =
+        (AutoInsurance<?>) camaroInsuranceFactory.create(camaro, 800.0d);
     assertEquals(800.0d, camaroPolicy.premium);
     assertEquals(50000.0d, camaroPolicy.limit);
     assertEquals(camaro, camaroPolicy.car);
   }
+
+  public void testDuplicateAssistedFactoryBinding() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(5.0d);
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueMustang.color);
+    assertEquals(5.0d, blueMustang.engineSize);
+
+    Mustang redMustang = (Mustang) carFactory.create(Color.RED);
+    assertEquals(Color.RED, redMustang.color);
+    assertEquals(5.0d, redMustang.engineSize);
+  }
+
+  public interface Equals {
+
+    enum ComparisonMethod { SHALLOW, DEEP; }
+
+    interface Factory {
+      Equals equals(Equals.ComparisonMethod comparisonMethod);
+    }
+
+    public static class Impl implements Equals {
+      private final double sigma;
+      private final ComparisonMethod comparisonMethod;
+
+      @Inject
+      public Impl(double sigma, @Assisted ComparisonMethod comparisonMethod) {
+        this.sigma = sigma;
+        this.comparisonMethod = comparisonMethod;
+      }
+    }
+  }
+
+  public void testFactoryMethodCalledEquals() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(0.01d);
+        bind(Equals.Factory.class).toProvider(
+            FactoryProvider.newFactory(Equals.Factory.class, Equals.Impl.class));
+      }
+    });
+    Equals.Factory equalsFactory = injector.getInstance(Equals.Factory.class);
+    Equals.Impl shallowEquals = (Impl) equalsFactory.equals(ComparisonMethod.SHALLOW);
+    assertEquals(ComparisonMethod.SHALLOW, shallowEquals.comparisonMethod);
+    assertEquals(0.01d, shallowEquals.sigma);
+  }
 }
\ No newline at end of file