Allow aspects to specify multiple sets of required providers to match against rules. Aspects can attach to a rule if at least one set of required providers are present on the rule.

--
MOS_MIGRATED_REVID=140605023
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
index 635b39a..9fadc28 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
@@ -537,11 +537,22 @@
         extractAspectCandidates(aspects, attributeAndOwner.attribute, originalRule);
     RuleClass ruleClass = ((Rule) target).getRuleClassObject();
     ImmutableSet.Builder<AspectDescriptor> result = ImmutableSet.builder();
+
     for (Aspect aspectCandidate : aspectCandidates) {
-      if (ruleClass.canHaveAnyProvider() || Sets.difference(
-              aspectCandidate.getDefinition().getRequiredProviders(),
-              ruleClass.getAdvertisedProviders())
-          .isEmpty()) {
+      boolean requireAspect = ruleClass.canHaveAnyProvider();
+
+      if (!requireAspect) {
+        ImmutableList<ImmutableSet<Class<?>>> providersList =
+            aspectCandidate.getDefinition().getRequiredProviders();
+        for (ImmutableSet<Class<?>> providers : providersList) {
+          if (Sets.difference(providers, ruleClass.getAdvertisedProviders()).isEmpty()) {
+            requireAspect = true;
+            break;
+          }
+        }
+      }
+
+      if (requireAspect) {
         result.add(
             new AspectDescriptor(
                 aspectCandidate.getAspectClass(),
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
index f3186d7..bda14e9 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
@@ -29,7 +29,6 @@
 import com.google.devtools.build.lib.util.Preconditions;
 import java.util.Collection;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,8 +55,8 @@
 public final class AspectDefinition {
 
   private final AspectClass aspectClass;
-  private final ImmutableSet<Class<?>> requiredProviders;
-  private final ImmutableSet<String> requiredProviderNames;
+  private final ImmutableList<ImmutableSet<Class<?>>> requiredProviderSets;
+  private final ImmutableList<ImmutableSet<String>> requiredProviderNameSets;
   private final ImmutableMap<String, Attribute> attributes;
   private final PropagationFunction attributeAspects;
   @Nullable private final ConfigurationFragmentPolicy configurationFragmentPolicy;
@@ -68,16 +67,22 @@
 
   private AspectDefinition(
       AspectClass aspectClass,
-      ImmutableSet<Class<?>> requiredProviders,
+      ImmutableList<ImmutableSet<Class<?>>> requiredProviderSets,
       ImmutableMap<String, Attribute> attributes,
       PropagationFunction attributeAspects,
       @Nullable ConfigurationFragmentPolicy configurationFragmentPolicy) {
     this.aspectClass = aspectClass;
-    this.requiredProviders = requiredProviders;
-    this.requiredProviderNames = toStringSet(requiredProviders);
+    this.requiredProviderSets = requiredProviderSets;
     this.attributes = attributes;
     this.attributeAspects = attributeAspects;
     this.configurationFragmentPolicy = configurationFragmentPolicy;
+
+    ImmutableList.Builder<ImmutableSet<String>> requiredProviderNameSetsBuilder =
+        new ImmutableList.Builder<>();
+    for (ImmutableSet<Class<?>> requiredProviderSet : requiredProviderSets) {
+      requiredProviderNameSetsBuilder.add(toStringSet(requiredProviderSet));
+    }
+    this.requiredProviderNameSets = requiredProviderNameSetsBuilder.build();
   }
 
   public String getName() {
@@ -98,8 +103,9 @@
   }
 
   /**
-   * Returns the set of {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider}
-   * instances that must be present on a configured target so that this aspect can be applied to it.
+   * Returns the list of {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider}
+   * sets. All required providers from at least one set must be present on a configured target so
+   * that this aspect can be applied to it.
    *
    * <p>We cannot refer to that class here due to our dependency structure, so this returns a set
    * of unconstrained class objects.
@@ -107,14 +113,15 @@
    * <p>If a configured target does not have a required provider, the aspect is silently not created
    * for it.
    */
-  public ImmutableSet<Class<?>> getRequiredProviders() {
-    return requiredProviders;
+  public ImmutableList<ImmutableSet<Class<?>>> getRequiredProviders() {
+    return requiredProviderSets;
   }
 
   /**
-   * Returns the set of class names of
-   * {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider} instances that must be
-   * present on a configured target so that this aspect can be applied to it.
+   * Returns the list of class name sets of
+   * {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider}. All required providers
+   * from at least one set must be present on a configured target so that this aspect can be applied
+   * to it.
    *
    * <p>This set is a mirror of the set returned by {@link #getRequiredProviders}, but contains the
    * names of the classes rather than the class objects themselves.
@@ -122,8 +129,8 @@
    * <p>If a configured target does not have a required provider, the aspect is silently not created
    * for it.
    */
-  public ImmutableSet<String> getRequiredProviderNames() {
-    return requiredProviderNames;
+  public ImmutableList<ImmutableSet<String>> getRequiredProviderNames() {
+    return requiredProviderNameSets;
   }
 
   /**
@@ -166,11 +173,19 @@
     for (Aspect candidateClass : attribute.getAspects(from)) {
       // Check if target satisfies condition for this aspect (has to provide all required
       // TransitiveInfoProviders)
-      if (!canHaveAnyProvider && !advertisedProviders.containsAll(
-          candidateClass.getDefinition().getRequiredProviderNames())) {
-        continue;
+      if (!canHaveAnyProvider) {
+        ImmutableList<ImmutableSet<String>> providerNamesList =
+            candidateClass.getDefinition().getRequiredProviderNames();
+
+        for (ImmutableSet<String> providerNames : providerNamesList) {
+          if (advertisedProviders.containsAll(providerNames)) {
+            addAllAttributesOfAspect(from, result, candidateClass, dependencyFilter);
+            break;
+          }
+        }
+      } else {
+        addAllAttributesOfAspect(from, result, candidateClass, dependencyFilter);
       }
-      addAllAttributesOfAspect(from, result, candidateClass, dependencyFilter);
     }
     return ImmutableMultimap.copyOf(result);
   }
@@ -228,7 +243,7 @@
   public static final class Builder {
     private final AspectClass aspectClass;
     private final Map<String, Attribute> attributes = new LinkedHashMap<>();
-    private final Set<Class<?>> requiredProviders = new LinkedHashSet<>();
+    private ImmutableList<ImmutableSet<Class<?>>> requiredProviderSets = ImmutableList.of();
     private final Multimap<String, AspectClass> attributeAspects = LinkedHashMultimap.create();
     private ImmutableCollection<AspectClass> allAttributesAspects = null;
     private final ConfigurationFragmentPolicy.Builder configurationFragmentPolicy =
@@ -239,10 +254,25 @@
     }
 
     /**
-     * Asserts that this aspect can only be evaluated for rules that supply the specified provider.
+     * Asserts that this aspect can only be evaluated for rules that supply all of the providers
+     * from at least one set of required providers.
      */
-    public Builder requireProvider(Class<?> requiredProvider) {
-      this.requiredProviders.add(requiredProvider);
+    public Builder requireProviderSets(Iterable<? extends Set<Class<?>>> providerSets) {
+      ImmutableList.Builder<ImmutableSet<Class<?>>> requiredProviderSetsBuilder =
+          ImmutableList.builder();
+      for (Iterable<Class<?>> providerSet : providerSets) {
+        requiredProviderSetsBuilder.add(ImmutableSet.copyOf(providerSet));
+      }
+      requiredProviderSets = requiredProviderSetsBuilder.build();
+      return this;
+    }
+
+    /**
+     * Asserts that this aspect can only be evaluated for rules that supply all of the specified
+     * providers.
+     */
+    public Builder requireProviders(Class<?>... requiredProviders) {
+      requireProviderSets(ImmutableList.of(ImmutableSet.copyOf(requiredProviders)));
       return this;
     }
 
@@ -412,7 +442,15 @@
      * <p>The builder object is reusable afterwards.
      */
     public AspectDefinition build() {
-      return new AspectDefinition(aspectClass, ImmutableSet.copyOf(requiredProviders),
+      // If there is no required provider set, we still need to at least provide one empty set of
+      // providers. We consider this case specially because aspects with no required providers
+      // should match all rules, and having an empty set faciliates the matching logic.
+      ImmutableList<ImmutableSet<Class<?>>> requiredProviders =
+          requiredProviderSets.isEmpty()
+          ? ImmutableList.of(ImmutableSet.<Class<?>>of())
+          : requiredProviderSets;
+
+      return new AspectDefinition(aspectClass, ImmutableList.copyOf(requiredProviders),
           ImmutableMap.copyOf(attributes),
           allAttributesAspects != null
               ? new AllAttributesPropagationFunction(allAttributesAspects)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java
index e743a92..b7c6c59 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java
@@ -83,7 +83,7 @@
     }
 
     return builder
-        .requireProvider(JavaCompilationArgsProvider.class)
+        .requireProviders(JavaCompilationArgsProvider.class)
         .requiresConfigurationFragments()
         .build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
index eec142c..a4d8960 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
@@ -112,7 +112,7 @@
   public AspectDefinition getDefinition(AspectParameters params) {
     AspectDefinition.Builder result = new AspectDefinition.Builder(this)
         // Actually we care about JavaRuntimeJarProvider, but rules don't advertise that provider.
-        .requireProvider(JavaCompilationArgsProvider.class)
+        .requireProviders(JavaCompilationArgsProvider.class)
         // Parse labels since we don't have RuleDefinitionEnvironment.getLabel like in a rule
         .add(attr(ASPECT_DESUGAR_PREREQ, LABEL).cfg(HOST).exec()
             .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/android:desugar_java8")))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
index 4df83e7..c6a25ee 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
@@ -61,7 +61,7 @@
     }
 
     return new AspectDefinition.Builder(this)
-        .requireProvider(JavaSourceInfoProvider.class)
+        .requireProviders(JavaSourceInfoProvider.class)
         .add(attr(":android_sdk", LABEL)
               .allowedRuleClasses("android_sdk")
               .value(new AndroidSdkLabel(androidSdk)))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaLiteProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaLiteProtoAspect.java
index e8fa346..43d8256 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaLiteProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaLiteProtoAspect.java
@@ -113,7 +113,7 @@
         new AspectDefinition.Builder(this)
             .attributeAspect("deps", this)
             .requiresConfigurationFragments(JavaConfiguration.class, ProtoConfiguration.class)
-            .requireProvider(ProtoSourcesProvider.class)
+            .requireProviders(ProtoSourcesProvider.class)
             .add(
                 attr(PROTO_TOOLCHAIN_ATTR, LABEL)
                     .mandatoryNativeProviders(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
index 5c0b292..d2e194e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/proto/JavaProtoAspect.java
@@ -161,7 +161,7 @@
         new AspectDefinition.Builder(this)
             .attributeAspect("deps", this)
             .requiresConfigurationFragments(JavaConfiguration.class, ProtoConfiguration.class)
-            .requireProvider(ProtoSourcesProvider.class)
+            .requireProviders(ProtoSourcesProvider.class)
             .add(
                 attr(SPEED_PROTO_RUNTIME_ATTR, LABEL)
                     .legacyAllowAnyFileType()
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AbstractJ2ObjcProtoAspect.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AbstractJ2ObjcProtoAspect.java
index a6a8a1e..3dcfb6f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AbstractJ2ObjcProtoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AbstractJ2ObjcProtoAspect.java
@@ -83,7 +83,7 @@
   @Override
   public AspectDefinition getDefinition(AspectParameters aspectParameters) {
     AspectDefinition.Builder builder = new AspectDefinition.Builder(this)
-        .requireProvider(ProtoSourcesProvider.class)
+        .requireProviders(ProtoSourcesProvider.class)
         .requiresConfigurationFragments(
             AppleConfiguration.class,
             J2ObjcConfiguration.class,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
index 8679970..0f1bc80 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java
@@ -110,8 +110,7 @@
         .attributeAspect("deps", this, j2ObjcProtoAspect)
         .attributeAspect("exports", this, j2ObjcProtoAspect)
         .attributeAspect("runtime_deps", this, j2ObjcProtoAspect)
-        .requireProvider(JavaSourceInfoProvider.class)
-        .requireProvider(JavaCompilationArgsProvider.class)
+        .requireProviders(JavaSourceInfoProvider.class, JavaCompilationArgsProvider.class)
         .requiresConfigurationFragments(
             AppleConfiguration.class, J2ObjcConfiguration.class, ObjcConfiguration.class)
         .requiresHostConfigurationFragments(Jvm.class)
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index 38db025..df1b7c9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -20,6 +20,7 @@
 import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.LinkedListMultimap;
@@ -844,12 +845,22 @@
 
   private static boolean aspectMatchesConfiguredTarget(ConfiguredTarget dep, Aspect aspect) {
     AspectDefinition aspectDefinition = aspect.getDefinition();
-    for (Class<?> provider : aspectDefinition.getRequiredProviders()) {
-      if (dep.getProvider(provider.asSubclass(TransitiveInfoProvider.class)) == null) {
-        return false;
+    ImmutableList<ImmutableSet<Class<?>>> providersList = aspectDefinition.getRequiredProviders();
+
+    for (ImmutableSet<Class<?>> providers : providersList) {
+      boolean matched = true;
+      for (Class<?> provider : providers) {
+        if (dep.getProvider(provider.asSubclass(TransitiveInfoProvider.class)) == null) {
+          matched = false;
+          break;
+        }
+      }
+
+      if (matched) {
+        return true;
       }
     }
-    return true;
+    return false;
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java
index eb6b8f8..6edae5e 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectDefinitionTest.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.packages.AspectDefinition;
 import com.google.devtools.build.lib.packages.AspectParameters;
@@ -150,15 +151,36 @@
   @Test
   public void testRequireProvider_AddsToSetOfRequiredProvidersAndNames() throws Exception {
     AspectDefinition requiresProviders = new AspectDefinition.Builder(TEST_ASPECT_CLASS)
-        .requireProvider(String.class)
-        .requireProvider(Integer.class)
+        .requireProviders(String.class, Integer.class)
         .build();
-    assertThat(requiresProviders.getRequiredProviders())
+    assertThat(requiresProviders.getRequiredProviders()).hasSize(1);
+    assertThat(requiresProviders.getRequiredProviders().get(0))
         .containsExactly(String.class, Integer.class);
-    assertThat(requiresProviders.getRequiredProviderNames())
+    assertThat(requiresProviders.getRequiredProviderNames()).hasSize(1);
+    assertThat(requiresProviders.getRequiredProviderNames().get(0))
         .containsExactly("java.lang.String", "java.lang.Integer");
   }
 
+ @Test
+  public void testRequireProvider_AddsTwoSetsOfRequiredProvidersAndNames() throws Exception {
+    AspectDefinition requiresProviders = new AspectDefinition.Builder(TEST_ASPECT_CLASS)
+        .requireProviderSets(
+            ImmutableList.of(
+                ImmutableSet.<Class<?>>of(String.class, Integer.class),
+                ImmutableSet.<Class<?>>of(Boolean.class)))
+        .build();
+    assertThat(requiresProviders.getRequiredProviders()).hasSize(2);
+    assertThat(requiresProviders.getRequiredProviders().get(0))
+        .containsExactly(String.class, Integer.class);
+    assertThat(requiresProviders.getRequiredProviders().get(1))
+        .containsExactly(Boolean.class);
+    assertThat(requiresProviders.getRequiredProviderNames()).hasSize(2);
+    assertThat(requiresProviders.getRequiredProviderNames().get(0))
+        .containsExactly("java.lang.String", "java.lang.Integer");
+    assertThat(requiresProviders.getRequiredProviderNames().get(1))
+        .containsExactly("java.lang.Boolean");
+  }
+
   @Test
   public void testNoConfigurationFragmentPolicySetup_HasNonNullPolicy() throws Exception {
     AspectDefinition noPolicy = new AspectDefinition.Builder(TEST_ASPECT_CLASS)
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
index 37abaaf..ae4f533 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
@@ -163,6 +163,21 @@
   }
 
   @Test
+  public void aspectCreatedIfAtLeastOneSetOfAdvertisedProvidersArePresent() throws Exception {
+    setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
+        new TestAspects.HonestRule2(), new TestAspects.AspectRequiringProviderSetsRule());
+
+    pkg("a",
+        "aspect_requiring_provider_sets(name='a', foo=[':b', ':c'])",
+        "honest(name='b', foo=[])",
+        "honest2(name='c', foo=[])");
+
+    ConfiguredTarget a = getConfiguredTarget("//a:a");
+    assertThat(a.getProvider(RuleInfo.class).getData())
+        .containsExactly("rule //a:a", "aspect //a:b", "aspect //a:c");
+  }
+
+  @Test
   public void aspectWithParametrizedDefinition() throws Exception {
     setRulesAvailableInTests(
         new TestAspects.BaseRule(),
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
index 55b0fe8..22a9412 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
@@ -24,6 +24,8 @@
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.ConfiguredAspect;
@@ -117,6 +119,14 @@
   public static final class RequiredProvider implements TransitiveInfoProvider {
   }
 
+  /**
+   * Another very simple provider used in tests that check whether the logic that attaches aspects
+   * depending on whether a configured target has a provider works or not.
+   */
+  @Immutable
+  public static final class RequiredProvider2 implements TransitiveInfoProvider {
+  }
+
   private static NestedSet<String> collectAspectData(String me, RuleContext ruleContext) {
     NestedSetBuilder<String> result = new NestedSetBuilder<>(Order.STABLE_ORDER);
     result.add(me);
@@ -160,6 +170,24 @@
   }
 
   /**
+   * A simple rule configured target factory that exports provider {@link RequiredProvider2}.
+   */
+  public static class DummyRuleFactory2 implements RuleConfiguredTargetFactory {
+    @Override
+    public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException {
+      return new RuleConfiguredTargetBuilder(ruleContext)
+              .addProvider(
+                  new RuleInfo(collectAspectData("rule " + ruleContext.getLabel(), ruleContext)))
+              .setFilesToBuild(NestedSetBuilder.<Artifact>create(Order.STABLE_ORDER))
+              .setRunfilesSupport(null, null)
+              .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY))
+              .addProvider(new RequiredProvider())
+              .addProvider(new RequiredProvider2())
+              .build();
+    }
+  }
+
+  /**
    * A base class for mock aspects to reduce boilerplate.
    */
   public abstract static class BaseAspect extends NativeAspectClass
@@ -203,7 +231,7 @@
   private static final AspectDefinition EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER_DEFINITION =
       new AspectDefinition.Builder(EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER)
           .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra")))
-          .requireProvider(RequiredProvider.class)
+          .requireProviders(RequiredProvider.class)
           .build();
 
   /**
@@ -286,6 +314,17 @@
   }
 
   /**
+   * An aspect that requires provider sets {{@link RequiredProvider}} and
+   * {{@link RequiredProvider2}}.
+   */
+  public static class AspectRequiringProviderSets extends BaseAspect {
+    @Override
+    public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+      return ASPECT_REQUIRING_PROVIDER_SETS_DEFINITION;
+    }
+  }
+
+  /**
    * An aspect that has a definition depending on parameters provided by originating rule.
    */
   public static class ParametrizedDefinitionAspect extends NativeAspectClass
@@ -334,9 +373,18 @@
 
   private static final AspectRequiringProvider ASPECT_REQUIRING_PROVIDER =
       new AspectRequiringProvider();
+  private static final AspectRequiringProviderSets ASPECT_REQUIRING_PROVIDER_SETS =
+      new AspectRequiringProviderSets();
   private static final AspectDefinition ASPECT_REQUIRING_PROVIDER_DEFINITION =
       new AspectDefinition.Builder(ASPECT_REQUIRING_PROVIDER)
-          .requireProvider(RequiredProvider.class)
+          .requireProviders(RequiredProvider.class)
+          .build();
+  private static final AspectDefinition ASPECT_REQUIRING_PROVIDER_SETS_DEFINITION =
+      new AspectDefinition.Builder(ASPECT_REQUIRING_PROVIDER_SETS)
+          .requireProviderSets(
+              ImmutableList.of(
+                  ImmutableSet.<Class<?>>of(RequiredProvider.class),
+                  ImmutableSet.<Class<?>>of(RequiredProvider2.class)))
           .build();
 
   /**
@@ -486,6 +534,30 @@
   }
 
   /**
+   * A rule that defines an {@link AspectRequiringProviderSets} on one of its attributes.
+   */
+  public static class AspectRequiringProviderSetsRule implements RuleDefinition {
+
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
+              .aspect(ASPECT_REQUIRING_PROVIDER_SETS))
+          .add(attr("baz", STRING))
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("aspect_requiring_provider_sets")
+          .factoryClass(DummyRuleFactory.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+  /**
    * A rule that defines an {@link ExtraAttributeAspect} on one of its attributes.
    */
   public static class ExtraAttributeAspectRule implements RuleDefinition {
@@ -731,6 +803,29 @@
   }
 
   /**
+   * A rule that advertises another, different provider and implements it.
+   */
+  public static class HonestRule2 implements RuleDefinition {
+    @Override
+    public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+      return builder
+          .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE))
+          .advertiseProvider(RequiredProvider2.class)
+          .build();
+    }
+
+    @Override
+    public Metadata getMetadata() {
+      return RuleDefinition.Metadata.builder()
+          .name("honest2")
+          .factoryClass(DummyRuleFactory2.class)
+          .ancestors(BaseRule.class)
+          .build();
+    }
+  }
+
+
+  /**
    * Rule with an implcit dependency.
    */
   public static class ImplicitDepRule implements RuleDefinition {