Implement my old patch for issue #366, with some tweaks.  This does the
following:
* Fixes @Provides injection so that parameters are checked for nullability.
By default this will error.  The flag is named:
guice_check_nullable_provides_params and can be set to ERROR, WARNING or IGNORE.
* Adds InjectionPoint.forMethod to build an InjectionPoint off an arbitrary
method.
* Adds Binder.getProvider(Dependency) to a get a Provider for a given
dependency (with all its nullability & injection points maintained).
* Update ProviderLookup to accept a Dependency in addition to a Key.

This is in preparation for two things:
1) Allowing multibindings/mapbindings/optionalbindings to be specified as
annotations on methods in a module.
2) Adding a dagger compatibility module.
... the general idea will be that I'll also add a hook into
ProvidesMethodModule somehow to look at arbitrary other annotations and let
folks process them specially.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=85353820
diff --git a/core/src/com/google/inject/Binder.java b/core/src/com/google/inject/Binder.java
index 91f436f..e895759 100644
--- a/core/src/com/google/inject/Binder.java
+++ b/core/src/com/google/inject/Binder.java
@@ -20,6 +20,7 @@
 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.matcher.Matcher;
+import com.google.inject.spi.Dependency;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.ProvisionListener;
 import com.google.inject.spi.TypeConverter;
@@ -323,6 +324,18 @@
   <T> Provider<T> getProvider(Key<T> key);
 
   /**
+   * Returns the provider used to obtain instances for the given injection key.
+   * The returned provider will be attached to the injection point and will
+   * follow the nullability specified in the dependency.
+   * Additionally, the returned provider will not be valid until the {@link Injector} 
+   * has been created. The provider will throw an {@code IllegalStateException} if you
+   * try to use it beforehand.
+   *
+   * @since 4.0
+   */
+  <T> Provider<T> getProvider(Dependency<T> dependency);
+
+  /**
    * Returns the provider used to obtain instances for the given injection type.
    * The returned provider will not be valid until the {@link Injector} has been
    * created. The provider will throw an {@code IllegalStateException} if you
diff --git a/core/src/com/google/inject/internal/Errors.java b/core/src/com/google/inject/internal/Errors.java
index 385d4b8..87e0cc8 100644
--- a/core/src/com/google/inject/internal/Errors.java
+++ b/core/src/com/google/inject/internal/Errors.java
@@ -22,9 +22,11 @@
 import com.google.common.collect.Ordering;
 import com.google.inject.ConfigurationException;
 import com.google.inject.CreationException;
+import com.google.inject.Guice;
 import com.google.inject.Key;
 import com.google.inject.MembersInjector;
 import com.google.inject.Provider;
+import com.google.inject.Provides;
 import com.google.inject.ProvisionException;
 import com.google.inject.Scope;
 import com.google.inject.TypeLiteral;
@@ -53,6 +55,8 @@
 import java.util.Formatter;
 import java.util.List;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * A collection of error messages. If this type is passed as a method parameter, the method is
@@ -71,6 +75,8 @@
  */
 public final class Errors implements Serializable {
 
+  private static final Logger logger = Logger.getLogger(Guice.class.getName());
+
 
   /**
    * The root errors object. Used to access the list of error messages.
@@ -602,6 +608,29 @@
       return value;
     }
 
+    // Hack to allow null parameters to @Provides methods, for backwards compatibility.
+    if (dependency.getInjectionPoint().getMember() instanceof Method) {
+      Method annotated = (Method) dependency.getInjectionPoint().getMember();
+      if (annotated.isAnnotationPresent(Provides.class)) {
+        switch (InternalFlags.getNullableProvidesOption()) {
+          case ERROR:
+            break; // break out & let the below exception happen
+          case IGNORE:
+            return value; // user doesn't care about injecting nulls to non-@Nullables.
+          case WARN:
+            logger.log(Level.WARNING,
+                "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
+                    + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+                    + " error.",
+                new Object[] {
+                    dependency.getParameterIndex(),
+                    convert(dependency.getInjectionPoint().getMember()),
+                    convert(dependency.getKey())});
+            return null; // log & exit.
+        }
+      }
+    }
+
     int parameterIndex = dependency.getParameterIndex();
     String parameterName = (parameterIndex != -1)
         ? "parameter " + parameterIndex + " of "
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index eef7d98..d1d028f 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -1000,9 +1000,9 @@
     return getProvider(Key.get(type));
   }
 
-  <T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException {
+  <T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException {
+    final Key<T> key = dependency.getKey();
     final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
-    final Dependency<T> dependency = Dependency.get(key);
 
     return new Provider<T>() {
       public T get() {
@@ -1034,7 +1034,7 @@
   public <T> Provider<T> getProvider(final Key<T> key) {
     Errors errors = new Errors(key);
     try {
-      Provider<T> result = getProviderOrThrow(key, errors);
+      Provider<T> result = getProviderOrThrow(Dependency.get(key), errors);
       errors.throwIfNewErrors(0);
       return result;
     } catch (ErrorsException e) {
diff --git a/core/src/com/google/inject/internal/InternalFlags.java b/core/src/com/google/inject/internal/InternalFlags.java
index 4e0b227..85c07ac 100644
--- a/core/src/com/google/inject/internal/InternalFlags.java
+++ b/core/src/com/google/inject/internal/InternalFlags.java
@@ -33,6 +33,9 @@
   private static final CustomClassLoadingOption CUSTOM_CLASS_LOADING
       = parseCustomClassLoadingOption();
 
+  private static final NullableProvidesOption NULLABLE_PROVIDES
+      = parseNullableProvidesOption(NullableProvidesOption.ERROR);
+
 
   /**
    * The options for Guice stack trace collection.
@@ -56,6 +59,15 @@
     BRIDGE
   }
 
+  public enum NullableProvidesOption {
+    /** Ignore null parameters to @Provides methods. */
+    IGNORE,
+    /** Warn if null parameters are passed to non-@Nullable parameters of provides methods. */
+    WARN,
+    /** Error if null parameters are passed to non-@Nullable parameters of provides parameters */
+    ERROR
+  }
+
   public static IncludeStackTraceOption getIncludeStackTraceOption() {
     return INCLUDE_STACK_TRACES;
   }
