Add some SPI methods to allow users to annotate Module methods with arbitrary
bindings and have those methods bound as Providers to specialized Keys.

This is the basis of what will be used to allow
Multibinder/MapBinder/OptionalBinder to have stuff like @SetProvides,
@MapProvides, @OptionalProvides and dagger interop support.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=85361820
diff --git a/core/src/com/google/inject/internal/ProviderMethod.java b/core/src/com/google/inject/internal/ProviderMethod.java
index 1a952ed..beaf406 100644
--- a/core/src/com/google/inject/internal/ProviderMethod.java
+++ b/core/src/com/google/inject/internal/ProviderMethod.java
@@ -24,6 +24,7 @@
 import com.google.inject.Key;
 import com.google.inject.PrivateBinder;
 import com.google.inject.Provider;
+import com.google.inject.Provides;
 import com.google.inject.internal.BytecodeGen.Visibility;
 import com.google.inject.internal.util.StackTraceElements;
 import com.google.inject.spi.BindingTargetVisitor;
@@ -52,21 +53,28 @@
   /**
    * Creates a {@link ProviderMethod}.
    *
-   * <p>Unless {@code skipFastClassGeneration} is set, this will use {@link FastClass} to invoke
-   * the actual method, since it is significantly faster.  However, this will fail if the method is
-   * {@code private} or {@code protected}, since fastclass is subject to java access policies.
+   * <p>Unless {@code skipFastClassGeneration} is set, this will use
+   * {@link net.sf.cglib.reflect.FastClass} to invoke the actual method, since it is significantly
+   * faster. However, this will fail if the method is {@code private} or {@code protected}, since
+   * fastclass is subject to java access policies.
    */
   static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
       ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
-      Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration) {
+      Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration,
+      Annotation annotation) {
     int modifiers = method.getModifiers();
     /*if[AOP]*/
     if (!skipFastClassGeneration && !Modifier.isPrivate(modifiers)
         && !Modifier.isProtected(modifiers)) {
       try {
         // We use an index instead of FastMethod to save a stack frame.
-        return new FastClassProviderMethod<T>(
-            key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+        return new FastClassProviderMethod<T>(key,
+            method,
+            instance,
+            dependencies,
+            parameterProviders,
+            scopeAnnotation,
+            annotation);
       } catch (net.sf.cglib.core.CodeGenerationException e) {/* fall-through */}
     }
     /*end[AOP]*/
@@ -76,8 +84,13 @@
       method.setAccessible(true);
     }
 
-    return new ReflectionProviderMethod<T>(
-        key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+    return new ReflectionProviderMethod<T>(key,
+        method,
+        instance,
+        dependencies,
+        parameterProviders,
+        scopeAnnotation,
+        annotation);
   }
 
   protected final Object instance;
@@ -88,13 +101,14 @@
   private final ImmutableSet<Dependency<?>> dependencies;
   private final List<Provider<?>> parameterProviders;
   private final boolean exposed;
+  private final Annotation annotation;
 
   /**
    * @param method the method to invoke. It's return type must be the same type as {@code key}.
    */
   private ProviderMethod(Key<T> key, Method method, Object instance,
       ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
-      Class<? extends Annotation> scopeAnnotation) {
+      Class<? extends Annotation> scopeAnnotation, Annotation annotation) {
     this.key = key;
     this.scopeAnnotation = scopeAnnotation;
     this.instance = instance;
@@ -102,12 +116,15 @@
     this.method = method;
     this.parameterProviders = parameterProviders;
     this.exposed = method.isAnnotationPresent(Exposed.class);
+    this.annotation = annotation;
   }
 
+  @Override
   public Key<T> getKey() {
     return key;
   }
 
+  @Override
   public Method getMethod() {
     return method;
   }
@@ -117,9 +134,15 @@
     return instance;
   }
   
+  @Override
   public Object getEnclosingInstance() {
     return instance;
   }
