$ cquery --show_config_fragments: include Starlark-declared requirements

Supports https://github.com/bazelbuild/bazel/issues/10613.

PiperOrigin-RevId: 293647011
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index d9647af..c85ebe2 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -289,7 +289,8 @@
    * @param configuration the configuration for this rule
    * @param universallyRequiredFragments fragments that are always required even if not explicitly
    *     specified for this rule
-   * @param directlyRequiredFragments fragments directly required by this rule's definition
+   * @param configurationFragmentPolicy source of truth for the fragments required by this rule's
+   *     rule class
    * @param configConditions {@link FragmentOptions} required by {@code select}s on this rule. This
    *     is a different type than the others: options and fragments are different concepts. There's
    *     some subtlety to their relationship (e.g. a {@link FragmentOptions} can be associated with
@@ -304,7 +305,7 @@
       Rule rule,
       BuildConfiguration configuration,
       Collection<Class<? extends BuildConfiguration.Fragment>> universallyRequiredFragments,
-      Collection<Class<?>> directlyRequiredFragments,
+      ConfigurationFragmentPolicy configurationFragmentPolicy,
       Collection<ConfigMatchingProvider> configConditions,
       Iterable<ConfiguredTargetAndData> prerequisites) {
     TreeSet<String> requiredFragments = new TreeSet<>();
@@ -317,9 +318,19 @@
 
     // Add directly required fragments:
 
-    // Fragments explicitly required by this rule:
-    directlyRequiredFragments.forEach(
-        fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment)));
+    // Fragments explicitly required by this rule via the native rule definition API:
+    configurationFragmentPolicy
+        .getRequiredConfigurationFragments()
+        .forEach(fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment)));
+    // Fragments explicitly required by this rule via the Starlark rule definition API:
+    configurationFragmentPolicy
+        .getRequiredStarlarkFragments()
+        .forEach(
+            starlarkName -> {
+              requiredFragments.add(
+                  ClassName.getSimpleNameWithOuter(
+                      configuration.getSkylarkFragmentByName(starlarkName)));
+            });
     // Fragments universally required by all rules:
     universallyRequiredFragments.forEach(
         fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment)));
@@ -392,7 +403,7 @@
                     rule,
                     configuration,
                     ruleClassProvider.getUniversalFragments(),
-                    configurationFragmentPolicy.getRequiredConfigurationFragments(),
+                    configurationFragmentPolicy,
                     configConditions.values(),
                     prerequisiteMap.values()))
             .build();
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ConfigurationFragmentPolicy.java b/src/main/java/com/google/devtools/build/lib/packages/ConfigurationFragmentPolicy.java
index 773210a..7dd4437 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ConfigurationFragmentPolicy.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ConfigurationFragmentPolicy.java
@@ -71,11 +71,14 @@
     private final SetMultimap<ConfigurationTransition, Class<?>> requiredConfigurationFragments
         = LinkedHashMultimap.create();
     /**
-     * Sets of configuration fragment names required by this rule, a set for each configuration.
-     * Duplicate entries will automatically be ignored by the SetMultimap.
+     * Sets of configuration fragments required by this rule, as defined by their Starlark names
+     * (see {@link SkylarkModule}, a set for each configuration.
+     *
+     * <p>Duplicate entries will automatically be ignored by the SetMultimap.
      */
-    private final SetMultimap<ConfigurationTransition, String> requiredConfigurationFragmentNames
-        = LinkedHashMultimap.create();
+    private final SetMultimap<ConfigurationTransition, String>
+        starlarkRequiredConfigurationFragments = LinkedHashMultimap.create();
+
     private MissingFragmentPolicy missingFragmentPolicy = MissingFragmentPolicy.FAIL_ANALYSIS;
 
     /**
@@ -107,12 +110,11 @@
     }
 
     /**
-     * Declares that the implementation of the associated rule class requires the given
-     * fragments to be present in this rule's target configuration only.
+     * Declares that the implementation of the associated rule class requires the given fragments to
+     * be present in this rule's target configuration only.
      *
      * <p>In contrast to {@link #requiresConfigurationFragments(Collection)}, this method takes the
-     * names of fragments (as determined by {@link SkylarkModule.Resolver}) instead of their
-     * classes.
+     * names of fragments (as determined by {@link SkylarkModule}) instead of their classes.
      *
      * <p>The value is inherited by subclasses.
      */
@@ -126,13 +128,12 @@
 
     /**
      * Declares the configuration fragments that are required by this rule for the specified
-     * configuration. Valid transition values are HOST for the host configuration and NONE for
-     * the target configuration.
+     * configuration. Valid transition values are HOST for the host configuration and NONE for the
+     * target configuration.
      *
      * <p>In contrast to {@link #requiresConfigurationFragments(ConfigurationTransition,
-     * Collection)}, this method takes the names of fragments (as determined by
-     * {@link SkylarkModule.Resolver}) instead of their
-     * classes.
+     * Collection)}, this method takes the names of fragments (as determined by {@link
+     * SkylarkModule}) instead of their classes.
      */
     public Builder requiresConfigurationFragmentsBySkylarkModuleName(
         ConfigurationTransition transition, Collection<String> configurationFragmentNames) {
@@ -140,7 +141,7 @@
       // than its own configuration. So we don't want to casually proliferate this pattern.
       Preconditions.checkArgument(
           transition == NoTransition.INSTANCE || transition.isHostTransition());
-      requiredConfigurationFragmentNames.putAll(transition, configurationFragmentNames);
+      starlarkRequiredConfigurationFragments.putAll(transition, configurationFragmentNames);
       return this;
     }
 