@@ -64,6 +76,10 @@
     return CUSTOM_CLASS_LOADING;
   }
 
+  public static NullableProvidesOption getNullableProvidesOption() {
+    return NULLABLE_PROVIDES;
+  }
+
   private static IncludeStackTraceOption parseIncludeStackTraceOption() {
     return getSystemOption("guice_include_stack_traces",
         IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE);
@@ -74,6 +90,11 @@
         CustomClassLoadingOption.BRIDGE, CustomClassLoadingOption.OFF);
   }
 
+  private static NullableProvidesOption parseNullableProvidesOption(
+      NullableProvidesOption defaultValue) {
+    return getSystemOption("guice_check_nullable_provides_params", defaultValue);
+  }
+
   /**
    * Gets the system option indicated by the specified key; runs as a privileged action.
    *
diff --git a/core/src/com/google/inject/internal/LookupProcessor.java b/core/src/com/google/inject/internal/LookupProcessor.java
index 3971818..bf11b83 100644
--- a/core/src/com/google/inject/internal/LookupProcessor.java
+++ b/core/src/com/google/inject/internal/LookupProcessor.java
@@ -48,7 +48,7 @@
   @Override public <T> Boolean visit(ProviderLookup<T> lookup) {
     // ensure the provider can be created
     try {
-      Provider<T> provider = injector.getProviderOrThrow(lookup.getKey(), errors);
+      Provider<T> provider = injector.getProviderOrThrow(lookup.getDependency(), errors);
       lookup.initializeDelegate(provider);
     } catch (ErrorsException e) {
       errors.merge(e.getErrors()); // TODO: source
diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java
index 214039d..77a8ff2 100644
--- a/core/src/com/google/inject/internal/ProviderMethodsModule.java
+++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java
@@ -29,6 +29,7 @@
 import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.Dependency;
+import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
 import com.google.inject.util.Modules;
 
@@ -38,7 +39,6 @@
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.List;
-import java.util.logging.Logger;
 
 /**
  * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
@@ -48,7 +48,6 @@
  * @author jessewilson@google.com (Jesse Wilson)
  */
 public final class ProviderMethodsModule implements Module {
-  private static final Key<Logger> LOGGER_KEY = Key.get(Logger.class);
 
   private final Object delegate;
   private final TypeLiteral<?> typeLiteral;
@@ -202,35 +201,21 @@
     Errors errors = new Errors(method);
 
     // prepare the parameter providers
-    List<Dependency<?>> dependencies = Lists.newArrayList();
+    InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
+    List<Dependency<?>> dependencies = point.getDependencies();
     List<Provider<?>> parameterProviders = Lists.newArrayList();
-    List<TypeLiteral<?>> parameterTypes = typeLiteral.getParameterTypes(method);
-    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
-    for (int i = 0; i < parameterTypes.size(); i++) {
-      Key<?> key = getKey(errors, parameterTypes.get(i), method, parameterAnnotations[i]);
-      if (key.equals(LOGGER_KEY)) {
-        // If it was a Logger, change the key to be unique & bind it to a
-        // provider that provides a logger with a proper name.
-        // This solves issue 482 (returning a new anonymous logger on every call exhausts memory)
-        Key<Logger> loggerKey = Key.get(Logger.class, UniqueAnnotations.create());
-        binder.bind(loggerKey).toProvider(new LogProvider(method));
-        key = loggerKey;
-      }
-      dependencies.add(Dependency.get(key));
-      parameterProviders.add(binder.getProvider(key));        
+    for (Dependency<?> dependency : point.getDependencies()) {
+      parameterProviders.add(binder.getProvider(dependency));
     }
 
     @SuppressWarnings("unchecked") // Define T as the method's return type.
     TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
-
     Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
     Class<? extends Annotation> scopeAnnotation
         = Annotations.findScopeAnnotation(errors, method.getAnnotations());
-
     for (Message message : errors.getMessages()) {
       binder.addError(message);
     }
-
     return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies),
         parameterProviders, scopeAnnotation, skipFastClassGeneration);
   }
