[tint] Wrap accessing argc & argv to remove suppressions

This adds in an utility for converting argc & argv passed into main()
into a tint::Vector of the arguments.

This is done because it is a known issue that accessing the c-style
argv will always cause `-Wunsafe-buffer-usage` to fire, and this
wrapper isolates this known needed suppression into a single location
instead of being scattered across the code base.

This CL cleans up a bunch of main.cc files using this new utility,
though there is a couple exceptions. In remote_compile/main.cc the
suppression is just narrowed, since there is non-main related issues
in that file. There are some other main.cc files, like for the
benchmark tool, that use a different broader suppression that are not
touched.

Bug: 394825124
Change-Id: I92ea0fbc0012ef0900214a1eaefc9fb89a63553d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/224915
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/cmd/fuzz/ir/as/main.cc b/src/tint/cmd/fuzz/ir/as/main.cc
index a9b6f2b..6d62e38 100644
--- a/src/tint/cmd/fuzz/ir/as/main.cc
+++ b/src/tint/cmd/fuzz/ir/as/main.cc
@@ -39,6 +39,7 @@
 #include "src/tint/lang/wgsl/ast/module.h"
 #include "src/tint/lang/wgsl/helpers/apply_substitute_overrides.h"
 #include "src/tint/lang/wgsl/reader/reader.h"
+#include "src/tint/utils/command/args.h"
 #include "src/tint/utils/command/cli.h"
 #include "src/tint/utils/containers/transform.h"
 #include "src/tint/utils/macros/defer.h"
