Introduce a builder in `RequiredConfigFragmentsProvider` that accepts various configuration fragment types.

Right now the builder just serves as a common utility to convert these types to strings. Eventually we will want to store the objects in a more structured way - introducing the builder interface now allows for a more gradual refactoring.

PiperOrigin-RevId: 391126263
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index b9bf9f1..669d6d3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -980,8 +980,11 @@
     srcs = ["RequiredConfigFragmentsProvider.java"],
     deps = [
         ":config/fragment",
+        ":config/fragment_options",
         ":transitive_info_provider",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
         "//src/main/java/com/google/devtools/build/lib/concurrent",
+        "//src/main/java/com/google/devtools/build/lib/util",
         "//third_party:guava",
     ],
 )
@@ -1888,8 +1891,8 @@
     deps = [
         ":config/build_options",
         ":config/fragment_options",
+        ":required_config_fragments_provider",
         "//src/main/java/com/google/devtools/build/lib/events",
-        "//src/main/java/com/google/devtools/build/lib/util",
         "//third_party:guava",
     ],
 )
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RequiredConfigFragmentsProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/RequiredConfigFragmentsProvider.java
index 91fbc6f..54b1c8b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RequiredConfigFragmentsProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RequiredConfigFragmentsProvider.java
@@ -17,8 +17,13 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.analysis.config.Fragment;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.util.ClassName;
 import java.util.List;
 
 /**
@@ -29,7 +34,7 @@
  * <p>See {@link com.google.devtools.build.lib.analysis.config.RequiredFragmentsUtil} for details.
  */
 @Immutable
