[tint][ast][fuzz] Replace Options DI with Context

Instead of dependency-injecting `fuzz::wgsl::Options` to the TINT_WGSL_PROGRAM_FUZZER() function, wrap this in a new `Context` structure, which holds the `Options` along with a new set of `ProgramProperties`.
This holds useful information about the `Program` that transforms can use (like skip).

Bug: 339704100
Change-Id: I66542b1a719281eb16a17aeb2f569a7fb66c0d45
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/189120
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/cmd/fuzz/ir/fuzz.cc b/src/tint/cmd/fuzz/ir/fuzz.cc
index 771a37a..b726fb7 100644
--- a/src/tint/cmd/fuzz/ir/fuzz.cc
+++ b/src/tint/cmd/fuzz/ir/fuzz.cc
@@ -64,7 +64,7 @@
 void Register(const IRFuzzer& fuzzer) {
     wgsl::Register({
         fuzzer.name,
-        [fn = fuzzer.fn](const Program& program, const fuzz::wgsl::Options& options,
+        [fn = fuzzer.fn](const Program& program, const fuzz::wgsl::Context& context,
                          Slice<const std::byte> data) {
             if (program.AST().Enables().Any(IsUnsupported)) {
                 return;
diff --git a/src/tint/cmd/fuzz/wgsl/fuzz.cc b/src/tint/cmd/fuzz/wgsl/fuzz.cc
index 17c2e1c..20de4f5 100644
--- a/src/tint/cmd/fuzz/wgsl/fuzz.cc
+++ b/src/tint/cmd/fuzz/wgsl/fuzz.cc
@@ -28,14 +28,24 @@
 #include "src/tint/cmd/fuzz/wgsl/fuzz.h"
 
 #include <iostream>
+#include <string>
+#include <string_view>
 #include <thread>
 
+#include "src/tint/lang/core/builtin_type.h"
+#include "src/tint/lang/wgsl/ast/alias.h"
+#include "src/tint/lang/wgsl/ast/function.h"
+#include "src/tint/lang/wgsl/ast/identifier.h"
+#include "src/tint/lang/wgsl/ast/struct.h"
+#include "src/tint/lang/wgsl/ast/variable.h"
+#include "src/tint/lang/wgsl/builtin_fn.h"
 #include "src/tint/lang/wgsl/common/allowed_features.h"
 #include "src/tint/lang/wgsl/reader/options.h"
 #include "src/tint/lang/wgsl/reader/reader.h"
 #include "src/tint/utils/containers/vector.h"
 #include "src/tint/utils/macros/defer.h"
 #include "src/tint/utils/macros/static_init.h"
+#include "src/tint/utils/rtti/switch.h"
 
 #if TINT_BUILD_WGSL_WRITER
 #include "src/tint/lang/wgsl/program/program.h"
@@ -61,6 +71,42 @@
     __builtin_trap();
 }
 
+bool IsBuiltinFn(std::string_view name) {
+    return tint::wgsl::ParseBuiltinFn(name) != tint::wgsl::BuiltinFn::kNone;
+}
+
+bool IsBuiltinType(std::string_view name) {
+    return tint::core::ParseBuiltinType(name) != tint::core::BuiltinType::kUndefined;
+}
+
+/// Scans @p program for patterns, returning a set of ProgramProperties.
+EnumSet<ProgramProperties> ScanProgramProperties(const Program& program) {
+    EnumSet<ProgramProperties> out;
+    auto check = [&](std::string_view name) {
+        if (IsBuiltinFn(name)) {
+            out.Add(ProgramProperties::kBuiltinFnsShadowed);
+        }
+        if (IsBuiltinType(name)) {
+            out.Add(ProgramProperties::kBuiltinTypesShadowed);
+        }
+    };
+
+    for (auto* node : program.ASTNodes().Objects()) {
+        tint::Switch(
+            node,  //
+            [&](const ast::Variable* variable) { check(variable->name->symbol.NameView()); },
+            [&](const ast::Function* fn) { check(fn->name->symbol.NameView()); },
+            [&](const ast::Struct* str) { check(str->name->symbol.NameView()); },
+            [&](const ast::Alias* alias) { check(alias->name->symbol.NameView()); });
+
+        if (out.Contains(ProgramProperties::kBuiltinFnsShadowed) &&
+            out.Contains(ProgramProperties::kBuiltinTypesShadowed)) {
+            break;  // Early exit - nothing more to find.
+        }
+    }
+    return out;
+}
+
 }  // namespace
 
 void Register(const ProgramFuzzer& fuzzer) {
@@ -100,6 +146,10 @@
         return;
     }
 
+    Context context;
+    context.options = options;
+    context.program_properties = ScanProgramProperties(program);
+
     // Run each of the program fuzzer functions
     if (options.run_concurrently) {
         size_t n = Fuzzers().Length();
@@ -110,13 +160,13 @@
                 Fuzzers()[i].name.find(options.filter) == std::string::npos) {
                 continue;
             }
-            threads.Push(std::thread([i, &program, &data, &options] {
+            threads.Push(std::thread([i, &program, &data, &context] {
                 auto& fuzzer = Fuzzers()[i];
                 currently_running = fuzzer.name;
-                if (options.verbose) {
+                if (context.options.verbose) {
                     std::cout << " • [" << i << "] Running: " << currently_running << std::endl;
                 }
-                fuzzer.fn(program, options, data);
+                fuzzer.fn(program, context, data);
             }));
         }
         for (auto& thread : threads) {
@@ -133,7 +183,7 @@
             if (options.verbose) {
                 std::cout << " • Running: " << currently_running << std::endl;
             }
-            fuzzer.fn(program, options, data);
+            fuzzer.fn(program, context, data);
         }
     }
 }
diff --git a/src/tint/cmd/fuzz/wgsl/fuzz.h b/src/tint/cmd/fuzz/wgsl/fuzz.h
index d128695..a24514b 100644
--- a/src/tint/cmd/fuzz/wgsl/fuzz.h
+++ b/src/tint/cmd/fuzz/wgsl/fuzz.h
@@ -35,6 +35,7 @@
 #include "src/tint/lang/wgsl/program/program.h"
 #include "src/tint/utils/bytes/buffer_reader.h"
 #include "src/tint/utils/bytes/decoder.h"
+#include "src/tint/utils/containers/enum_set.h"
 #include "src/tint/utils/containers/slice.h"
 #include "src/tint/utils/macros/static_init.h"
 
@@ -53,17 +54,34 @@
     std::string dxc;
 };
 
+/// ProgramProperties is an enumerator of flags used to describe characteristics of the input
+/// program.
+enum class ProgramProperties {
+    /// The program has builtin functions which have been shadowed
+    kBuiltinFnsShadowed,
+    /// The program has builtin types which have been shadowed
+    kBuiltinTypesShadowed,
+};
+
+/// Context holds information about the fuzzer options and the input program.
+struct Context {
+    /// The options used for Run()
+    Options options;
+    /// The properties of the input program
+    EnumSet<ProgramProperties> program_properties;
+};
+
 /// ProgramFuzzer describes a fuzzer function that takes a WGSL program as input
 struct ProgramFuzzer {
     /// @param name the name of the fuzzer
-    /// @param fn the fuzzer function with the signature `void(const Program&, const Options&, ...)`
-    /// @returns a ProgramFuzzer that invokes the function @p fn with the Program, Options, along
+    /// @param fn the fuzzer function with the signature `void(const Program&, const Context&, ...)`
+    /// @returns a ProgramFuzzer that invokes the function @p fn with the Program, Context, along
     /// with any additional arguments which are deserialized from the fuzzer input.
     template <typename... ARGS>
     static ProgramFuzzer Create(std::string_view name,
-                                void (*fn)(const Program&, const Options&, ARGS...)) {
+                                void (*fn)(const Program&, const Context&, ARGS...)) {
         if constexpr (sizeof...(ARGS) > 0) {
-            auto fn_with_decode = [fn](const Program& program, const Options& options,
+            auto fn_with_decode = [fn](const Program& program, const Context& context,
                                        Slice<const std::byte> data) {
                 if (!data.data) {
                     return;
@@ -72,7 +90,7 @@
                 auto data_args = bytes::Decode<std::tuple<std::decay_t<ARGS>...>>(reader);
                 if (data_args == Success) {
                     auto all_args =
-                        std::tuple_cat(std::tuple<const Program&, const Options&>{program, options},
+                        std::tuple_cat(std::tuple<const Program&, const Context&>{program, context},
                                        data_args.Get());
                     std::apply(*fn, all_args);
                 }
@@ -81,7 +99,7 @@
         } else {
             return ProgramFuzzer{
                 name,
-                [fn](const Program& program, const Options& options, Slice<const std::byte>) {
+                [fn](const Program& program, const Context& options, Slice<const std::byte>) {
                     fn(program, options);
                 },
             };
@@ -95,7 +113,7 @@
     template <typename... ARGS>
     static ProgramFuzzer Create(std::string_view name, void (*fn)(const Program&, ARGS...)) {
         if constexpr (sizeof...(ARGS) > 0) {
-            auto fn_with_decode = [fn](const Program& program, const Options&,
+            auto fn_with_decode = [fn](const Program& program, const Context&,
                                        Slice<const std::byte> data) {
                 if (!data.data) {
                     return;
@@ -112,7 +130,7 @@
         } else {
             return ProgramFuzzer{
                 name,
-                [fn](const Program& program, const Options&, Slice<const std::byte>) {
+                [fn](const Program& program, const Context&, Slice<const std::byte>) {
                     fn(program);
                 },
             };
@@ -122,7 +140,7 @@
     /// Name of the fuzzer function
     std::string_view name;
     /// The fuzzer function
-    std::function<void(const Program&, const Options&, Slice<const std::byte> data)> fn;
+    std::function<void(const Program&, const Context&, Slice<const std::byte> data)> fn;
 };
 
 /// Runs all the registered WGSL fuzzers with the supplied WGSL
@@ -142,9 +160,9 @@
 /// Where `...` is any number of deserializable parameters which are decoded from the base64
 /// content of the WGSL comments.
 /// @see bytes::Decode()
-#define TINT_WGSL_PROGRAM_FUZZER(FUNCTION)         \
+#define TINT_WGSL_PROGRAM_FUZZER(FUNCTION, ...)    \
     TINT_STATIC_INIT(::tint::fuzz::wgsl::Register( \
-        ::tint::fuzz::wgsl::ProgramFuzzer::Create(#FUNCTION, FUNCTION)))
+        ::tint::fuzz::wgsl::ProgramFuzzer::Create(#FUNCTION, FUNCTION, ##__VA_ARGS__)))
 
 }  // namespace tint::fuzz::wgsl
 
diff --git a/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc b/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc
index 12497e6..568a636 100644
--- a/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc
+++ b/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc
@@ -39,9 +39,7 @@
 namespace tint::hlsl::writer {
 namespace {
 
-void ASTFuzzer(const tint::Program& program,
-               const fuzz::wgsl::Options& fuzz_options,
-               Options options) {
+void ASTFuzzer(const tint::Program& program, const fuzz::wgsl::Context& context, Options options) {
     if (program.AST().HasOverrides()) {
         return;
     }
@@ -50,9 +48,9 @@
     if (res == Success) {
         const char* dxc_path = validate::kDxcDLLName;
         bool must_validate = false;
-        if (!fuzz_options.dxc.empty()) {
+        if (!context.options.dxc.empty()) {
             must_validate = true;
-            dxc_path = fuzz_options.dxc.c_str();
+            dxc_path = context.options.dxc.c_str();
         }
 
         auto dxc = tint::Command::LookPath(dxc_path);