BEGIN_PUBLIC
Pull more info into ArgsListInfo.

This allows us to make queries such as "get me all the flags / files required for this specific action". This will allow us to implement cc_action_config more easily and efficiently.
END_PUBLIC

PiperOrigin-RevId: 609828504
Change-Id: Ie3978674c5027f892d2e5e4c8d937a52c59fde5d
diff --git a/cc/toolchains/args.bzl b/cc/toolchains/args.bzl
index e8c1c2b..3409d00 100644
--- a/cc/toolchains/args.bzl
+++ b/cc/toolchains/args.bzl
@@ -51,7 +51,15 @@
     )
     return [
         args,
-        ArgsListInfo(label = ctx.label, args = tuple([args])),
+        ArgsListInfo(
+            label = ctx.label,
+            args = tuple([args]),
+            files = files,
+            by_action = tuple([
+                struct(action = action, args = [args], files = files)
+                for action in actions.to_list()
+            ]),
+        ),
     ]
 
 cc_args = rule(
diff --git a/cc/toolchains/cc_toolchain_info.bzl b/cc/toolchains/cc_toolchain_info.bzl
index 5efdda9..c105364 100644
--- a/cc/toolchains/cc_toolchain_info.bzl
+++ b/cc/toolchains/cc_toolchain_info.bzl
@@ -73,6 +73,8 @@
     fields = {
         "label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
         "args": "(Sequence[ArgsInfo]) The flag sets contained within",
+        "files": "(depset[File]) The files required for all of the arguments",
+        "by_action": "(Sequence[struct(action=ActionTypeInfo, args=List[ArgsInfo], files=depset[Files])]) Relevant information about the args keyed by the action type.",
     },
 )
 
@@ -83,12 +85,12 @@
         "label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
         "name": "(str) The name of the feature",
         "enabled": "(bool) Whether this feature is enabled by default",
-        "args": "(Sequence[ArgsInfo]) Flag sets enabled by this feature",
+        "args": "(ArgsListInfo) Args enabled by this feature",
         "implies": "(depset[FeatureInfo]) Set of features implied by this feature",
         "requires_any_of": "(Sequence[FeatureSetInfo]) A list of feature sets, at least one of which is required to enable this feature. This is semantically equivalent to the requires attribute of rules_cc's FeatureInfo",
-        "provides": "(Sequence[MutuallyExclusiveCategoryInfo]) Indicates that this feature is one of several mutually exclusive alternate features.",
+        "mutually_exclusive": "(Sequence[MutuallyExclusiveCategoryInfo]) Indicates that this feature is one of several mutually exclusive alternate features.",
         "known": "(bool) Whether the feature is a known feature. Known features are assumed to be defined elsewhere.",
-        "overrides": "(Optional[FeatureInfo]) The feature that this overrides",
+        "overrides": "(Optional[FeatureInfo]) The feature that this overrides. Must be a known feature",
     },
 )
 FeatureSetInfo = provider(
diff --git a/tests/rule_based_toolchain/args/args_test.bzl b/tests/rule_based_toolchain/args/args_test.bzl
index 859cc6c..d4937b7 100644
--- a/tests/rule_based_toolchain/args/args_test.bzl
+++ b/tests/rule_based_toolchain/args/args_test.bzl
@@ -15,11 +15,19 @@
 
 load(
     "//cc/toolchains:cc_toolchain_info.bzl",
+    "ActionTypeInfo",
     "ArgsInfo",
+    "ArgsListInfo",
 )
 
 visibility("private")
 
+_SIMPLE_FILES = [
+    "tests/rule_based_toolchain/testdata/file1",
+    "tests/rule_based_toolchain/testdata/multiple1",
+    "tests/rule_based_toolchain/testdata/multiple2",
+]
+
 def _test_simple_args_impl(env, targets):
     simple = env.expect.that_target(targets.simple).provider(ArgsInfo)
     simple.actions().contains_exactly([
@@ -28,11 +36,13 @@
     ])
     simple.args().contains_exactly([targets.simple.label])
     simple.env().contains_exactly({"BAR": "bar"})
-    simple.files().contains_exactly([
-        "tests/rule_based_toolchain/testdata/file1",
-        "tests/rule_based_toolchain/testdata/multiple1",
-        "tests/rule_based_toolchain/testdata/multiple2",
-    ])
+    simple.files().contains_exactly(_SIMPLE_FILES)
+
+    c_compile = env.expect.that_target(targets.simple).provider(ArgsListInfo).by_action().get(
+        targets.c_compile[ActionTypeInfo],
+    )
+    c_compile.args().contains_exactly([targets.simple[ArgsInfo]])
+    c_compile.files().contains_exactly(_SIMPLE_FILES)
 
 TARGETS = [
     ":simple",
diff --git a/tests/rule_based_toolchain/generate_factory.bzl b/tests/rule_based_toolchain/generate_factory.bzl
index 72c9680..7697a4c 100644
--- a/tests/rule_based_toolchain/generate_factory.bzl
+++ b/tests/rule_based_toolchain/generate_factory.bzl
@@ -64,8 +64,9 @@
 
     def validate(*, value, meta):
         got_keys = sorted(structs.to_dict(value).keys())
-        if got_keys != want_keys:
-            meta.add_failure("Wanted a %s with keys %r, got %r" % (name, want_keys, got_keys), "")
+        subjects.collection(got_keys, meta = meta.derive(details = [
+            "Value was not a %s - it has a different set of fields" % name,
+        ])).contains_exactly(want_keys).in_order()
 
     def type_factory(value, *, meta):
         validate(value = value, meta = meta)
diff --git a/tests/rule_based_toolchain/generics.bzl b/tests/rule_based_toolchain/generics.bzl
index 2244b0b..4551f2e 100644
--- a/tests/rule_based_toolchain/generics.bzl
+++ b/tests/rule_based_toolchain/generics.bzl
@@ -120,3 +120,13 @@
     meta = meta,
     attrs = attrs,
 )
+
+# We can't do complex assertions on containers. This allows you to write
+# assert.that_value({"foo": 1), factory=dict_key_subject(subjects.int))
+#   .get("foo").equals(1)
+dict_key_subject = lambda factory: lambda value, *, meta: struct(
+    get = lambda key: factory(
+        value[key],
+        meta = meta.derive(".get({})".format(key)),
+    ),
+)
diff --git a/tests/rule_based_toolchain/subjects.bzl b/tests/rule_based_toolchain/subjects.bzl
index 5e5ca62..fbda2d9 100644
--- a/tests/rule_based_toolchain/subjects.bzl
+++ b/tests/rule_based_toolchain/subjects.bzl
@@ -23,6 +23,7 @@
     "ActionTypeSetInfo",
     "AddArgsInfo",
     "ArgsInfo",
+    "ArgsListInfo",
     "FeatureConstraintInfo",
     "FeatureInfo",
     "FeatureSetInfo",
@@ -30,7 +31,7 @@
     "ToolInfo",
 )
 load(":generate_factory.bzl", "ProviderDepset", "ProviderSequence", "generate_factory")