-public class RequiredConfigFragmentsProvider implements TransitiveInfoProvider {
+public final class RequiredConfigFragmentsProvider implements TransitiveInfoProvider {
   private final ImmutableSet<String> requiredConfigFragments;
 
   public RequiredConfigFragmentsProvider(ImmutableSet<String> requiredConfigFragments) {
@@ -47,10 +52,67 @@
     if (providers.size() == 1) {
       return providers.get(0);
     }
-    ImmutableSet.Builder<String> merged = ImmutableSet.builder();
+    RequiredConfigFragmentsProvider.Builder merged = builder();
     for (RequiredConfigFragmentsProvider provider : providers) {
-      merged.addAll(provider.requiredConfigFragments);
+      merged.merge(provider);
     }
     return new RequiredConfigFragmentsProvider(merged.build());
   }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  /** Builder for required config fragments. */
+  // TODO(b/149094955): Have this build a structured version of RequiredConfigFragmentsProvider.
+  public static final class Builder {
+    private final ImmutableSortedSet.Builder<String> strings = ImmutableSortedSet.naturalOrder();
+
+    private Builder() {}
+
+    // TODO(b/149094955): This should never accept arbitrary strings.
+    public Builder addAll(Iterable<String> strings) {
+      this.strings.addAll(strings);
+      return this;
+    }
+
+    public Builder addOptionsClass(Class<? extends FragmentOptions> optionsClass) {
+      strings.add(ClassName.getSimpleNameWithOuter(optionsClass));
+      return this;
+    }
+
+    public Builder addOptionsClasses(Iterable<Class<? extends FragmentOptions>> optionsClasses) {
+      return addClasses(optionsClasses);
+    }
+
+    public Builder addFragmentClasses(Iterable<Class<? extends Fragment>> fragmentClasses) {
+      return addClasses(fragmentClasses);
+    }
+
+    private Builder addClasses(Iterable<? extends Class<?>> classes) {
+      return addAll(Iterables.transform(classes, ClassName::getSimpleNameWithOuter));
+    }
+
+    public Builder addDefine(String define) {
+      strings.add("--define:" + define);
+      return this;
+    }
+
+    public Builder addStarlarkOption(Label starlarkOption) {
+      return addStarlarkOption(starlarkOption.toString());
+    }
+
+    public Builder addStarlarkOption(String starlarkOption) {
+      strings.add(starlarkOption);
+      return this;
+    }
+
+    public Builder merge(RequiredConfigFragmentsProvider provider) {
+      return addAll(provider.requiredConfigFragments);
+    }
+
+    public ImmutableSortedSet<String> build() {
+      return strings.build();
+    }
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/RequiredFragmentsUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/config/RequiredFragmentsUtil.java
index f4d70f2..e9fe9d9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/RequiredFragmentsUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/RequiredFragmentsUtil.java
@@ -14,9 +14,9 @@
 
 package com.google.devtools.build.lib.analysis.config;
 
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.devtools.build.lib.analysis.BuildSettingProvider;
 import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
@@ -33,10 +33,8 @@
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.RuleTransitionData;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
-import com.google.devtools.build.lib.util.ClassName;
 import java.util.Collection;
 import java.util.Optional;
-import java.util.TreeSet;
 
 /**
  * Utility methods for determining what {@link Fragment}s are required to analyze targets.
@@ -152,38 +150,31 @@
         == CoreOptions.IncludeConfigFragmentsEnum.OFF) {
       return ImmutableSortedSet.of();
     }
-    ImmutableSortedSet.Builder<String> requiredFragments = ImmutableSortedSet.naturalOrder();
     // Add directly required fragments:
-
-    // Fragments explicitly required by the native target/aspect definition API:
-    configurationFragmentPolicy
-        .getRequiredConfigurationFragments()
-        .forEach(fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment)));
-    // Fragments explicitly required by the Starlark target/aspect definition API:
-    configurationFragmentPolicy
-        .getRequiredStarlarkFragments()
-        .forEach(
-            starlarkName ->
-                requiredFragments.add(
-                    ClassName.getSimpleNameWithOuter(
-                        configuration.getStarlarkFragmentByName(starlarkName))));
-    // Fragments universally required by everything:
-    universallyRequiredFragments.forEach(
-        fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment)));
+    RequiredConfigFragmentsProvider.Builder requiredFragments =
+        RequiredConfigFragmentsProvider.builder()
+            // Fragments explicitly required by the native target/aspect definition API:
+            .addFragmentClasses(configurationFragmentPolicy.getRequiredConfigurationFragments())
+            // Fragments explicitly required by the Starlark target/aspect definition API:
+            .addFragmentClasses(
+                Collections2.transform(
+                    configurationFragmentPolicy.getRequiredStarlarkFragments(),
+                    configuration::getStarlarkFragmentByName))
+            // Fragments universally required by everything:
+            .addFragmentClasses(universallyRequiredFragments);
     // Fragments required by attached select()s.
     configConditions.forEach(
         configCondition -> requiredFragments.addAll(configCondition.requiredFragmentOptions()));
     // We consider build settings (which are both targets and configuration) to require themselves:
-    if (buildSettingLabel.isPresent()) {
-      requiredFragments.add(buildSettingLabel.get().toString());
-    }
+    buildSettingLabel.ifPresent(requiredFragments::addStarlarkOption);
+
     // Fragments required by attached configuration transitions.
     for (ConfigurationTransition transition : associatedTransitions) {
       requiredFragments.addAll(transition.requiresOptionFragments(configuration.getOptions()));
     }
 
     // Optionally add transitively required fragments (only if --show_config_fragments=transitive):
-    requiredFragments.addAll(getRequiredFragmentsFromDeps(configuration, prerequisites));
+    addRequiredFragmentsFromDeps(requiredFragments, configuration, prerequisites);
     return requiredFragments.build();
   }
 
@@ -256,23 +247,18 @@
    *       the rule.
    * </ul>
    */
-  private static ImmutableSet<String> getRequiredFragmentsFromDeps(
-      BuildConfiguration configuration, Iterable<ConfiguredTargetAndData> prerequisites) {
-
-    TreeSet<String> requiredFragments = new TreeSet<>();
+  private static void addRequiredFragmentsFromDeps(
+      RequiredConfigFragmentsProvider.Builder requiredFragments,
+      BuildConfiguration configuration,
+      Iterable<ConfiguredTargetAndData> prerequisites) {
     CoreOptions coreOptions = configuration.getOptions().get(CoreOptions.class);
-    if (coreOptions.includeRequiredConfigFragmentsProvider
-        == CoreOptions.IncludeConfigFragmentsEnum.OFF) {
-      return ImmutableSet.of();
-    }
-
     for (ConfiguredTargetAndData prereq : prerequisites) {
       // If the target depends on a Starlark build setting, conceptually that means it directly
       // requires that as an option (even though it's technically a dependency).
       BuildSettingProvider buildSettingProvider =
           prereq.getConfiguredTarget().getProvider(BuildSettingProvider.class);
       if (buildSettingProvider != null) {
-        requiredFragments.add(buildSettingProvider.getLabel().toString());
+        requiredFragments.addStarlarkOption(buildSettingProvider.getLabel());
       }
       if (coreOptions.includeRequiredConfigFragmentsProvider
           == CoreOptions.IncludeConfigFragmentsEnum.TRANSITIVE) {
@@ -280,12 +266,10 @@
         RequiredConfigFragmentsProvider depProvider =
             prereq.getConfiguredTarget().getProvider(RequiredConfigFragmentsProvider.class);
         if (depProvider != null) {
-          requiredFragments.addAll(depProvider.getRequiredConfigFragments());
+          requiredFragments.merge(depProvider);
         }
       }
     }
-
-    return ImmutableSet.copyOf(requiredFragments);
   }
 
   private RequiredFragmentsUtil() {}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/transitions/ConfigurationTransition.java b/src/main/java/com/google/devtools/build/lib/analysis/config/transitions/ConfigurationTransition.java
index 6811845..cb9aba7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/transitions/ConfigurationTransition.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/transitions/ConfigurationTransition.java
@@ -14,14 +14,12 @@
 
 package com.google.devtools.build.lib.analysis.config.transitions;
 
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-
 import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.BuildOptionsView;
 import com.google.devtools.build.lib.analysis.config.FragmentOptions;
 import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.util.ClassName;
 import java.util.Map;
 
 /**
@@ -57,9 +55,9 @@
    * <p>Callers may ignore this method if they know they're not calling into a Starlark transition.
    */
   default ImmutableSet<String> requiresOptionFragments(BuildOptions options) {
-    return requiresOptionFragments().stream()
-        .map(ClassName::getSimpleNameWithOuter)
-        .collect(toImmutableSet());
+    return RequiredConfigFragmentsProvider.builder()
+        .addOptionsClasses(requiresOptionFragments())
+        .build();
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkTransition.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkTransition.java
index 3822d0c..5169a22 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkTransition.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkTransition.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition;
 import com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition.Settings;
@@ -37,7 +38,6 @@
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.packages.Type.ConversionException;
 import com.google.devtools.build.lib.skyframe.PackageValue;
-import com.google.devtools.build.lib.util.ClassName;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
@@ -85,22 +85,22 @@
   public ImmutableSet<String> requiresOptionFragments(BuildOptions buildOptions) {
     // TODO(bazel-team): complexity cleanup: merge buildOptionInfo with TransitiveOptionDetails.
     Map<String, OptionInfo> optionToFragment = FunctionTransitionUtil.buildOptionInfo(buildOptions);
-    ImmutableSet.Builder<String> fragments = ImmutableSet.builder();
+    RequiredConfigFragmentsProvider.Builder requiredFragments =
+        RequiredConfigFragmentsProvider.builder();
     for (String optionStarlarkName : Iterables.concat(getInputs(), getOutputs())) {
       if (!optionStarlarkName.startsWith(COMMAND_LINE_OPTION_PREFIX)) {
-        // Starlark flags don't belong to any fragment.
-        fragments.add(optionStarlarkName);
+        requiredFragments.addStarlarkOption(optionStarlarkName);
       } else {
         String optionNativeName = optionStarlarkName.substring(COMMAND_LINE_OPTION_PREFIX.length());
         OptionInfo optionInfo = optionToFragment.get(optionNativeName);
         // A null optionInfo means the flag is invalid. Starlark transitions independently catch and
         // report that (search the code for "do not correspond to valid settings").
         if (optionInfo != null) {
-          fragments.add(ClassName.getSimpleNameWithOuter(optionInfo.getOptionClass()));
+          requiredFragments.addOptionsClass(optionInfo.getOptionClass());
         }
       }
     }
-    return fragments.build();
+    return requiredFragments.build();
   }
 
   /** Exception class for exceptions thrown during application of a starlark-defined transition */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/BUILD b/src/main/java/com/google/devtools/build/lib/rules/config/BUILD
index 3aced6e..1a37be8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/BUILD
@@ -34,7 +34,6 @@
         "//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options",
-        "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_provider",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/invalid_configuration_exception",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_defined_config_transition",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/no_transition",
@@ -44,8 +43,8 @@
         "//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
         "//src/main/java/com/google/devtools/build/lib/analysis:file_provider",
         "//src/main/java/com/google/devtools/build/lib/analysis:platform_configuration",
+        "//src/main/java/com/google/devtools/build/lib/analysis:required_config_fragments_provider",
         "//src/main/java/com/google/devtools/build/lib/analysis:rule_definition_environment",
-        "//src/main/java/com/google/devtools/build/lib/analysis:starlark/function_transition_util",
         "//src/main/java/com/google/devtools/build/lib/analysis:starlark/starlark_config",
         "//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_collection",
         "//src/main/java/com/google/devtools/build/lib/analysis/platform",
@@ -55,19 +54,16 @@
         "//src/main/java/com/google/devtools/build/lib/concurrent",
         "//src/main/java/com/google/devtools/build/lib/events",
         "//src/main/java/com/google/devtools/build/lib/packages",
-        "//src/main/java/com/google/devtools/build/lib/packages/semantics",
         "//src/main/java/com/google/devtools/build/lib/rules:core_rules",
         "//src/main/java/com/google/devtools/build/lib/rules/platform",
         "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
         "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
         "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/config",
-        "//src/main/java/com/google/devtools/build/lib/util",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
         "//src/main/java/com/google/devtools/common/options",
         "//src/main/java/net/starlark/java/annot",
         "//src/main/java/net/starlark/java/eval",
         "//src/main/java/net/starlark/java/syntax",
-        "//third_party:auto_value",
         "//third_party:guava",
         "//third_party:jsr305",
     ],
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
index ac6b3b8..7fae70c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
@@ -35,6 +35,7 @@
 import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.FilesToRunProvider;
 import com.google.devtools.build.lib.analysis.LicensesProviderImpl;
+import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
 import com.google.devtools.build.lib.analysis.RuleContext;
@@ -58,7 +59,6 @@
 import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.rules.config.ConfigRuleClasses.ConfigSettingRule;
-import com.google.devtools.build.lib.util.ClassName;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.common.options.OptionsParser;
 import com.google.devtools.common.options.OptionsParsingException;
@@ -67,6 +67,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.Nullable;
 
 /**
  * Implementation for the config_setting rule.
@@ -112,15 +113,18 @@
 
     TransitiveOptionDetails optionDetails =
         BuildConfigurationOptionDetails.get(ruleContext.getConfiguration());
-    ImmutableSet.Builder<String> requiredFragmentOptions = ImmutableSet.builder();
+
+    RequiredConfigFragmentsProvider.Builder requiredFragments =
+        ruleContext.shouldIncludeRequiredConfigFragmentsProvider()
+            ? RequiredConfigFragmentsProvider.builder()
+            : null;
 
     boolean nativeFlagsMatch =
-        matchesConfig(
-            nativeFlagSettings.entries(), optionDetails, requiredFragmentOptions, ruleContext);
+        matchesConfig(nativeFlagSettings.entries(), optionDetails, requiredFragments, ruleContext);
 
     UserDefinedFlagMatch userDefinedFlags =
         UserDefinedFlagMatch.fromAttributeValueAndPrerequisites(
-            userDefinedFlagSettings, optionDetails, requiredFragmentOptions, ruleContext);
+            userDefinedFlagSettings, optionDetails, requiredFragments, ruleContext);
 
     boolean constraintValuesMatch = constraintValuesMatch(ruleContext);
 
@@ -133,9 +137,7 @@
             ruleContext.getLabel(),
             nativeFlagSettings,
             userDefinedFlags.getSpecifiedFlagValues(),
-            ruleContext.shouldIncludeRequiredConfigFragmentsProvider()
-                ? requiredFragmentOptions.build()
-                : ImmutableSet.of(),
+            requiredFragments != null ? requiredFragments.build() : ImmutableSet.of(),
             nativeFlagsMatch && userDefinedFlags.matches() && constraintValuesMatch);
 
     return new RuleConfiguredTargetBuilder(ruleContext)
@@ -253,7 +255,7 @@
   private static boolean matchesConfig(
       Collection<Map.Entry<String, String>> expectedSettings,
       TransitiveOptionDetails options,
-      ImmutableSet.Builder<String> requiredFragmentOptions,
+      @Nullable RequiredConfigFragmentsProvider.Builder requiredFragments,
       RuleContext ruleContext) {
     // Rather than returning fast when we find a mismatch, continue looking at the other flags
     // to check they're indeed valid flag specifications.
@@ -272,23 +274,22 @@
         continue;
       }
 
-      if (optionName.equals("define")) {
-        // --define is more like user-defined build flags than traditional native flags. Report it
-        // like user-defined flags: the dependency is directly on the flag vs. the fragment that
-        // contains the flag. This frees a rule that depends on "--define a=1" from preserving
-        // another rule's dependency on "--define b=2". In other words, if both rules simply said
-        // "I require CoreOptions" (which is the FragmentOptions --define belongs to), that would
-        // hide the reality that they really have orthogonal dependencies: removing
-        // "--define b=2" is perfectly safe for the rule that needs "--define a=1".
-        int equalsIndex = expectedRawValue.indexOf('=');
-        requiredFragmentOptions.add(
-            "--define:"
-                + (equalsIndex > 0
-                    ? expectedRawValue.substring(0, equalsIndex)
-                    : expectedRawValue));
-      } else {
-        // For other native flags, it's reasonable to report the fragment they belong to.
-        requiredFragmentOptions.add(ClassName.getSimpleNameWithOuter(optionClass));
+      if (requiredFragments != null) {
+        if (optionName.equals("define")) {
+          // --define is more like user-defined build flags than traditional native flags. Report it
+          // like user-defined flags: the dependency is directly on the flag vs. the fragment that
+          // contains the flag. This frees a rule that depends on "--define a=1" from preserving
+          // another rule's dependency on "--define b=2". In other words, if both rules simply said
+          // "I require CoreOptions" (which is the FragmentOptions --define belongs to), that would
+          // hide the reality that they really have orthogonal dependencies: removing
+          // "--define b=2" is perfectly safe for the rule that needs "--define a=1".
+          int equalsIndex = expectedRawValue.indexOf('=');
+          requiredFragments.addDefine(
+              equalsIndex > 0 ? expectedRawValue.substring(0, equalsIndex) : expectedRawValue);
+        } else {
+          // For other native flags, it's reasonable to report the fragment they belong to.
+          requiredFragments.addOptionsClass(optionClass);
+        }
       }
 
       SelectRestriction selectRestriction = options.getSelectRestriction(optionName);
@@ -426,14 +427,14 @@
      * @param attributeValue map of user-defined flag labels to their values as set in the
      *     'flag_values' attribute
      * @param optionDetails information about the configuration to match against
-     * @param requiredFragmentOptions set of config fragments this config_setting requires. This
-     *     method adds feature flag and Starlark-defined setting requirements to this set.
+     * @param requiredFragments builder to which this method adds feature flags and Starlark-defined
+     *     settings required by this config_setting
      * @param ruleContext this rule's RuleContext
      */
     static UserDefinedFlagMatch fromAttributeValueAndPrerequisites(
         Map<Label, String> attributeValue,
         TransitiveOptionDetails optionDetails,
-        ImmutableSet.Builder<String> requiredFragmentOptions,
+        @Nullable RequiredConfigFragmentsProvider.Builder requiredFragments,
         RuleContext ruleContext) {
       Map<Label, String> specifiedFlagValues = new LinkedHashMap<>();
       boolean matches = true;
@@ -455,7 +456,9 @@
 
         if (target.satisfies(ConfigFeatureFlagProvider.REQUIRE_CONFIG_FEATURE_FLAG_PROVIDER)) {
           // config_feature_flag
-          requiredFragmentOptions.add(target.getLabel().toString());
+          if (requiredFragments != null) {
+            requiredFragments.addStarlarkOption(target.getLabel());
+          }
           ConfigFeatureFlagProvider provider = ConfigFeatureFlagProvider.fromTarget(target);
           if (!provider.isValidValue(specifiedValue)) {
             ruleContext.attributeError(
@@ -472,7 +475,9 @@
           }
         } else if (target.satisfies(BuildSettingProvider.REQUIRE_BUILD_SETTING_PROVIDER)) {
           // build setting
-          requiredFragmentOptions.add(target.getLabel().toString());
+          if (requiredFragments != null) {
+            requiredFragments.addStarlarkOption(target.getLabel());
+          }
           BuildSettingProvider provider = target.getProvider(BuildSettingProvider.class);
           Object configurationValue =
               optionDetails.getOptionValue(specifiedLabel) != null