+  
+  @Override
+  public Annotation getAnnotation() {
+    return annotation;
+  }
 
   public void configure(Binder binder) {
     binder = binder.withSource(method);
@@ -137,6 +160,7 @@
     }
   }
 
+  @Override
   public T get() {
     Object[] parameters = new Object[parameterProviders.size()];
     for (int i = 0; i < parameters.length; i++) {
@@ -158,10 +182,12 @@
   abstract Object doProvision(Object[] parameters)
       throws IllegalAccessException, InvocationTargetException;
 
+  @Override
   public Set<Dependency<?>> getDependencies() {
     return dependencies;
   }
   
+  @Override
   @SuppressWarnings("unchecked")
   public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
       ProviderInstanceBinding<? extends B> binding) {
@@ -172,15 +198,24 @@
   }
 
   @Override public String toString() {
-    return "@Provides " + StackTraceElements.forMember(method);
+    String annotationString = annotation.toString();
+    // Show @Provides w/o the com.google.inject prefix.
+    if (annotation.annotationType() == Provides.class) {
+      annotationString = "@Provides";
+    } else if (annotationString.endsWith("()")) {
+      // Remove the common "()" suffix if there are no values.
+      annotationString = annotationString.substring(0, annotationString.length() - 2);
+    }
+    return annotationString + " " + StackTraceElements.forMember(method);
   }
   
   @Override
   public boolean equals(Object obj) {
     if (obj instanceof ProviderMethod) {
-      ProviderMethod o = (ProviderMethod)obj;
+      ProviderMethod<?> o = (ProviderMethod<?>) obj;
       return method.equals(o.method)
-         && instance.equals(o.instance);
+         && instance.equals(o.instance)
+         && annotation.equals(o.annotation);
     } else {
       return false;
     }
@@ -191,7 +226,7 @@
     // Avoid calling hashCode on 'instance', which is a user-object
     // that might not be expecting it.
     // (We need to call equals, so we do.  But we can avoid hashCode.)
-    return Objects.hashCode(method);
+    return Objects.hashCode(method, annotation);
   }
 
   /*if[AOP]*/
@@ -208,8 +243,15 @@
         Object instance,
         ImmutableSet<Dependency<?>> dependencies,
         List<Provider<?>> parameterProviders,
-        Class<? extends Annotation> scopeAnnotation) {
-      super(key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+        Class<? extends Annotation> scopeAnnotation,
+        Annotation annotation) {
+      super(key,
+          method,
+          instance,
+          dependencies,
+          parameterProviders,
+          scopeAnnotation,
+          annotation);
       // We need to generate a FastClass for the method's class, not the object's class.
       this.fastClass =
           BytecodeGen.newFastClass(method.getDeclaringClass(), Visibility.forMember(method));
@@ -241,8 +283,15 @@
         Object instance,
         ImmutableSet<Dependency<?>> dependencies,
         List<Provider<?>> parameterProviders,
-        Class<? extends Annotation> scopeAnnotation) {
-      super(key, method, instance, dependencies, parameterProviders, scopeAnnotation);
+        Class<? extends Annotation> scopeAnnotation,
+        Annotation annotation) {
+      super(key,
+          method,
+          instance,
+          dependencies,
+          parameterProviders,
+          scopeAnnotation,
+          annotation);
     }
 
     @Override Object doProvision(Object[] parameters) throws IllegalAccessException,
diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java
index 77a8ff2..98eb45d 100644
--- a/core/src/com/google/inject/internal/ProviderMethodsModule.java
+++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.common.base.Optional;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -28,6 +29,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
+import com.google.inject.spi.ModuleAnnotatedMethodScanner;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.InjectionPoint;
 import com.google.inject.spi.Message;
@@ -39,6 +41,7 @@
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
@@ -49,21 +52,45 @@
  */
 public final class ProviderMethodsModule implements Module {
 
+  private static ModuleAnnotatedMethodScanner PROVIDES_BUILDER =
+      new ModuleAnnotatedMethodScanner() {
+        @Override
+        public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
+            InjectionPoint injectionPoint) {
+          return key;
+        }
+
+        @Override
+        public Set<? extends Class<? extends Annotation>> annotationClasses() {
+          return ImmutableSet.of(Provides.class);
+        }
+      };
+
   private final Object delegate;
   private final TypeLiteral<?> typeLiteral;
   private final boolean skipFastClassGeneration;
+  private final ModuleAnnotatedMethodScanner scanner;
 
-  private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration) {
+  private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration,
+      ModuleAnnotatedMethodScanner scanner) {
     this.delegate = checkNotNull(delegate, "delegate");
     this.typeLiteral = TypeLiteral.get(this.delegate.getClass());
     this.skipFastClassGeneration = skipFastClassGeneration;
+    this.scanner = scanner;
   }
 
   /**
    * Returns a module which creates bindings for provider methods from the given module.
    */
   public static Module forModule(Module module) {
-    return forObject(module, false);
+    return forObject(module, false, PROVIDES_BUILDER);
+  }
+
+  /**
+   * Returns a module which creates bindings methods in the module that match the scanner.
+   */
+  public static Module forModule(Module module, ModuleAnnotatedMethodScanner scanner) {
+    return forObject(module, false, scanner);
   }
 
   /**
@@ -74,18 +101,20 @@
    * are only interested in Module metadata.
    */
   public static Module forObject(Object object) {
-    return forObject(object, true);
+    return forObject(object, true, PROVIDES_BUILDER);
   }
 