-load(":generics.bzl", "optional_subject", "result_subject", "struct_subject", _result_fn_wrapper = "result_fn_wrapper")
+load(":generics.bzl", "dict_key_subject", "optional_subject", "result_subject", "struct_subject", _result_fn_wrapper = "result_fn_wrapper")
 
 visibility("//tests/rule_based_toolchain/...")
 
@@ -66,10 +67,10 @@
 _FEATURE_FLAGS = dict(
     name = _subjects.str,
     enabled = _subjects.bool,
-    flag_sets = None,
+    args = None,
     implies = None,
     requires_any_of = None,
-    provides = ProviderSequence(_MutuallyExclusiveCategoryFactory),
+    mutually_exclusive = ProviderSequence(_MutuallyExclusiveCategoryFactory),
     known = _subjects.bool,
     overrides = None,
 )
@@ -123,13 +124,29 @@
 )
 
 # buildifier: disable=name-conventions
+_ArgsListFactory = generate_factory(
+    ArgsListInfo,
+    "ArgsListInfo",
+    dict(
+        args = ProviderSequence(_ArgsFactory),
+        by_action = lambda values, *, meta: dict_key_subject(struct_subject(
+            args = _subjects.collection,
+            files = _subjects.depset_file,
+        ))({value.action: value for value in values}, meta = meta),
+        files = _subjects.depset_file,
+    ),
+)
+
+# buildifier: disable=name-conventions
 _FeatureFactory = generate_factory(
     FeatureInfo,
     "FeatureInfo",
     _FEATURE_FLAGS | dict(
+        # Use .factory so it's not inlined.
+        args = _ArgsListFactory.factory,
         implies = ProviderDepset(_FakeFeatureFactory),
         requires_any_of = ProviderSequence(_FeatureSetFactory),
-        overrides = _FakeFeatureFactory,
+        overrides = optional_subject(_FakeFeatureFactory),
     ),
 )
 
@@ -153,7 +170,7 @@
         action = _ActionTypeFactory,
         enabled = _subjects.bool,
         tools = ProviderSequence(_ToolFactory),
-        flag_sets = ProviderSequence(_ArgsFactory),
+        args = ProviderSequence(_ArgsFactory),
         implies = ProviderDepset(_FeatureFactory),
         files = _subjects.depset_file,
     ),
@@ -185,6 +202,7 @@
     _ActionTypeSetFactory,
     _AddArgsFactory,
     _ArgsFactory,
+    _ArgsListFactory,
     _MutuallyExclusiveCategoryFactory,
     _FeatureFactory,
     _FeatureConstraintFactory,
@@ -201,5 +219,6 @@
         optional = optional_subject,
         struct = struct_subject,
         runfiles = runfiles_subject,
+        dict_key = dict_key_subject,
     ) | {factory.name: factory.factory for factory in FACTORIES})
 )