@@ -248,17 +233,4 @@
   @Override public int hashCode() {
     return delegate.hashCode();
   }
-  
-  /** A provider that returns a logger based on the method name. */
-  private static final class LogProvider implements Provider<Logger> {
-    private final String name;
-    
-    public LogProvider(Method method) {
-      this.name = method.getDeclaringClass().getName() + "." + method.getName();
-    }
-    
-    public Logger get() {
-      return Logger.getLogger(name);
-    }
-  }
 }
diff --git a/core/src/com/google/inject/spi/Elements.java b/core/src/com/google/inject/spi/Elements.java
index f507fe0..986582e 100644
--- a/core/src/com/google/inject/spi/Elements.java
+++ b/core/src/com/google/inject/spi/Elements.java
@@ -302,7 +302,11 @@
     }
 
     public <T> Provider<T> getProvider(final Key<T> key) {
-      final ProviderLookup<T> element = new ProviderLookup<T>(getElementSource(), key);
+      return getProvider(Dependency.get(key));
+    }
+
+    public <T> Provider<T> getProvider(final Dependency<T> dependency) {
+      final ProviderLookup<T> element = new ProviderLookup<T>(getElementSource(), dependency);
       elements.add(element);
       return element.getProvider();
     }
diff --git a/core/src/com/google/inject/spi/InjectionPoint.java b/core/src/com/google/inject/spi/InjectionPoint.java
index 653f87c..267e0ab 100644
--- a/core/src/com/google/inject/spi/InjectionPoint.java
+++ b/core/src/com/google/inject/spi/InjectionPoint.java
@@ -307,6 +307,20 @@
   }
 
   /**
+   * Returns a new injection point for the specified method of {@code type}.
+   * This is useful for extensions that need to build dependency graphs from
+   * arbitrary methods.
+   *
+   * @param method any single method present on {@code type}.
+   * @param type the concrete type that defines {@code method}.
+   *
+   * @since 4.0
+   */
+  public static <T> InjectionPoint forMethod(Method method, TypeLiteral<T> type) {
+    return new InjectionPoint(type, method, false);
+  }
+
+  /**
    * Returns all static method and field injection points on {@code type}.
    *
    * @return a possibly empty set of injection points. The set has a specified iteration order. All
diff --git a/core/src/com/google/inject/spi/ProviderLookup.java b/core/src/com/google/inject/spi/ProviderLookup.java
index e4b5da6..a232431 100644
--- a/core/src/com/google/inject/spi/ProviderLookup.java
+++ b/core/src/com/google/inject/spi/ProviderLookup.java
@@ -39,12 +39,17 @@
  */
 public final class ProviderLookup<T> implements Element {
   private final Object source;
-  private final Key<T> key;
+  private final Dependency<T> dependency;
   private Provider<T> delegate;
 
   public ProviderLookup(Object source, Key<T> key) {
+    this(source, Dependency.get(checkNotNull(key, "key")));
+  }
+
+  /** @since 4.0 */
+  public ProviderLookup(Object source, Dependency<T> dependency) {
     this.source = checkNotNull(source, "source");
-    this.key = checkNotNull(key, "key");
+    this.dependency = checkNotNull(dependency, "dependency");
   }
 
   public Object getSource() {
@@ -52,7 +57,11 @@
   }
 
   public Key<T> getKey() {
-    return key;
+    return dependency.getKey();
+  }
+
+  public Dependency<T> getDependency() {
+    return dependency;
   }
 
   public <T> T acceptVisitor(ElementVisitor<T> visitor) {
@@ -70,7 +79,7 @@
   }
 
   public void applyTo(Binder binder) {
-    initializeDelegate(binder.withSource(getSource()).getProvider(key));
+    initializeDelegate(binder.withSource(getSource()).getProvider(dependency));
   }
 
   /**
@@ -93,16 +102,16 @@
             "This Provider cannot be used until the Injector has been created.");
         return delegate.get();
       }
-      
+
       public Set<Dependency<?>> getDependencies() {
         // We depend on Provider<T>, not T directly.  This is an important distinction
         // for dependency analysis tools that short-circuit on providers.
-        Key<?> providerKey = key.ofType(Types.providerOf(key.getTypeLiteral().getType()));
+        Key<?> providerKey = getKey().ofType(Types.providerOf(getKey().getTypeLiteral().getType()));
         return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
       }
 
       @Override public String toString() {
-        return "Provider<" + key.getTypeLiteral() + ">";
+        return "Provider<" + getKey().getTypeLiteral() + ">";
       }
     };
   }
diff --git a/core/test/com/google/inject/spi/ProviderMethodsTest.java b/core/test/com/google/inject/spi/ProviderMethodsTest.java
index cf08e46..5814967 100644
--- a/core/test/com/google/inject/spi/ProviderMethodsTest.java
+++ b/core/test/com/google/inject/spi/ProviderMethodsTest.java
@@ -21,6 +21,8 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.inject.AbstractModule;
 import com.google.inject.Binder;
 import com.google.inject.Binding;
@@ -32,24 +34,33 @@
 import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provides;
+import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.google.inject.Stage;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.InternalFlags;
 import com.google.inject.internal.ProviderMethod;
 import com.google.inject.internal.ProviderMethodsModule;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
+import com.google.inject.util.Providers;
 import com.google.inject.util.Types;
 
 import junit.framework.TestCase;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
 import java.util.logging.Logger;
 
 /**
@@ -211,7 +222,7 @@
   public void testGenericProviderMethods() {
     Injector injector = Guice.createInjector(
         new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
-    
+
     assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
     assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
     assertEquals(ImmutableSet.of("A", "B"),
@@ -246,7 +257,7 @@
       return ImmutableSet.of(first, second);
     }
   }
-  
+
   public void testAutomaticProviderMethods() {
     Injector injector = Guice.createInjector((Module) new AbstractModule() {
       @Override protected void configure() { }
@@ -281,7 +292,7 @@
     Injector injector = Guice.createInjector(installsSelf);
     assertEquals("A5", injector.getInstance(String.class));
   }
-  
+
   public void testWildcardProviderMethods() {
     final List<String> strings = ImmutableList.of("A", "B", "C");
     final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
@@ -312,8 +323,8 @@
     @Inject Class<?> type;
   }
 
-  public void testProviderMethodDependenciesAreExposed() {
-    Injector injector = Guice.createInjector(new AbstractModule() {
+  public void testProviderMethodDependenciesAreExposed() throws Exception {
+    Module module = new AbstractModule() {
       @Override protected void configure() {
         bind(Integer.class).toInstance(50);
         bindConstant().annotatedWith(Names.named("units")).to("Kg");
@@ -321,13 +332,18 @@
       @Provides @Named("weight") String provideWeight(Integer count, @Named("units") String units) {
         return count + units;
       }
-    });
+    };
+    Injector injector = Guice.createInjector(module);
 
     ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding(
         Key.get(String.class, Names.named("weight")));
-    assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Integer.class)),
-        Dependency.get(Key.get(String.class, Names.named("units")))),
-        binding.getDependencies());
+    Method method =
+      module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class);
+    InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false);
+    assertEquals(ImmutableSet.<Dependency<?>>of(
+        new Dependency<Integer>(point, Key.get(Integer.class), false, 0),
+        new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)),
+         binding.getDependencies());
   }
 
   public void testNonModuleProviderMethods() {
@@ -357,9 +373,9 @@
         element instanceof ProviderInstanceBinding);
 
     ProviderInstanceBinding binding = (ProviderInstanceBinding) element;
-    javax.inject.Provider provider = binding.getUserSuppliedProvider();    
+    javax.inject.Provider provider = binding.getUserSuppliedProvider();
     assertTrue(provider instanceof ProviderMethod);
-    assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());    
+    assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());
     assertSame(provider, binding.getProviderInstance());
   }
 
@@ -372,29 +388,29 @@
       });
       fail();
     } catch (CreationException expected) {
-      assertContains(expected.getMessage(), 
+      assertContains(expected.getMessage(),
           "1) Provider methods must return a value. Do not return void.",
           getClass().getName(), ".provideFoo(ProviderMethodsTest.java:");
     }
   }
-  
+
   public void testInjectsJustOneLogger() {
     AtomicReference<Logger> loggerRef = new AtomicReference<Logger>();
     Injector injector = Guice.createInjector(new FooModule(loggerRef));
-    
+
     assertNull(loggerRef.get());
     injector.getInstance(Integer.class);
     Logger lastLogger = loggerRef.getAndSet(null);
     assertNotNull(lastLogger);
     injector.getInstance(Integer.class);
     assertSame(lastLogger, loggerRef.get());
-    
-    assertEquals(FooModule.class.getName() + ".foo", lastLogger.getName());
+
+    assertEquals(FooModule.class.getName(), lastLogger.getName());
   }
-  
+
   private static class FooModule extends AbstractModule {
     private final AtomicReference<Logger> loggerRef;
-    
+
     public FooModule(AtomicReference<Logger> loggerRef) {
       this.loggerRef = loggerRef;
     }
@@ -407,7 +423,7 @@
       return 42;
     }
   }
-  
+
   public void testSpi() throws Exception {
     Module m1 = new AbstractModule() {
       @Override protected void configure() {}
@@ -418,7 +434,7 @@
       @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 =
@@ -429,7 +445,7 @@
     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>());
@@ -439,21 +455,21 @@
     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);
-    }    
+    }
   }
 
   public void testProvidesMethodVisibility() {
@@ -495,7 +511,7 @@
       fail("Expected injector creation failure");
     } catch (CreationException expected) {
       // both of our super class bindings cause errors
-      assertContains(expected.getMessage(), 
+      assertContains(expected.getMessage(),
           "A binding to java.lang.Long was already configured",
           "A binding to java.lang.Integer was already configured");
     }
@@ -561,14 +577,14 @@
   public void testShareFastClassWithSuperClass() {
     CallerInspecterSubClassModule module = new CallerInspecterSubClassModule();
     Guice.createInjector(Stage.PRODUCTION, module);
-    assertEquals("Expected provider methods in the same class to share fastclass classes", 
+    assertEquals("Expected provider methods in the same class to share fastclass classes",
         module.fooCallerClass, module.barCallerClass);
     assertFalse(
         "Did not expect provider methods in the subclasses to share fastclass classes "
             + "with their parent classes",
         module.bazCallerClass.equals(module.barCallerClass));
   }
-  
+
 
   private static class CallerInspecterSubClassModule extends CallerInspecterModule {
     String bazCallerClass;
@@ -594,7 +610,7 @@
     @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) {
       return f;
     }
-    
+
     @Provides @Named("list") List<String> annotatedGenericProviderMethod() {
       return new ArrayList<String>();
     }
@@ -617,7 +633,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
@@ -634,7 +650,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
@@ -651,7 +667,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
@@ -667,13 +683,13 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
     }
   }
-  
+
 
   public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() {
     class SubClassModule extends SuperClassModule {
@@ -685,7 +701,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
@@ -702,7 +718,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
           "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
@@ -747,11 +763,11 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
-          "@Provides method: " + SuperClassModule.class.getName() 
+          "@Provides method: " + SuperClassModule.class.getName()
               + ".annotatedGenericParameterProviderMethod()",
-          "overridden by: " + SubClassModule.class.getName() 
+          "overridden by: " + SubClassModule.class.getName()
               + ".annotatedGenericParameterProviderMethod()");
     }
   }
@@ -767,7 +783,7 @@
       Guice.createInjector(new SubClassModule());
       fail();
     } catch (CreationException e) {
-      assertContains(e.getMessage(), 
+      assertContains(e.getMessage(),
           "Overriding @Provides methods is not allowed.",
           "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()",
           "overridden by: " + SubClassModule.class.getName() + ".rawProvider()");
@@ -836,4 +852,101 @@
   public void testIgnoreSyntheticBridgeMethods() {
     Guice.createInjector(new ModuleImpl());
   }
+
+  public void testNullability() throws Exception {
+    Module module = new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(String.class).toProvider(Providers.<String>of(null));
+      }
+
+      @SuppressWarnings("unused")
+      @Provides
+      Integer fail(String foo) {
+        return 1;
+      }
+
+      @SuppressWarnings("unused")
+      @Provides
+      Long succeed(@Nullable String foo) {
+        return 2L;
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    InjectionPoint fooPoint = InjectionPoint.forMethod(
+        module.getClass().getDeclaredMethod("fail", String.class),
+        TypeLiteral.get(module.getClass()));
+    Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies());
+
+    runNullableTest(injector, fooDependency, module);
+
+    injector.getInstance(Long.class);
+  }
+
+  private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) {
+    switch (InternalFlags.getNullableProvidesOption()) {
+      case ERROR:
+        validateNullableFails(injector, module);
+        break;
+      case IGNORE:
+        validateNullableIgnored(injector);
+        break;
+      case WARN:
+        validateNullableWarns(injector, dependency);
+        break;
+    }
+  }
+
+  private void validateNullableFails(Injector injector, Module module) {
+    try {
+      injector.getInstance(Integer.class);
+      fail();
+    } catch (ProvisionException expected) {
+      assertContains(expected.getMessage(),
+          "1) null returned by binding at " + module.getClass().getName() + ".configure(",
+          "but parameter 0 of " + module.getClass().getName() + ".fail() is not @Nullable",
+          "while locating java.lang.String",
+          "for parameter 0 at " + module.getClass().getName() + ".fail(",
+          "while locating java.lang.Integer");
+
+      assertEquals(1, expected.getErrorMessages().size());
+    }
+  }
+
+  private void validateNullableIgnored(Injector injector) {
+    injector.getInstance(Integer.class); // no exception
+  }
+
+  private void validateNullableWarns(Injector injector, Dependency<?> dependency) {
+    final List<LogRecord> logRecords = Lists.newArrayList();
+    final Handler fakeHandler = new Handler() {
+      @Override
+      public void publish(LogRecord logRecord) {
+        logRecords.add(logRecord);
+      }
+      @Override
+      public void flush() {}
+      @Override
+      public void close() throws SecurityException {}
+    };
+    Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler);
+    try {
+      injector.getInstance(Integer.class); // no exception, but assert it does log.
+      LogRecord record = Iterables.getOnlyElement(logRecords);
+      assertEquals(
+          "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
+              + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+              + " error.",
+          record.getMessage());
+      assertEquals(dependency.getParameterIndex(), record.getParameters()[0]);
+      assertEquals(Errors.convert(dependency.getInjectionPoint().getMember()),
+          record.getParameters()[1]);
+      assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[2]);
+    } finally {
+      Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler);
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface Nullable {}
 }