@@ -151,7 +152,7 @@
      */
     public Builder includeConfigurationFragmentsFrom(ConfigurationFragmentPolicy other) {
       requiredConfigurationFragments.putAll(other.requiredConfigurationFragments);
-      requiredConfigurationFragmentNames.putAll(other.requiredConfigurationFragmentNames);
+      starlarkRequiredConfigurationFragments.putAll(other.starlarkRequiredConfigurationFragments);
       return this;
     }
 
@@ -167,7 +168,7 @@
     public ConfigurationFragmentPolicy build() {
       return new ConfigurationFragmentPolicy(
           ImmutableSetMultimap.copyOf(requiredConfigurationFragments),
-          ImmutableSetMultimap.copyOf(requiredConfigurationFragmentNames),
+          ImmutableSetMultimap.copyOf(starlarkRequiredConfigurationFragments),
           missingFragmentPolicy);
     }
   }
@@ -184,7 +185,7 @@
    * configuration) to lists of Skylark module names of required configuration fragments.
    */
   private final ImmutableSetMultimap<ConfigurationTransition, String>
-      requiredConfigurationFragmentNames;
+      starlarkRequiredConfigurationFragments;
 
   /**
    * What to do during analysis if a configuration fragment is missing.
@@ -194,10 +195,10 @@
   @AutoCodec.VisibleForSerialization
   ConfigurationFragmentPolicy(
       ImmutableSetMultimap<ConfigurationTransition, Class<?>> requiredConfigurationFragments,
-      ImmutableSetMultimap<ConfigurationTransition, String> requiredConfigurationFragmentNames,
+      ImmutableSetMultimap<ConfigurationTransition, String> starlarkRequiredConfigurationFragments,
       MissingFragmentPolicy missingFragmentPolicy) {
     this.requiredConfigurationFragments = requiredConfigurationFragments;
-    this.requiredConfigurationFragmentNames = requiredConfigurationFragmentNames;
+    this.starlarkRequiredConfigurationFragments = starlarkRequiredConfigurationFragments;
     this.missingFragmentPolicy = missingFragmentPolicy;
   }
 
@@ -210,6 +211,17 @@
   }
 
   /**
+   * Returns the fragments required by Starlark definitions (e.g. <code>fragments = ["cpp"]</code>
+   * with the naming form seen in the Starlark API.
+   *
+   * <p>{@link
+   * com.google.devtools.build.lib.analysis.config.BuildConfiguration#getSkylarkFragmentByName} can
+   * be used to convert this to Java fragment instances.
+   */
+  public Collection<String> getRequiredStarlarkFragments() {
+    return starlarkRequiredConfigurationFragments.values();
+  }
+  /**
    * Checks if the configuration fragment may be accessed (i.e., if it's declared) in the specified
    * configuration (target or host).
    *
@@ -240,8 +252,7 @@
     SkylarkModule fragmentModule = SkylarkInterfaceUtils.getSkylarkModule(configurationFragment);
 
     return fragmentModule != null
-        ? requiredConfigurationFragmentNames.containsEntry(transition, fragmentModule.name())
-        : false;
+        && starlarkRequiredConfigurationFragments.containsEntry(transition, fragmentModule.name());
   }
 
   /**
@@ -252,8 +263,7 @@
     SkylarkModule fragmentModule = SkylarkInterfaceUtils.getSkylarkModule(configurationFragment);
 
     return fragmentModule != null
-        ? requiredConfigurationFragmentNames.containsValue(fragmentModule.name())
-        : false;
+        && starlarkRequiredConfigurationFragments.containsValue(fragmentModule.name());
   }
 
   /**
diff --git a/src/test/shell/integration/configured_query_test.sh b/src/test/shell/integration/configured_query_test.sh
index f52143b..018143c 100755
--- a/src/test/shell/integration/configured_query_test.sh
+++ b/src/test/shell/integration/configured_query_test.sh
@@ -217,6 +217,12 @@
 EOF
 }
 
+# TODO(gregce): --show_config_fragments and RequiredConfigFragmentsProvider
+# (the native Java code that powers --show_config_fragments) were originally
+# conceived as two pieces of the same functionality. But the former is just
+# a provider, which means it can be consumed by other logic. Consider moving
+# these tests out of cquery and into proper Java integration tests.
+
 function test_show_transitive_config_fragments() {
   local -r pkg=$FUNCNAME
   mkdir -p $pkg
@@ -618,6 +624,31 @@
   assert_not_contains "//$pkg:cclib_with_select .*--define:b" output
 }
 
+function test_show_config_fragments_on_starlark_required_fragments() {
+  local -r pkg=$FUNCNAME
+  mkdir -p $pkg
+  cat > $pkg/defs.bzl <<EOF
+def _impl(ctx):
+  pass
+
+java_requiring_rule = rule(
+  implementation = _impl,
+  fragments = ["java"],
+  attrs = {}
+)
+EOF
+  cat > $pkg/BUILD <<EOF
+load("//$pkg:defs.bzl", "java_requiring_rule")
+java_requiring_rule(
+  name = "buildme"
+)
+EOF
+
+  bazel cquery "//$pkg:all" --show_config_fragments=direct \
+    > output 2>"$TEST_log" || fail "Expected success"
+  assert_contains "//$pkg:buildme .*JavaConfiguration" output
+}
+
 function test_manual_tagged_targets_always_included_for_queries() {
   local -r pkg=$FUNCNAME
   mkdir -p $pkg