-  private static Module forObject(Object object, boolean skipFastClassGeneration) {
+  private static Module forObject(Object object, boolean skipFastClassGeneration,
+      ModuleAnnotatedMethodScanner scanner) {
     // avoid infinite recursion, since installing a module always installs itself
     if (object instanceof ProviderMethodsModule) {
       return Modules.EMPTY_MODULE;
     }
 
-    return new ProviderMethodsModule(object, skipFastClassGeneration);
+    return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
   }
 
+  @Override
   public synchronized void configure(Binder binder) {
     for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
       providerMethod.configure(binder);
@@ -106,8 +135,9 @@
             && !method.isBridge() && !method.isSynthetic()) {
           methodsBySignature.put(new Signature(method), method);
         }
-        if (isProvider(method)) {
-          result.add(createProviderMethod(binder, method));
+        Optional<Annotation> annotation = isProvider(binder, method);
+        if (annotation.isPresent()) {
+          result.add(createProviderMethod(binder, method, annotation.get()));
         }
       }
     }
@@ -124,9 +154,11 @@
         }
         // now we know matching signature is in a subtype of method.getDeclaringClass()
         if (overrides(matchingSignature, method)) {
+          String annotationString = provider.getAnnotation().annotationType() == Provides.class
+              ? "@Provides" : "@" + provider.getAnnotation().annotationType().getCanonicalName();
           binder.addError(
-              "Overriding @Provides methods is not allowed."
-                  + "\n\t@Provides method: %s\n\toverridden by: %s",
+              "Overriding " + annotationString + " methods is not allowed."
+                  + "\n\t" + annotationString + " method: %s\n\toverridden by: %s",
               method,
               matchingSignature);
           break;
@@ -142,10 +174,24 @@
    * Synthetic bridge methods are excluded. Starting with JDK 8, javac copies annotations onto
    * bridge methods (which always have erased signatures).
    */
-  private static boolean isProvider(Method method) {
-    return !method.isBridge()
-        && !method.isSynthetic()
-        && method.isAnnotationPresent(Provides.class);
+  private Optional<Annotation> isProvider(Binder binder, Method method) {
+    if (method.isBridge() || method.isSynthetic()) {
+      return Optional.absent();
+    }
+    Annotation annotation = null;
+    for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
+      Annotation foundAnnotation = method.getAnnotation(annotationClass);
+      if (foundAnnotation != null) {
+        if (annotation != null) {
+          binder.addError("More than one annotation claimed by %s on method %s."
+              + " Methods can only have one annotation claimed per scanner.",
+              scanner, method);
+          return Optional.absent();
+        }
+        annotation = foundAnnotation;
+      }
+    }
+    return Optional.fromNullable(annotation);
   }
 
   private final class Signature {
@@ -196,7 +242,8 @@
     return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
   }
 
-  private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method) {
+  private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method,
+      Annotation annotation) {
     binder = binder.withSource(method);
     Errors errors = new Errors(method);
 
@@ -211,13 +258,14 @@
     @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());
