fix provider circular dependency detection to use the Key it is creating, not the Key it is fulfulling, to catch errors sooner.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1544 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/core/src/com/google/inject/internal/BindingProcessor.java b/core/src/com/google/inject/internal/BindingProcessor.java
index 4b57f6a..524230e 100644
--- a/core/src/com/google/inject/internal/BindingProcessor.java
+++ b/core/src/com/google/inject/internal/BindingProcessor.java
@@ -100,7 +100,7 @@
         Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
         Initializable<Provider<? extends T>> initializable = initializer
             .<Provider<? extends T>>requestInjection(injector, provider, source, injectionPoints);
-        InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
+        InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(key,
             initializable, source, !injector.options.disableCircularProxies);
         InternalFactory<? extends T> scopedFactory
             = Scoping.scope(key, injector, factory, source, scoping);
@@ -112,7 +112,7 @@
       public Boolean visit(ProviderKeyBinding<? extends T> binding) {
         prepareBinding();
         Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
-        BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
+        BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(key,
             injector, providerKey, source, !injector.options.disableCircularProxies);
         bindingData.addCreationListener(boundProviderFactory);
         InternalFactory<? extends T> scopedFactory = Scoping.scope(
diff --git a/core/src/com/google/inject/internal/BoundProviderFactory.java b/core/src/com/google/inject/internal/BoundProviderFactory.java
index 2a3d790..2988f16 100644
--- a/core/src/com/google/inject/internal/BoundProviderFactory.java
+++ b/core/src/com/google/inject/internal/BoundProviderFactory.java
@@ -30,11 +30,12 @@
   private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
 
   BoundProviderFactory(
+      Key<T> key,
       InjectorImpl injector,
       Key<? extends javax.inject.Provider<? extends T>> providerKey,
       Object source,
       boolean allowProxy) {
-    super(source, allowProxy);
+    super(key, source, allowProxy);
     this.injector = injector;
     this.providerKey = providerKey;
   }
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index 715f933..e0361b4 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -678,7 +678,7 @@
         = getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
 
     InternalFactory<T> internalFactory =
-        new ProviderInternalFactory<T>(providerKey, !options.disableCircularProxies) {
+        new ProviderInternalFactory<T>(key, providerKey, !options.disableCircularProxies) {
       public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
           throws ErrorsException {
         errors = errors.withSource(providerKey);
diff --git a/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java b/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
index af3c470..eeb35a7 100644
--- a/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
+++ b/core/src/com/google/inject/internal/InternalFactoryToInitializableAdapter.java
@@ -16,6 +16,7 @@
 
 package com.google.inject.internal;
 
+import com.google.inject.Key;
 import com.google.inject.Provider;
 import static com.google.inject.internal.util.Preconditions.checkNotNull;
 import com.google.inject.spi.Dependency;
@@ -32,8 +33,9 @@
   private final Initializable<Provider<? extends T>> initializable;
 
   public InternalFactoryToInitializableAdapter(
-      Initializable<Provider<? extends T>> initializable, Object source, boolean allowProxy) {
-    super(source, allowProxy);
+      Key<T> key, Initializable<Provider<? extends T>> initializable,
+      Object source, boolean allowProxy) {
+    super(key, source, allowProxy);
     this.initializable = checkNotNull(initializable, "provider");
   }
 
diff --git a/core/src/com/google/inject/internal/ProviderInternalFactory.java b/core/src/com/google/inject/internal/ProviderInternalFactory.java
index aba14d9..7ed741c 100644
--- a/core/src/com/google/inject/internal/ProviderInternalFactory.java
+++ b/core/src/com/google/inject/internal/ProviderInternalFactory.java
@@ -21,6 +21,7 @@
 

 import javax.inject.Provider;

 

+import com.google.inject.Key;

 import com.google.inject.spi.Dependency;

 

 /**

@@ -31,10 +32,12 @@
  */

 abstract class ProviderInternalFactory<T> implements InternalFactory<T> {

   

+  private final Key<T> key;

   protected final Object source;

   private final boolean allowProxy;

   

-  ProviderInternalFactory(Object source, boolean allowProxy) {

+  ProviderInternalFactory(Key<T> key, Object source, boolean allowProxy) {

+    this.key = key;

     this.source = checkNotNull(source, "source");

     this.allowProxy = allowProxy;

   }

@@ -43,9 +46,13 @@
       InternalContext context, Dependency<?> dependency, boolean linked)

       throws ErrorsException {    

     Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();

-    // we do not use 'this' as the key because for JIT provider bindings,

-    // 'this' is recreated on failures... the key suffices.

-    ConstructionContext<T> constructionContext = context.getConstructionContext(dependency.getKey());

+    

+    // Use the Key we are providing for as a unique key to locate the context.

+    // We cannot use dependency.getKey() because that is the Key of the type

+    // we are trying to fulfill (which may be different among different

+    // calls to us).  We also cannot use 'this', because the factory can

+    // be recreated different times during @ProvidedBy creations.

+    ConstructionContext<T> constructionContext = context.getConstructionContext(key);

     

     // We have a circular reference between constructors. Return a proxy.

     if (constructionContext.isConstructing()) {

diff --git a/core/test/com/google/inject/CircularDependencyTest.java b/core/test/com/google/inject/CircularDependencyTest.java
index 3fbfc83..0a185bb 100644
--- a/core/test/com/google/inject/CircularDependencyTest.java
+++ b/core/test/com/google/inject/CircularDependencyTest.java
@@ -16,6 +16,9 @@
 
 package com.google.inject;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import junit.framework.TestCase;
 import static com.google.inject.Asserts.assertContains;
 
@@ -405,4 +408,35 @@
       return "G";
     }
   }
+  
+  /**
+   * Tests that ProviderInternalFactory can detect circular dependencies
+   * before it gets to Scopes.SINGLETON.  This is especially important
+   * because the failure in Scopes.SINGLETON doesn't have enough context to
+   * provide a decent error message.
+   */
+  public void testCircularDependenciesDetectedEarlyWhenDependenciesHaveDifferentTypes() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Number.class).to(Integer.class);
+      }
+      
+      @Provides @Singleton Integer provideInteger(List list) { 
+        return new Integer(2);
+      }
+      
+      @Provides List provideList(Integer integer) {
+        return new ArrayList();
+      }
+    });
+    try {
+      injector.getInstance(Number.class);
+      fail();
+    } catch(ProvisionException expected) {
+      assertContains(expected.getMessage(),
+          "Tried proxying " + Integer.class.getName() + " to support a circular dependency, ",
+          "but it is not an interface.");      
+    }
+  }
 }