Add an SPI for @Provides methods (using the extensions SPI) so that users can
do more analysis (with the enclosing instance, method, etc..).

Notably, this can let users write analysis that looks for @Provides methods
declared with @Nullable, and compare against injection points w/o @Nullable,
failing if so.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=62834918
diff --git a/core/src/com/google/inject/internal/ProviderMethod.java b/core/src/com/google/inject/internal/ProviderMethod.java
index 6221499..d8bc18b 100644
--- a/core/src/com/google/inject/internal/ProviderMethod.java
+++ b/core/src/com/google/inject/internal/ProviderMethod.java
@@ -24,8 +24,13 @@
 import com.google.inject.PrivateBinder;
 import com.google.inject.Provider;
 import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.spi.BindingTargetVisitor;
 import com.google.inject.spi.Dependency;
-import com.google.inject.spi.ProviderWithDependencies;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderWithExtensionVisitor;
+import com.google.inject.spi.ProvidesMethodBinding;
+import com.google.inject.spi.ProvidesMethodTargetVisitor;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
@@ -38,7 +43,8 @@
  *
  * @author jessewilson@google.com (Jesse Wilson)
  */
-public class ProviderMethod<T> implements ProviderWithDependencies<T> {
+public class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
+    ProvidesMethodBinding<T> {
   private final Key<T> key;
   private final Class<? extends Annotation> scopeAnnotation;
   private final Object instance;
@@ -76,6 +82,10 @@
   public Object getInstance() {
     return instance;
   }
+  
+  public Object getEnclosingInstance() {
+    return instance;
+  }
 
   public void configure(Binder binder) {
     binder = binder.withSource(method);
@@ -114,6 +124,15 @@
   public Set<Dependency<?>> getDependencies() {
     return dependencies;
   }
+  
+  @SuppressWarnings("unchecked")
+  public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
+      ProviderInstanceBinding<? extends B> binding) {
+    if (visitor instanceof ProvidesMethodTargetVisitor) {
+      return ((ProvidesMethodTargetVisitor<T, V>)visitor).visit(this);
+    }
+    return visitor.visit(binding);
+  }
 
   @Override public String toString() {
     return "@Provides " + StackTraceElements.forMember(method).toString();
diff --git a/core/src/com/google/inject/spi/ProvidesMethodBinding.java b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
new file mode 100644
index 0000000..f35d9a4
--- /dev/null
+++ b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
@@ -0,0 +1,24 @@
+package com.google.inject.spi;
+
+import com.google.inject.Key;
+import com.google.inject.Provides;
+
+import java.lang.reflect.Method;
+
+/**
+ * An {@literal @}{@link Provides} binding.
+ * 
+ * @since 4.0
+ * @author sameb@google.com (Sam Berlin)
+ */
+public interface ProvidesMethodBinding<T> extends HasDependencies {
+  
+  /** Returns the method this binding uses. */
+  Method getMethod(); 
+  
+  /** Returns the instance of the object the method is defined in. */
+  Object getEnclosingInstance();
+  
+  /** Returns the key of the binding. */
+  Key<T> getKey();
+}
diff --git a/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java b/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java
new file mode 100644
index 0000000..d6c4973
--- /dev/null
+++ b/core/src/com/google/inject/spi/ProvidesMethodTargetVisitor.java
@@ -0,0 +1,21 @@
+package com.google.inject.spi;
+
+import com.google.inject.Provides;
+import com.google.inject.spi.BindingTargetVisitor;
+
+/**
+ * A visitor for the {@literal @}{@link Provides} bindings.
+ * <p>
+ * If your {@link BindingTargetVisitor} implements this interface, bindings created by using
+ * {@code @Provides} will be visited through this interface.
+ *
+ * @since 4.0
+ * @author sameb@google.com (Sam Berlin)
+ */
+public interface ProvidesMethodTargetVisitor<T, V> extends BindingTargetVisitor<T, V> {
+  
+  /**
+   * Visits an {@link ProvidesMethodBinding} created with an {@literal @}{@link Provides} method.
+   */
+  V visit(ProvidesMethodBinding<? extends T> providesMethodBinding);
+}
diff --git a/core/test/com/google/inject/spi/ProviderMethodsTest.java b/core/test/com/google/inject/spi/ProviderMethodsTest.java
index 433500e..466e835 100644
--- a/core/test/com/google/inject/spi/ProviderMethodsTest.java
+++ b/core/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
+import com.google.inject.Binding;
 import com.google.inject.BindingAnnotation;
 import com.google.inject.CreationException;
 import com.google.inject.Guice;
@@ -403,4 +404,52 @@
       return 42;
     }
   }
+  
+  public void testSpi() throws Exception {
+    Module m1 = new AbstractModule() {
+      @Override protected void configure() {}
+      @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; }
+    };
+    Module m2 = new AbstractModule() {
+      @Override protected void configure() {}
+      @Provides Integer provideInt(@Named("foo") String dep) { return 42; }
+    };
+    Injector injector = Guice.createInjector(m1, m2);
+    
+    Binding<String> stringBinding =
+        injector.getBinding(Key.get(String.class, Names.named("foo")));
+    ProvidesMethodBinding<String> stringMethod =
+        stringBinding.acceptTargetVisitor(new BindingCapturer<String>());
+    assertEquals(m1, stringMethod.getEnclosingInstance());
+    assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class),
+        stringMethod.getMethod());
+    assertEquals(((HasDependencies) stringBinding).getDependencies(),
+        stringMethod.getDependencies());
+    assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey());
+    
+    Binding<Integer> intBinding = injector.getBinding(Integer.class);
+    ProvidesMethodBinding<Integer> intMethod =
+        intBinding.acceptTargetVisitor(new BindingCapturer<Integer>());
+    assertEquals(m2, intMethod.getEnclosingInstance());
+    assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class),
+        intMethod.getMethod());
+    assertEquals(((HasDependencies) intBinding).getDependencies(),
+        intMethod.getDependencies());
+    assertEquals(Key.get(Integer.class), intMethod.getKey());
+    
+  }
+  
+  private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
+      implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
+    
+    @SuppressWarnings("unchecked")
+    public ProvidesMethodBinding<T> visit(
+        ProvidesMethodBinding<? extends T> providesMethodBinding) {
+      return (ProvidesMethodBinding<T>)providesMethodBinding;
+    }
+    
+    @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
+      throw new IllegalStateException("unexpected visit of: " + binding);
+    }    
+  }
 }