+    key = scanner.prepareMethod(binder, annotation, key, point);
     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);
+        parameterProviders, scopeAnnotation, skipFastClassGeneration, annotation);
   }
 
   <T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
diff --git a/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java b/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java
new file mode 100644
index 0000000..99b6d91
--- /dev/null
+++ b/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.spi;
+
+import com.google.inject.Binder;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.internal.ProviderMethodsModule;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Allows extensions to scan modules for annotated methods and bind those methods
+ * as providers, similar to {@code @Provides} methods.
+ */
+public abstract class ModuleAnnotatedMethodScanner {
+  
+  /**
+   * Scans the module for methods and returns a module that will bind the methods
+   * that match this scanner.
+   */
+  public final Module forModule(Module module) {
+    return ProviderMethodsModule.forModule(module, this);
+  }
+
+  /**
+   * Returns the annotations this should scan for. Every method in the module that has one of these
+   * annotations will create a Provider binding, with the return value of the binding being what's
+   * provided and the parameters of the method being dependencies of the provider.
+   */
+  public abstract Set<? extends Class<? extends Annotation>> annotationClasses();
+
+  /**
+   * Prepares a method for binding. This {@code key} parameter is the key discovered from looking at
+   * the binding annotation and return value of the method. Implementations can modify the key to
+   * instead bind to another key. For example, Multibinder may want to change
+   * {@code @SetProvides String provideFoo()} to bind into a unique Key within the multibinder
+   * instead of binding {@code String}.
+   *
+   * <p>The injection point and annotation are provided in case the implementation wants to set the
+   * key based on the property of the annotation or if any additional preparation is needed for any
+   * of the dependencies. The annotation is guaranteed to be an instance of one the classes returned
+   * by {@link #annotationClasses}.
+   */
+  public abstract <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
+      InjectionPoint injectionPoint);
+
+}
diff --git a/core/src/com/google/inject/spi/ProvidesMethodBinding.java b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
index 010e936..a862fcc 100644
--- a/core/src/com/google/inject/spi/ProvidesMethodBinding.java
+++ b/core/src/com/google/inject/spi/ProvidesMethodBinding.java
@@ -19,11 +19,13 @@
 import com.google.inject.Key;
 import com.google.inject.Provides;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 
 /**
- * An {@literal @}{@link Provides} binding.
- * 
+ * An {@literal @}{@link Provides} binding or binding produced by a
+ * {@link ModuleAnnotatedMethodScanner}.
+ *
  * @since 4.0
  * @author sameb@google.com (Sam Berlin)
  */
@@ -37,4 +39,12 @@
   
   /** Returns the key of the binding. */
   Key<T> getKey();
+
+  /**
+   * Returns the annotation that caused this binding to be created. For {@code @Provides} methods,
+   * this is an instance of the {@code @Provides} annotation. For bindings from
+   * {@link ModuleAnnotatedMethodScanner}, this is the annotation that caused the scanner to produce
+   * the binding.
+   */
+  Annotation getAnnotation();
 }
diff --git a/core/test/com/google/inject/AllTests.java b/core/test/com/google/inject/AllTests.java
index e7ca9da..f700608 100644
--- a/core/test/com/google/inject/AllTests.java
+++ b/core/test/com/google/inject/AllTests.java
@@ -31,6 +31,7 @@
 import com.google.inject.spi.HasDependenciesTest;
 import com.google.inject.spi.InjectionPointTest;
 import com.google.inject.spi.InjectorSpiTest;