@@ -51,8 +52,6 @@
 #include "src/tint/utils/protos/ir_fuzz/ir_fuzz.pb.h"
 TINT_END_DISABLE_PROTOBUF_WARNINGS();
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 struct Options {
@@ -280,14 +279,7 @@
 }  // namespace
 
 int main(int argc, const char** argv) {
-    tint::Vector<std::string_view, 8> arguments;
-    for (int i = 1; i < argc; i++) {
-        std::string_view arg(argv[i]);
-        if (!arg.empty()) {
-            arguments.Push(argv[i]);
-        }
-    }
-
+    tint::Vector<std::string_view, 8> arguments = tint::args::Vectorize(argc, argv);
     Options options;
 
     tint::Initialize();
@@ -341,5 +333,3 @@
 
     return EXIT_SUCCESS;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/cmd/fuzz/ir/dis/main.cc b/src/tint/cmd/fuzz/ir/dis/main.cc
index 9b2e5e5..4923a3e 100644
--- a/src/tint/cmd/fuzz/ir/dis/main.cc
+++ b/src/tint/cmd/fuzz/ir/dis/main.cc
@@ -36,6 +36,7 @@
 #include "src/tint/lang/core/ir/disassembler.h"
 #include "src/tint/lang/spirv/writer/writer.h"
 #include "src/tint/lang/wgsl/writer/writer.h"
+#include "src/tint/utils/command/args.h"
 #include "src/tint/utils/command/cli.h"
 #include "src/tint/utils/containers/transform.h"
 #include "src/tint/utils/macros/defer.h"
@@ -50,8 +51,6 @@
 #include "src/tint/utils/protos/ir_fuzz/ir_fuzz.pb.h"
 TINT_END_DISABLE_PROTOBUF_WARNINGS();
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 /// @param data spriv shader to be converted
@@ -386,14 +385,7 @@
 }  // namespace
 
 int main(int argc, const char** argv) {
-    tint::Vector<std::string_view, 8> arguments;
-    for (int i = 1; i < argc; i++) {
-        std::string_view arg(argv[i]);
-        if (!arg.empty()) {
-            arguments.Push(argv[i]);
-        }
-    }
-
+    tint::Vector<std::string_view, 8> arguments = tint::args::Vectorize(argc, argv);
     Options options;
 
     tint::Initialize();
@@ -413,5 +405,3 @@
 
     return EXIT_SUCCESS;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/cmd/info/BUILD.bazel b/src/tint/cmd/info/BUILD.bazel
index 36573a1..532bc65 100644
--- a/src/tint/cmd/info/BUILD.bazel
+++ b/src/tint/cmd/info/BUILD.bazel
@@ -55,6 +55,7 @@
     "//src/tint/lang/wgsl/program",
     "//src/tint/lang/wgsl/sem",
     "//src/tint/utils",
+    "//src/tint/utils/command",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
     "//src/tint/utils/ice",
diff --git a/src/tint/cmd/info/BUILD.cmake b/src/tint/cmd/info/BUILD.cmake
index c7b8af0..4d83eb8 100644
--- a/src/tint/cmd/info/BUILD.cmake
+++ b/src/tint/cmd/info/BUILD.cmake
@@ -56,6 +56,7 @@
   tint_lang_wgsl_program
   tint_lang_wgsl_sem
   tint_utils
+  tint_utils_command
   tint_utils_containers
   tint_utils_diagnostic
   tint_utils_ice
diff --git a/src/tint/cmd/info/BUILD.gn b/src/tint/cmd/info/BUILD.gn
index 8d245c3..53efac9 100644
--- a/src/tint/cmd/info/BUILD.gn
+++ b/src/tint/cmd/info/BUILD.gn
@@ -57,6 +57,7 @@
     "${tint_src_dir}/lang/wgsl/program",
     "${tint_src_dir}/lang/wgsl/sem",
     "${tint_src_dir}/utils",
+    "${tint_src_dir}/utils/command",
     "${tint_src_dir}/utils/containers",
     "${tint_src_dir}/utils/diagnostic",
     "${tint_src_dir}/utils/ice",
diff --git a/src/tint/cmd/info/main.cc b/src/tint/cmd/info/main.cc
index 7943cb6..73dd517 100644
--- a/src/tint/cmd/info/main.cc
+++ b/src/tint/cmd/info/main.cc
@@ -33,10 +33,9 @@
 #include "src/tint/cmd/common/helper.h"
 #include "src/tint/lang/core/type/struct.h"
 #include "src/tint/lang/wgsl/inspector/entry_point.h"
+#include "src/tint/utils/command/args.h"
 #include "src/tint/utils/text/string.h"
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 struct Options {
@@ -58,9 +57,8 @@
 
 )";
 
-bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
-    for (size_t i = 1; i < args.size(); ++i) {
-        const std::string& arg = args[i];
+bool ParseArgs(tint::VectorRef<std::string_view> args, Options* opts) {
+    for (auto arg : args) {
         if (arg == "-h" || arg == "--help") {
             opts->show_help = true;
         } else if (arg == "--json") {
@@ -295,7 +293,7 @@
 }  // namespace
 
 int main(int argc, const char** argv) {
-    std::vector<std::string> args(argv, argv + argc);
+    tint::Vector<std::string_view, 8> args = tint::args::Vectorize(argc, argv);
     Options options;
 
     tint::SetInternalCompilerErrorReporter(&tint::cmd::TintInternalCompilerErrorReporter);
@@ -326,5 +324,3 @@
 
     return 0;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/cmd/loopy/BUILD.bazel b/src/tint/cmd/loopy/BUILD.bazel
index 362258f..b4c774c 100644
--- a/src/tint/cmd/loopy/BUILD.bazel
+++ b/src/tint/cmd/loopy/BUILD.bazel
@@ -62,6 +62,7 @@
     "//src/tint/lang/wgsl/sem",
     "//src/tint/lang/wgsl/writer/ir_to_program",
     "//src/tint/utils",
+    "//src/tint/utils/command",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
     "//src/tint/utils/ice",
diff --git a/src/tint/cmd/loopy/BUILD.cmake b/src/tint/cmd/loopy/BUILD.cmake
index c1dddb7..d45bb63 100644
--- a/src/tint/cmd/loopy/BUILD.cmake
+++ b/src/tint/cmd/loopy/BUILD.cmake
@@ -63,6 +63,7 @@
   tint_lang_wgsl_sem
   tint_lang_wgsl_writer_ir_to_program
   tint_utils
+  tint_utils_command
   tint_utils_containers
   tint_utils_diagnostic
   tint_utils_ice
diff --git a/src/tint/cmd/loopy/BUILD.gn b/src/tint/cmd/loopy/BUILD.gn
index 28abd46..6eb72be 100644
--- a/src/tint/cmd/loopy/BUILD.gn
+++ b/src/tint/cmd/loopy/BUILD.gn
@@ -64,6 +64,7 @@
     "${tint_src_dir}/lang/wgsl/sem",
     "${tint_src_dir}/lang/wgsl/writer/ir_to_program",
     "${tint_src_dir}/utils",
+    "${tint_src_dir}/utils/command",
     "${tint_src_dir}/utils/containers",
     "${tint_src_dir}/utils/diagnostic",
     "${tint_src_dir}/utils/ice",
diff --git a/src/tint/cmd/loopy/main.cc b/src/tint/cmd/loopy/main.cc
index 57a90e5..19dbef7 100644
--- a/src/tint/cmd/loopy/main.cc
+++ b/src/tint/cmd/loopy/main.cc
@@ -29,6 +29,7 @@
 
 #include "src/tint/api/tint.h"
 #include "src/tint/cmd/common/helper.h"
+#include "src/tint/utils/command/args.h"
 
 #if TINT_BUILD_GLSL_WRITER
 #include "src/tint/lang/glsl/writer/helpers/generate_bindings.h"
@@ -64,8 +65,6 @@
 #include "src/tint/lang/wgsl/writer/writer.h"
 #endif  // TINT_BUILD_WGSL_WRITER
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 enum class Format {
@@ -102,7 +101,7 @@
   --loop-count <num>                   -- Number of loops to run, default 100.
 )";
 
-Format parse_format(const std::string& fmt) {
+Format parse_format(const std::string_view fmt) {
     (void)fmt;
 
 #if TINT_BUILD_SPV_WRITER
@@ -142,12 +141,12 @@
     return Format::kUnknown;
 }
 
-bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
-    for (size_t i = 1; i < args.size(); ++i) {
-        const std::string& arg = args[i];
+bool ParseArgs(tint::VectorRef<std::string_view> args, Options* opts) {
+    for (size_t i = 1; i < args.Length(); ++i) {
+        auto arg = args[i];
         if (arg == "--format") {
             ++i;
-            if (i >= args.size()) {
+            if (i >= args.Length()) {
                 std::cerr << "Missing value for --format argument.\n";
                 return false;
             }
@@ -161,7 +160,7 @@
             opts->show_help = true;
         } else if (arg == "--loop") {
             ++i;
-            if (i >= args.size()) {
+            if (i >= args.Length()) {
                 std::cerr << "Missing value for --loop argument.\n";
                 return false;
             }
@@ -177,11 +176,11 @@
             }
         } else if (arg == "--loop-count") {
             ++i;
-            if (i >= args.size()) {
+            if (i >= args.Length()) {
                 std::cerr << "Missing value for --loop-count argument.\n";
                 return false;
             }
-            int32_t val = atoi(args[i].c_str());
+            int32_t val = atoi(std::string(args[i]).c_str());
             if (val <= 0) {
                 std::cerr << "Loop count must be greater then 0\n";
                 return false;
@@ -353,7 +352,7 @@
 }  // namespace
 
 int main(int argc, const char** argv) {
-    std::vector<std::string> args(argv, argv + argc);
+    tint::Vector<std::string_view, 8> args = tint::args::Vectorize(argc, argv);
     Options options;
 
     tint::Initialize();
@@ -481,5 +480,3 @@
 
     return 0;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/cmd/remote_compile/BUILD.bazel b/src/tint/cmd/remote_compile/BUILD.bazel
index 7abc45c..ba232a4 100644
--- a/src/tint/cmd/remote_compile/BUILD.bazel
+++ b/src/tint/cmd/remote_compile/BUILD.bazel
@@ -43,7 +43,13 @@
   ],
   deps = [
     "//src/tint/utils",
+    "//src/tint/utils/command",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/ice",
     "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/rtti",
     "//src/utils",
     
   ] + select({
diff --git a/src/tint/cmd/remote_compile/BUILD.cmake b/src/tint/cmd/remote_compile/BUILD.cmake
index 0d00547..23ea113 100644
--- a/src/tint/cmd/remote_compile/BUILD.cmake
+++ b/src/tint/cmd/remote_compile/BUILD.cmake
@@ -44,7 +44,13 @@
 
 tint_target_add_dependencies(tint_cmd_remote_compile_cmd cmd
   tint_utils
+  tint_utils_command
+  tint_utils_containers
+  tint_utils_ice
   tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_rtti
 )
 
 tint_target_add_external_dependencies(tint_cmd_remote_compile_cmd cmd
diff --git a/src/tint/cmd/remote_compile/BUILD.gn b/src/tint/cmd/remote_compile/BUILD.gn
index 2f22f2d..dc1f904 100644
--- a/src/tint/cmd/remote_compile/BUILD.gn
+++ b/src/tint/cmd/remote_compile/BUILD.gn
@@ -46,7 +46,13 @@
     "${dawn_root}/src/utils:utils",
     "${tint_src_dir}:thread",
     "${tint_src_dir}/utils",
+    "${tint_src_dir}/utils/command",
+    "${tint_src_dir}/utils/containers",
+    "${tint_src_dir}/utils/ice",
     "${tint_src_dir}/utils/macros",
+    "${tint_src_dir}/utils/math",
+    "${tint_src_dir}/utils/memory",
+    "${tint_src_dir}/utils/rtti",
   ]
 
   if (tint_build_msl_writer) {
diff --git a/src/tint/cmd/remote_compile/main.cc b/src/tint/cmd/remote_compile/main.cc
index 38aa37a..962cc86 100644
--- a/src/tint/cmd/remote_compile/main.cc
+++ b/src/tint/cmd/remote_compile/main.cc
@@ -40,11 +40,10 @@
 #include "src/tint/lang/msl/validate/validate.h"
 #endif
 
+#include "src/tint/utils/command/args.h"
 #include "src/tint/utils/macros/compiler.h"
 #include "src/tint/utils/socket.h"
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 /// The return structure of a compile function
@@ -158,6 +157,7 @@
         return error.empty();
     }
 
+    TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
     bool Read(void* data, size_t size) {
         auto buf = reinterpret_cast<uint8_t*>(data);
         while (size > 0 && error.empty()) {
@@ -174,6 +174,7 @@
         }
         return error.empty();
     }
+    TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -309,7 +310,7 @@
                int version_minor,
                bool verbose);
 
-int main(int argc, char* argv[]) {
+int main(int argc, const char** argv) {
     bool run_server = false;
     bool verbose = false;
     int version_major = 0;
@@ -318,17 +319,18 @@
 
     std::regex metal_version_re{"^-?-std=macos-metal([0-9]+)\\.([0-9]+)"};
 
-    std::vector<std::string> args;
-    for (int i = 1; i < argc; i++) {
-        std::string arg = argv[i];
+    auto args = tint::args::Vectorize(argc, argv);
+    std::vector<std::string> parsed_args;
+    for (size_t i = 0; i < args.Length(); i++) {
+        std::string arg = std::string(args[i]);
         if (arg == "-s" || arg == "--server") {
             run_server = true;
             continue;
         }
         if (arg == "-p" || arg == "--port") {
-            if (i < argc - 1) {
+            if (i < args.Length() - 1) {
                 i++;
-                port = argv[i];
+                port = args[i];
             } else {
                 printf("expected port number");
                 exit(1);
@@ -341,13 +343,13 @@
         }
 
         // xcrun flags are ignored so this executable can be used as a replacement for xcrun.
-        if ((arg == "-x" || arg == "-sdk") && (i < argc - 1)) {
+        if ((arg == "-x" || arg == "-sdk") && (i < args.Length() - 1)) {
             i++;
             continue;
         }
         if (arg == "metal") {
-            for (; i < argc; i++) {
-                arg = argv[i];
+            for (; i < args.Length(); i++) {
+                arg = args[i];
                 // metal_version_re
                 std::smatch metal_version_match;
                 if (std::regex_match(arg, metal_version_match, metal_version_re)) {
@@ -362,7 +364,7 @@
             continue;
         }
 
-        args.emplace_back(arg);
+        parsed_args.emplace_back(arg);
     }
 
     bool success = false;
@@ -372,21 +374,21 @@
     } else {
         std::string address;
         std::string file;
-        switch (args.size()) {
+        switch (parsed_args.size()) {
             case 1:
                 TINT_BEGIN_DISABLE_WARNING(DEPRECATED);
                 if (auto* addr = getenv("TINT_REMOTE_COMPILE_ADDRESS")) {
                     address = addr;
                 }
                 TINT_END_DISABLE_WARNING(DEPRECATED);
-                file = args[0];
+                file = parsed_args[0];
                 break;
             case 2:
-                address = args[0];
-                file = args[1];
+                address = parsed_args[0];
+                file = parsed_args[1];
                 break;
             default:
-                std::cerr << "Expected 1 or 2 arguments, got " << args.size() << "\n\n";
+                std::cerr << "Expected 1 or 2 arguments, got " << parsed_args.size() << "\n\n";
                 ShowUsage();
         }
         if (address.empty() || file.empty()) {
@@ -532,5 +534,3 @@
     }
     return true;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 7946de5..59057b6 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -33,6 +33,7 @@
 #include <unordered_map>
 #include <vector>
 #include "src/tint/lang/wgsl/sem/variable.h"
+#include "src/tint/utils/command/args.h"
 #include "src/tint/utils/text/color_mode.h"
 
 #if TINT_BUILD_SPV_READER || TINT_BUILD_SPV_WRITER
@@ -98,8 +99,6 @@
 #include "src/tint/lang/glsl/validate/validate.h"
 #endif  // TINT_BUILD_GLSL_VALIDATOR
 
-TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
-
 namespace {
 
 /// Prints the given hash value in a format string that the end-to-end test runner can parse.
@@ -220,6 +219,9 @@
     return Format::kUnknown;
 }
 
+// The actual warning occurs on `std::from_chars(hash.data(), hash.data() + hash.size(), value,
+// base);`, but disabling/enabling warnings cannot be done within function scope
+TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
 bool ParseArgs(tint::VectorRef<std::string_view> arguments, Options* opts) {
     using namespace tint::cli;  // NOLINT(build/namespaces)
 
@@ -366,6 +368,7 @@
                     hash = hash.substr(2);
                     base = 16;
                 }
+
                 std::from_chars(hash.data(), hash.data() + hash.size(), value, base);
                 opts->skip_hash.emplace(value);
             }
@@ -589,6 +592,7 @@
 
     return true;
 }
+TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
 
 [[maybe_unused]] void AddRenamer(Options& options,
                                  tint::ast::transform::Manager& transform_manager,
@@ -1290,14 +1294,7 @@
 }  // namespace
 
 int main(int argc, const char** argv) {
-    tint::Vector<std::string_view, 8> arguments;
-    for (int i = 1; i < argc; i++) {
-        std::string_view arg(argv[i]);
-        if (!arg.empty()) {
-            arguments.Push(argv[i]);
-        }
-    }
-
+    tint::Vector<std::string_view, 8> arguments = tint::args::Vectorize(argc, argv);
     Options options;
 
     tint::Initialize();
@@ -1434,5 +1431,3 @@
     }
     return success ? 0 : 1;
 }
-
-TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
diff --git a/src/tint/utils/command/BUILD.bazel b/src/tint/utils/command/BUILD.bazel
index 95d839e..4d8aecb 100644
--- a/src/tint/utils/command/BUILD.bazel
+++ b/src/tint/utils/command/BUILD.bazel
@@ -39,6 +39,7 @@
 cc_library(
   name = "command",
   srcs = [
+    "args.cc",
     "cli.cc",
   ] + select({
     ":_not_tint_build_is_linux__and__not_tint_build_is_mac__and__not_tint_build_is_win_": [
@@ -57,6 +58,7 @@
     "//conditions:default": [],
   }),
   hdrs = [
+    "args.h",
     "cli.h",
     "command.h",
   ],
diff --git a/src/tint/utils/command/BUILD.cmake b/src/tint/utils/command/BUILD.cmake
index 6724b16..8d730ef 100644
--- a/src/tint/utils/command/BUILD.cmake
+++ b/src/tint/utils/command/BUILD.cmake
@@ -39,6 +39,8 @@
 # Kind:      lib
 ################################################################################
 tint_add_target(tint_utils_command lib
+  utils/command/args.cc
+  utils/command/args.h
   utils/command/cli.cc
   utils/command/cli.h
   utils/command/command.h
diff --git a/src/tint/utils/command/BUILD.gn b/src/tint/utils/command/BUILD.gn
index 03e801a..86a0acb 100644
--- a/src/tint/utils/command/BUILD.gn
+++ b/src/tint/utils/command/BUILD.gn
@@ -45,6 +45,8 @@
 
 libtint_source_set("command") {
   sources = [
+    "args.cc",
+    "args.h",
     "cli.cc",
     "cli.h",
     "command.h",
diff --git a/src/tint/utils/command/args.cc b/src/tint/utils/command/args.cc
new file mode 100644
index 0000000..fb91a47
--- /dev/null
+++ b/src/tint/utils/command/args.cc
@@ -0,0 +1,46 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/utils/command/args.h"
+
+namespace tint::args {
+
+// Working with argv is known to always cause this warning to fire
+TINT_BEGIN_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
+tint::Vector<std::string_view, 8> Vectorize(int argc, const char** argv) {
+    tint::Vector<std::string_view, 8> arguments;
+    for (int i = 1; i < argc; i++) {
+        std::string_view arg(argv[i]);
+        if (!arg.empty()) {
+            arguments.Push(argv[i]);
+        }
+    }
+    return arguments;
+}
+TINT_END_DISABLE_WARNING(UNSAFE_BUFFER_USAGE);
+
+}  // namespace tint::args
diff --git a/src/tint/utils/command/args.h b/src/tint/utils/command/args.h
new file mode 100644
index 0000000..dceb4e5
--- /dev/null
+++ b/src/tint/utils/command/args.h
@@ -0,0 +1,46 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_COMMAND_ARGS_H_
+#define SRC_TINT_UTILS_COMMAND_ARGS_H_
+
+#include <string>
+
+#include "src/tint/utils/containers/vector.h"
+
+namespace tint::args {
+
+/// Converts the c-style program arguments into a safer C++ style vector.
+/// Note: This is done to isolate code that is known to cause compiler warnings to a single location
+/// @param argc number of arguments supplied to program, |argc| in main()
+/// @param argv array of argument strings supplied to program, |argv| in main()
+/// @returns a vector of string_views containing the argument strings
+tint::Vector<std::string_view, 8> Vectorize(int argc, const char** argv);
+
+}  // namespace tint::args
+
+#endif  // SRC_TINT_UTILS_COMMAND_ARGS_H_