+import com.google.inject.spi.ModuleAnnotatedMethodScannerTest;
 import com.google.inject.spi.ModuleRewriterTest;
 import com.google.inject.spi.ModuleSourceTest;
 import com.google.inject.spi.ProviderMethodsTest;
@@ -86,6 +87,7 @@
     suite.addTestSuite(MembersInjectorTest.class);
     suite.addTestSuite(ModulesTest.class);
     suite.addTestSuite(ModuleTest.class);
+    suite.addTestSuite(ModuleAnnotatedMethodScannerTest.class);
     suite.addTestSuite(NullableInjectionPointTest.class);
     suite.addTestSuite(OptionalBindingTest.class);
     suite.addTestSuite(OverrideModuleTest.class);
diff --git a/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java b/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java
new file mode 100644
index 0000000..62f8220
--- /dev/null
+++ b/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.spi;
+
+import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.name.Names.named;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+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.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.internal.util.StackTraceElements;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Set;
+
+/** Tests for {@link ModuleAnnotatedMethodScanner} usage. */
+public class ModuleAnnotatedMethodScannerTest extends TestCase {
+  
+  public void testScanning() throws Exception {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        install(new NamedMunger().forModule(this));
+      }
+      
+      @TestProvides @Named("foo") String foo() {
+        return "foo";
+      }
+      
+      @TestProvides @Named("foo2") String foo2() {
+        return "foo2";
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+
+    // assert no bindings named "foo" or "foo2" exist -- they were munged.
+    assertNull(injector.getExistingBinding(Key.get(String.class, named("foo"))));
+    assertNull(injector.getExistingBinding(Key.get(String.class, named("foo2"))));
+
+    Binding<String> fooBinding = injector.getBinding(Key.get(String.class, named("foo-munged")));
+    Binding<String> foo2Binding = injector.getBinding(Key.get(String.class, named("foo2-munged")));
+    assertEquals("foo", fooBinding.getProvider().get());
+    assertEquals("foo2", foo2Binding.getProvider().get());
+    
+    // Validate the provider has a sane toString
+    assertEquals(methodName(TestProvides.class, "foo", module),
+        fooBinding.getProvider().toString());
+    assertEquals(methodName(TestProvides.class, "foo2", module),
+        foo2Binding.getProvider().toString());
+  }
+
+  public void testMoreThanOneClaimedAnnotationFails() throws Exception {
+    final NamedMunger scanner = new NamedMunger();
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        install(scanner.forModule(this));
+      }
+      
+      @TestProvides @TestProvides2 String foo() {
+        return "foo";
+      }
+    };
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch(CreationException expected) {
+      assertEquals(1, expected.getErrorMessages().size());
+      assertContains(expected.getMessage(),
+          "More than one annotation claimed by " + scanner + " on method "
+              + module.getClass().getName() + ".foo(). Methods can only have "
+              + "one annotation claimed per scanner.");
+    }
+  }
+  
+  private String methodName(Class<? extends Annotation> annotation, String method, Object container)
+      throws Exception {
+    return "@" + annotation.getName() + " "
+        + StackTraceElements.forMember(container.getClass().getDeclaredMethod(method));
+  }
+  
+  @Documented @Target(METHOD) @Retention(RUNTIME)
+  private @interface TestProvides {}
+
+  @Documented @Target(METHOD) @Retention(RUNTIME)
+  private @interface TestProvides2 {}
+  
+  private static class NamedMunger extends ModuleAnnotatedMethodScanner {
+    @Override
+    public Set<? extends Class<? extends Annotation>> annotationClasses() {
+      return ImmutableSet.of(TestProvides.class, TestProvides2.class);
+    }
+
+    @Override
+    public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
+        InjectionPoint injectionPoint) {
+      return Key.get(key.getTypeLiteral(),
+          Names.named(((Named) key.getAnnotation()).value() + "-munged"));
+    }
+  }
+}