]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactor: Use tl::expected for process_args return value
authorJoel Rosdahl <joel@rosdahl.net>
Mon, 27 May 2024 17:55:22 +0000 (19:55 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 27 May 2024 19:53:01 +0000 (21:53 +0200)
This should make the error paths more obvious.

As discussed in #1459.

src/ccache/argprocessing.cpp
src/ccache/argprocessing.hpp
src/ccache/ccache.cpp
unittest/test_argprocessing.cpp

index 35ace36929180b005f10e430d3668bfefe19896f..8df0fa794763fb76f21ebe14baf54d42df587150 100644 (file)
@@ -1253,7 +1253,7 @@ get_default_pch_file_extension(const Config& config)
 
 } // namespace
 
-ProcessArgsResult
+tl::expected<ProcessArgsResult, core::Statistic>
 process_args(Context& ctx)
 {
   ASSERT(!ctx.orig_args.empty());
@@ -1283,18 +1283,19 @@ process_args(Context& ctx)
 
   if (state.input_files.empty()) {
     LOG_RAW("No input file found");
-    return Statistic::no_input_file;
+    return tl::unexpected(Statistic::no_input_file);
   }
   if (state.input_files.size() > 1) {
     if (is_link) {
       LOG_RAW("Called for link");
-      return pstr(state.input_files.front()).str().find("conftest.")
-                 != std::string::npos
-               ? Statistic::autoconf_test
-               : Statistic::called_for_link;
+      return tl::unexpected(
+        pstr(state.input_files.front()).str().find("conftest.")
+            != std::string::npos
+          ? Statistic::autoconf_test
+          : Statistic::called_for_link);
     } else {
       LOG_RAW("Multiple input files");
-      return Statistic::multiple_source_files;
+      return tl::unexpected(Statistic::multiple_source_files);
     }
   }
 
@@ -1310,7 +1311,7 @@ process_args(Context& ctx)
     // potentially support this by behaving differently depending on the
     // compiler type, but let's just bail out for now.
     LOG_RAW("-Wp,-M[M]D in combination with -MF is not supported");
-    return Statistic::unsupported_compiler_option;
+    return tl::unexpected(Statistic::unsupported_compiler_option);
   }
 
   if (!state.last_seen_msvc_z_debug_option.empty()
@@ -1318,13 +1319,13 @@ process_args(Context& ctx)
     // /Zi and /ZI are unsupported, but /Z7 is fine.
     LOG("Compiler option {} is unsupported",
         state.last_seen_msvc_z_debug_option);
-    return Statistic::unsupported_compiler_option;
+    return tl::unexpected(Statistic::unsupported_compiler_option);
   }
 
   // Don't try to second guess the compiler's heuristics for stdout handling.
   if (args_info.output_obj == "-") {
     LOG_RAW("Output file is -");
-    return Statistic::output_to_stdout;
+    return tl::unexpected(Statistic::output_to_stdout);
   }
 
   // Determine output object file.
@@ -1366,7 +1367,7 @@ process_args(Context& ctx)
             || DirEntry(args_info.orig_included_pch_file).is_directory())) {
       LOG("Unsupported folder path value for -Fp: {}",
           args_info.included_pch_file);
-      return Statistic::could_not_use_precompiled_header;
+      return tl::unexpected(Statistic::could_not_use_precompiled_header);
     }
 
     if (included_pch_file_by_source && !args_info.input_file.empty()) {
@@ -1388,7 +1389,7 @@ process_args(Context& ctx)
   // args_info.output_obj which is needed to determine the log filename in
   // CCACHE_DEBUG mode.
   if (argument_error) {
-    return *argument_error;
+    return tl::unexpected(*argument_error);
   }
 
   if (state.generating_debuginfo_level_3 && !config.run_second_cpp()) {
@@ -1414,7 +1415,7 @@ process_args(Context& ctx)
         "You have to specify \"time_macros\" sloppiness when using"
         " precompiled headers to get direct hits");
       LOG_RAW("Disabling direct mode");
-      return Statistic::could_not_use_precompiled_header;
+      return tl::unexpected(Statistic::could_not_use_precompiled_header);
     }
   }
 
@@ -1434,7 +1435,7 @@ process_args(Context& ctx)
   if (!state.explicit_language.empty()) {
     if (!language_is_supported(state.explicit_language)) {
       LOG("Unsupported language: {}", state.explicit_language);
-      return Statistic::unsupported_source_language;
+      return tl::unexpected(Statistic::unsupported_source_language);
     }
     args_info.actual_language = state.explicit_language;
   } else {
@@ -1458,7 +1459,7 @@ process_args(Context& ctx)
     LOG_RAW(
       "You have to specify \"pch_defines,time_macros\" sloppiness when"
       " creating precompiled headers");
-    return Statistic::could_not_use_precompiled_header;
+    return tl::unexpected(Statistic::could_not_use_precompiled_header);
   }
 
   if (is_link) {
@@ -1468,15 +1469,16 @@ process_args(Context& ctx)
       LOG_RAW("No -c option found");
       // Having a separate statistic for autoconf tests is useful, as they are
       // the dominant form of "called for link" in many cases.
-      return args_info.input_file.find("conftest.") != std::string::npos
-               ? Statistic::autoconf_test
-               : Statistic::called_for_link;
+      return tl::unexpected(args_info.input_file.find("conftest.")
+                                != std::string::npos
+                              ? Statistic::autoconf_test
+                              : Statistic::called_for_link);
     }
   }
 
   if (args_info.actual_language.empty()) {
     LOG("Unsupported source extension: {}", args_info.input_file);
-    return Statistic::unsupported_source_language;
+    return tl::unexpected(Statistic::unsupported_source_language);
   }
 
   if (args_info.actual_language == "assembler") {
@@ -1520,7 +1522,7 @@ process_args(Context& ctx)
     DirEntry entry(args_info.output_obj);
     if (entry.exists() && !entry.is_regular_file()) {
       LOG("Not a regular file: {}", args_info.output_obj);
-      return Statistic::bad_output_file;
+      return tl::unexpected(Statistic::bad_output_file);
     }
   }
 
@@ -1531,7 +1533,7 @@ process_args(Context& ctx)
   fs::path output_dir = fs::path(args_info.output_obj).parent_path();
   if (!output_dir.empty() && !fs::is_directory(output_dir)) {
     LOG("Directory does not exist: {}", output_dir);
-    return Statistic::bad_output_file;
+    return tl::unexpected(Statistic::bad_output_file);
   }
 
   // Some options shouldn't be passed to the real compiler when it compiles
@@ -1617,7 +1619,7 @@ process_args(Context& ctx)
           LOG_RAW(
             "-Wp,-M[M]D with -o without -MMD, -MQ or -MT is only supported for"
             " GCC or Clang");
-          return Statistic::unsupported_compiler_option;
+          return tl::unexpected(Statistic::unsupported_compiler_option);
         }
       }
 
@@ -1744,7 +1746,7 @@ process_args(Context& ctx)
     compiler_args.push_back("/showIncludes");
   }
 
-  return {
+  return ProcessArgsResult{
     preprocessor_args,
     extra_args_to_hash,
     compiler_args,
index 94ee9b120548df57bee1a44e363e7f9143b948e5..59c8db42aa08cf5c93ce35c5233636a853a69db3 100644 (file)
@@ -21,7 +21,8 @@
 #include <ccache/Args.hpp>
 #include <ccache/core/Statistic.hpp>
 
-#include <optional>
+#include <tl/expected.hpp>
+
 #include <string>
 #include <string_view>
 #include <vector>
@@ -30,16 +31,6 @@ class Context;
 
 struct ProcessArgsResult
 {
-  ProcessArgsResult(core::Statistic error_);
-  ProcessArgsResult(const Args& preprocessor_args_,
-                    const Args& extra_args_to_hash_,
-                    const Args& compiler_args_,
-                    bool hash_actual_cwd_);
-
-  // nullopt on success, otherwise the statistics counter that should be
-  // incremented.
-  std::optional<core::Statistic> error;
-
   // Arguments (except -E) to send to the preprocessor.
   Args preprocessor_args;
 
@@ -53,23 +44,7 @@ struct ProcessArgsResult
   bool hash_actual_cwd = false;
 };
 
-inline ProcessArgsResult::ProcessArgsResult(core::Statistic error_)
-  : error(error_)
-{
-}
-
-inline ProcessArgsResult::ProcessArgsResult(const Args& preprocessor_args_,
-                                            const Args& extra_args_to_hash_,
-                                            const Args& compiler_args_,
-                                            bool hash_actual_cwd_)
-  : preprocessor_args(preprocessor_args_),
-    extra_args_to_hash(extra_args_to_hash_),
-    compiler_args(compiler_args_),
-    hash_actual_cwd(hash_actual_cwd_)
-{
-}
-
-ProcessArgsResult process_args(Context& ctx);
+tl::expected<ProcessArgsResult, core::Statistic> process_args(Context& ctx);
 
 // Return whether `path` represents a precompiled header (see "Precompiled
 // Headers" in GCC docs).
index c87e2f58a18b0754a098b3c99e2c6832e1bc72d8..63015da1c8eef85c98f4c167374542635eb9af44 100644 (file)
@@ -2490,10 +2490,10 @@ do_cache_compilation(Context& ctx)
   // be disabled.
   util::setenv("CCACHE_DISABLE", "1");
 
-  ProcessArgsResult processed = process_args(ctx);
+  auto process_args_result = process_args(ctx);
 
-  if (processed.error) {
-    return tl::unexpected(*processed.error);
+  if (!process_args_result) {
+    return tl::unexpected(process_args_result.error());
   }
 
   TRY(set_up_uncached_err());
@@ -2595,9 +2595,9 @@ do_cache_compilation(Context& ctx)
   }
 
   TRY(hash_common_info(
-    ctx, processed.preprocessor_args, common_hash, ctx.args_info));
+    ctx, process_args_result->preprocessor_args, common_hash, ctx.args_info));
 
-  if (processed.hash_actual_cwd) {
+  if (process_args_result->hash_actual_cwd) {
     common_hash.hash_delimiter("actual_cwd");
     common_hash.hash(ctx.actual_cwd);
   }
@@ -2606,8 +2606,8 @@ do_cache_compilation(Context& ctx)
   Hash direct_hash = common_hash;
   init_hash_debug(ctx, direct_hash, 'd', "DIRECT MODE", debug_text_file);
 
-  Args args_to_hash = processed.preprocessor_args;
-  args_to_hash.push_back(processed.extra_args_to_hash);
+  Args args_to_hash = process_args_result->preprocessor_args;
+  args_to_hash.push_back(process_args_result->extra_args_to_hash);
 
   bool put_result_in_manifest = false;
   std::optional<Hash::Digest> result_key;
@@ -2659,7 +2659,7 @@ do_cache_compilation(Context& ctx)
     init_hash_debug(ctx, cpp_hash, 'p', "PREPROCESSOR MODE", debug_text_file);
 
     const auto result_and_manifest_key = calculate_result_and_manifest_key(
-      ctx, args_to_hash, cpp_hash, &processed.preprocessor_args);
+      ctx, args_to_hash, cpp_hash, &process_args_result->preprocessor_args);
     if (!result_and_manifest_key) {
       return tl::unexpected(result_and_manifest_key.error());
     }
@@ -2712,14 +2712,14 @@ do_cache_compilation(Context& ctx)
     return tl::unexpected(Statistic::cache_miss);
   }
 
-  add_prefix(processed.compiler_args, ctx.config.prefix_command());
+  add_prefix(process_args_result->compiler_args, ctx.config.prefix_command());
 
   // In depend_mode, extend the direct hash.
   Hash* depend_mode_hash = ctx.config.depend_mode() ? &direct_hash : nullptr;
 
   // Run real compiler, sending output to cache.
-  const auto digest =
-    to_cache(ctx, processed.compiler_args, result_key, depend_mode_hash);
+  const auto digest = to_cache(
+    ctx, process_args_result->compiler_args, result_key, depend_mode_hash);
   if (!digest) {
     return tl::unexpected(digest.error());
   }
index 1aee78c2ae61026fbea7b0b41a3a6b3d16e4e3e9..57ef044b2a257e89cb7e14f56ba69530941e9c05 100644 (file)
@@ -67,12 +67,12 @@ TEST_CASE("pass -fsyntax-only to compiler only")
   ctx.orig_args = Args::from_string("cc -c foo.c -fsyntax-only");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == "-fsyntax-only");
-  CHECK(result.compiler_args.to_string() == "cc -fsyntax-only -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == "-fsyntax-only");
+  CHECK(result->compiler_args.to_string() == "cc -fsyntax-only -c");
 }
 
 TEST_CASE("dash_E_should_result_in_called_for_preprocessing")
@@ -83,7 +83,7 @@ TEST_CASE("dash_E_should_result_in_called_for_preprocessing")
   ctx.orig_args = Args::from_string("cc -c foo.c -E");
 
   util::write_file("foo.c", "");
-  CHECK(process_args(ctx).error == Statistic::called_for_preprocessing);
+  CHECK(process_args(ctx).error() == Statistic::called_for_preprocessing);
 }
 
 TEST_CASE("dash_M_should_be_unsupported")
@@ -94,7 +94,7 @@ TEST_CASE("dash_M_should_be_unsupported")
   ctx.orig_args = Args::from_string("cc -c foo.c -M");
 
   util::write_file("foo.c", "");
-  CHECK(process_args(ctx).error == Statistic::unsupported_compiler_option);
+  CHECK(process_args(ctx).error() == Statistic::unsupported_compiler_option);
 }
 
 TEST_CASE("dependency_args_to_preprocessor_if_run_second_cpp_is_false")
@@ -108,12 +108,12 @@ TEST_CASE("dependency_args_to_preprocessor_if_run_second_cpp_is_false")
   util::write_file("foo.c", "");
   ctx.config.set_run_second_cpp(false);
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc " + dep_args);
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc " + dep_args);
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -c");
 }
 
 TEST_CASE("dependency_args_to_compiler_if_run_second_cpp_is_true")
@@ -126,12 +126,12 @@ TEST_CASE("dependency_args_to_compiler_if_run_second_cpp_is_true")
   ctx.orig_args = Args::from_string("cc " + dep_args + " -c foo.c -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == dep_args);
-  CHECK(result.compiler_args.to_string() == "cc -c " + dep_args);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == dep_args);
+  CHECK(result->compiler_args.to_string() == "cc -c " + dep_args);
 }
 
 TEST_CASE("cpp_only_args_to_preprocessor_if_run_second_cpp_is_false")
@@ -151,13 +151,13 @@ TEST_CASE("cpp_only_args_to_preprocessor_if_run_second_cpp_is_false")
   util::write_file("foo.c", "");
   ctx.config.set_run_second_cpp(false);
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string()
         == "cc " + cpp_args + " " + dep_args);
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -c");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -c");
 }
 
 TEST_CASE(
@@ -176,12 +176,12 @@ TEST_CASE(
     Args::from_string("cc " + cpp_args + " " + dep_args + " -c foo.c -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc " + cpp_args);
-  CHECK(result.extra_args_to_hash.to_string() == dep_args);
-  CHECK(result.compiler_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc " + cpp_args);
+  CHECK(result->extra_args_to_hash.to_string() == dep_args);
+  CHECK(result->compiler_args.to_string()
         == "cc " + cpp_args + " -c " + dep_args);
 }
 
@@ -194,12 +194,12 @@ TEST_CASE(
   ctx.orig_args = Args::from_string("cc -c " + dep_args + " foo.c -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == dep_args);
-  CHECK(result.compiler_args.to_string() == "cc -c " + dep_args);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == dep_args);
+  CHECK(result->compiler_args.to_string() == "cc -c " + dep_args);
 }
 
 TEST_CASE("MQ_flag_should_not_be_added_if_run_second_cpp_is_true")
@@ -209,12 +209,12 @@ TEST_CASE("MQ_flag_should_not_be_added_if_run_second_cpp_is_true")
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -MF foo.d -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD -MF foo.d");
-  CHECK(result.compiler_args.to_string() == "cc -c -MD -MF foo.d");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD -MF foo.d");
+  CHECK(result->compiler_args.to_string() == "cc -c -MD -MF foo.d");
 }
 
 TEST_CASE("MQ_flag_should_be_added_if_run_second_cpp_is_false")
@@ -225,12 +225,12 @@ TEST_CASE("MQ_flag_should_be_added_if_run_second_cpp_is_false")
   util::write_file("foo.c", "");
   ctx.config.set_run_second_cpp(false);
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -c");
 }
 
 TEST_CASE("MF_should_be_added_if_run_second_cpp_is_false")
@@ -241,12 +241,12 @@ TEST_CASE("MF_should_be_added_if_run_second_cpp_is_false")
   util::write_file("foo.c", "");
   ctx.config.set_run_second_cpp(false);
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -c");
 }
 
 TEST_CASE("MF_should_not_be_added_if_run_second_cpp_is_true")
@@ -256,12 +256,12 @@ TEST_CASE("MF_should_not_be_added_if_run_second_cpp_is_true")
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD");
-  CHECK(result.compiler_args.to_string() == "cc -c -MD");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD");
+  CHECK(result->compiler_args.to_string() == "cc -c -MD");
 }
 
 TEST_CASE("equal_sign_after_MF_should_be_removed")
@@ -271,12 +271,12 @@ TEST_CASE("equal_sign_after_MF_should_be_removed")
   ctx.orig_args = Args::from_string("cc -c -MF=path foo.c -o foo.o");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MFpath");
-  CHECK(result.compiler_args.to_string() == "cc -c -MFpath");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MFpath");
+  CHECK(result->compiler_args.to_string() == "cc -c -MFpath");
 }
 
 TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
@@ -291,12 +291,12 @@ TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
     FMT("cc --sysroot={}/foo/bar -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
+  const auto result = process_args(ctx);
+  CHECK(result);
 #ifdef _WIN32
-  CHECK(result.preprocessor_args[1] == "--sysroot=foo\\bar");
+  CHECK(result->preprocessor_args[1] == "--sysroot=foo\\bar");
 #else
-  CHECK(result.preprocessor_args[1] == "--sysroot=foo/bar");
+  CHECK(result->preprocessor_args[1] == "--sysroot=foo/bar");
 #endif
 }
 
@@ -312,10 +312,10 @@ TEST_CASE(
   std::string arg_string = FMT("cc --sysroot {}/foo -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "--sysroot");
-  CHECK(result.preprocessor_args[2] == "foo");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args[1] == "--sysroot");
+  CHECK(result->preprocessor_args[2] == "foo");
 }
 
 TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
@@ -328,11 +328,11 @@ TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
 
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MMD -MT bar -MFfoo.d");
-  CHECK(result.compiler_args.to_string() == "cc -c -MMD -MT bar -MFfoo.d");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MMD -MT bar -MFfoo.d");
+  CHECK(result->compiler_args.to_string() == "cc -c -MMD -MT bar -MFfoo.d");
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
@@ -345,12 +345,12 @@ TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
 
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string()
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string()
         == "-MMD -MFfoo.d -MT foo -MTbar");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result->compiler_args.to_string()
         == "cc -c -MMD -MFfoo.d -MT foo -MTbar");
 }
 
@@ -364,12 +364,12 @@ TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
 
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc");
-  CHECK(result.extra_args_to_hash.to_string()
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc");
+  CHECK(result->extra_args_to_hash.to_string()
         == "-MMD -MFfoo.d -MQ foo -MQbar");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result->compiler_args.to_string()
         == "cc -c -MMD -MFfoo.d -MQ foo -MQbar");
 }
 
@@ -380,12 +380,12 @@ TEST_CASE("MQ_flag_without_immediate_argument_should_not_add_MQobj")
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "gcc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQ foo.d");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "gcc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQ foo.d");
+  CHECK(result->compiler_args.to_string()
         == "gcc -c -MD -MP -MFfoo.d -MQ foo.d");
 }
 
@@ -396,12 +396,12 @@ TEST_CASE("MT_flag_without_immediate_argument_should_not_add_MTobj")
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "gcc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MT foo.d");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "gcc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MT foo.d");
+  CHECK(result->compiler_args.to_string()
         == "gcc -c -MD -MP -MFfoo.d -MT foo.d");
 }
 
@@ -412,12 +412,13 @@ TEST_CASE("MQ_flag_with_immediate_argument_should_not_add_MQobj")
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "gcc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQfoo.d");
-  CHECK(result.compiler_args.to_string() == "gcc -c -MD -MP -MFfoo.d -MQfoo.d");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "gcc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQfoo.d");
+  CHECK(result->compiler_args.to_string()
+        == "gcc -c -MD -MP -MFfoo.d -MQfoo.d");
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
@@ -427,12 +428,13 @@ TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "gcc");
-  CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MTfoo.d");
-  CHECK(result.compiler_args.to_string() == "gcc -c -MD -MP -MFfoo.d -MTfoo.d");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "gcc");
+  CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MTfoo.d");
+  CHECK(result->compiler_args.to_string()
+        == "gcc -c -MD -MP -MFfoo.d -MTfoo.d");
 }
 
 TEST_CASE(
@@ -447,9 +449,9 @@ TEST_CASE(
   std::string arg_string = FMT("cc -isystem {}/foo -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args[2] == "foo");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args[2] == "foo");
 }
 
 #ifndef _WIN32
@@ -465,9 +467,9 @@ TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   std::string arg_string = FMT("cc -isystem{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "-isystemfoo");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args[1] == "-isystemfoo");
 }
 
 TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
@@ -482,9 +484,9 @@ TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   std::string arg_string = FMT("cc -I{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "-Ifoo");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args[1] == "-Ifoo");
 }
 #endif // _WIN32
 
@@ -495,12 +497,12 @@ TEST_CASE("debug_flag_order_with_known_option_first")
   ctx.orig_args = Args::from_string("cc -g1 -gsplit-dwarf foo.c -c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc -g1 -gsplit-dwarf");
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -g1 -gsplit-dwarf -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc -g1 -gsplit-dwarf");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -g1 -gsplit-dwarf -c");
 }
 
 TEST_CASE("debug_flag_order_with_known_option_last")
@@ -510,12 +512,12 @@ TEST_CASE("debug_flag_order_with_known_option_last")
   ctx.orig_args = Args::from_string("cc -gsplit-dwarf -g1 foo.c -c");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc -gsplit-dwarf -g1");
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "cc -gsplit-dwarf -g1 -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc -gsplit-dwarf -g1");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "cc -gsplit-dwarf -g1 -c");
 }
 
 TEST_CASE("options_not_to_be_passed_to_the_preprocessor")
@@ -526,13 +528,13 @@ TEST_CASE("options_not_to_be_passed_to_the_preprocessor")
     "cc -Wa,foo foo.c -g -c -DX -Werror -Xlinker fie -Xlinker,fum -Wno-error");
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cc -g -DX");
-  CHECK(result.extra_args_to_hash.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cc -g -DX");
+  CHECK(result->extra_args_to_hash.to_string()
         == "-Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result->compiler_args.to_string()
         == "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c");
 }
 
@@ -546,12 +548,12 @@ TEST_CASE("cuda_option_file")
   util::write_file("foo.optf", "-c foo.c -g -Wall -o");
   util::write_file("bar.optf", "out -DX");
 
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "nvcc -g -Wall -DX");
-  CHECK(result.extra_args_to_hash.to_string() == "");
-  CHECK(result.compiler_args.to_string() == "nvcc -g -Wall -DX -c");
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "nvcc -g -Wall -DX");
+  CHECK(result->extra_args_to_hash.to_string() == "");
+  CHECK(result->compiler_args.to_string() == "nvcc -g -Wall -DX -c");
 }
 
 TEST_CASE("nvcc_warning_flags_short")
@@ -563,12 +565,12 @@ TEST_CASE("nvcc_warning_flags_short")
   ctx.orig_args =
     Args::from_string("nvcc -Werror all-warnings -Xcompiler -Werror -c foo.cu");
   util::write_file("foo.cu", "");
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "nvcc -Xcompiler -Werror");
-  CHECK(result.extra_args_to_hash.to_string() == "-Werror all-warnings");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "nvcc -Xcompiler -Werror");
+  CHECK(result->extra_args_to_hash.to_string() == "-Werror all-warnings");
+  CHECK(result->compiler_args.to_string()
         == "nvcc -Werror all-warnings -Xcompiler -Werror -c");
 }
 
@@ -581,12 +583,12 @@ TEST_CASE("nvcc_warning_flags_long")
   ctx.orig_args = Args::from_string(
     "nvcc --Werror all-warnings -Xcompiler -Werror -c foo.cu");
   util::write_file("foo.cu", "");
-  const ProcessArgsResult result = process_args(ctx);
+  const auto result = process_args(ctx);
 
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "nvcc -Xcompiler -Werror");
-  CHECK(result.extra_args_to_hash.to_string() == "--Werror all-warnings");
-  CHECK(result.compiler_args.to_string()
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "nvcc -Xcompiler -Werror");
+  CHECK(result->extra_args_to_hash.to_string() == "--Werror all-warnings");
+  CHECK(result->compiler_args.to_string()
         == "nvcc --Werror all-warnings -Xcompiler -Werror -c");
 }
 
@@ -617,11 +619,11 @@ TEST_CASE("-Xclang")
                       + extra_args + " " + pch_pth_variants);
   util::write_file("foo.c", "");
 
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(result.preprocessor_args.to_string()
+  const auto result = process_args(ctx);
+  CHECK(result->preprocessor_args.to_string()
         == "clang " + common_args + " " + pch_pth_variants);
-  CHECK(result.extra_args_to_hash.to_string() == extra_args);
-  CHECK(result.compiler_args.to_string()
+  CHECK(result->extra_args_to_hash.to_string() == extra_args);
+  CHECK(result->compiler_args.to_string()
         == "clang " + common_args + " " + color_diag + " " + extra_args + " "
              + pch_pth_variants + " -c -fcolor-diagnostics");
 }
@@ -639,63 +641,63 @@ TEST_CASE("-x")
     // specification.
     ctx.orig_args = Args::from_string("gcc -c foo.c -xCODE");
 
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.preprocessor_args.to_string() == "gcc -xCODE");
-    CHECK(result.extra_args_to_hash.to_string() == "");
-    CHECK(result.compiler_args.to_string() == "gcc -xCODE -c");
+    const auto result = process_args(ctx);
+    CHECK(result->preprocessor_args.to_string() == "gcc -xCODE");
+    CHECK(result->extra_args_to_hash.to_string() == "");
+    CHECK(result->compiler_args.to_string() == "gcc -xCODE -c");
   }
 
   SUBCASE("compile .c as c++ (without space)")
   {
     ctx.orig_args = Args::from_string("gcc -xc++ -c foo.c");
-    const ProcessArgsResult result = process_args(ctx);
+    const auto result = process_args(ctx);
     CHECK(ctx.args_info.actual_language == "c++");
-    CHECK(result.preprocessor_args.to_string() == "gcc -x c++");
-    CHECK(result.extra_args_to_hash.to_string() == "");
-    CHECK(result.compiler_args.to_string() == "gcc -x c++ -c");
+    CHECK(result->preprocessor_args.to_string() == "gcc -x c++");
+    CHECK(result->extra_args_to_hash.to_string() == "");
+    CHECK(result->compiler_args.to_string() == "gcc -x c++ -c");
   }
 
   SUBCASE("compile .c as c++ (with space)")
   {
     ctx.orig_args = Args::from_string("gcc -x c++ -c foo.c");
-    const ProcessArgsResult result = process_args(ctx);
+    const auto result = process_args(ctx);
     CHECK(ctx.args_info.actual_language == "c++");
-    CHECK(result.preprocessor_args.to_string() == "gcc -x c++");
-    CHECK(result.extra_args_to_hash.to_string() == "");
-    CHECK(result.compiler_args.to_string() == "gcc -x c++ -c");
+    CHECK(result->preprocessor_args.to_string() == "gcc -x c++");
+    CHECK(result->extra_args_to_hash.to_string() == "");
+    CHECK(result->compiler_args.to_string() == "gcc -x c++ -c");
   }
 
   SUBCASE("compile .c as c++ (file first, no effect)")
   {
     ctx.orig_args = Args::from_string("gcc -c foo.c -x c++");
-    const ProcessArgsResult result = process_args(ctx);
+    const auto result = process_args(ctx);
     CHECK(ctx.args_info.actual_language == "c");
-    CHECK(result.preprocessor_args.to_string() == "gcc");
-    CHECK(result.extra_args_to_hash.to_string() == "");
-    CHECK(result.compiler_args.to_string() == "gcc -c");
+    CHECK(result->preprocessor_args.to_string() == "gcc");
+    CHECK(result->extra_args_to_hash.to_string() == "");
+    CHECK(result->compiler_args.to_string() == "gcc -c");
   }
 
   SUBCASE("unknown -x option (lowercase)")
   {
     ctx.orig_args = Args::from_string("gcc -x unsupported_language -c foo.c");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::unsupported_source_language);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::unsupported_source_language);
     CHECK(ctx.args_info.actual_language == "");
   }
 
   SUBCASE("UNKNOWN -x option (uppercase)")
   {
     ctx.orig_args = Args::from_string("gcc -x UNSUPPORTED_LANGUAGE -c foo.c");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::unsupported_source_language);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::unsupported_source_language);
     CHECK(ctx.args_info.actual_language == "");
   }
 
   SUBCASE("missing param")
   {
     ctx.orig_args = Args::from_string("gcc -c foo.c -x");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::bad_compiler_arguments);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::bad_compiler_arguments);
     CHECK(ctx.args_info.actual_language == "");
   }
 }
@@ -715,10 +717,10 @@ TEST_CASE("MSVC options"
 
   ctx.orig_args = Args::from_string(
     FMT("cl.exe /Fobar.obj /c {}/foo.c /foobar", ctx.actual_cwd));
-  const ProcessArgsResult result = process_args(ctx);
-  CHECK(!result.error);
-  CHECK(result.preprocessor_args.to_string() == "cl.exe /foobar");
-  CHECK(result.compiler_args.to_string() == "cl.exe /foobar -c");
+  const auto result = process_args(ctx);
+  CHECK(result);
+  CHECK(result->preprocessor_args.to_string() == "cl.exe /foobar");
+  CHECK(result->compiler_args.to_string() == "cl.exe /foobar -c");
 }
 
 TEST_CASE("MSVC PCH options")
@@ -734,14 +736,14 @@ TEST_CASE("MSVC PCH options")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h /Fopch.cpp.obj /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.cpp.pch");
     CHECK(ctx.args_info.output_obj == "pch.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h -c");
   }
 
@@ -752,14 +754,14 @@ TEST_CASE("MSVC PCH options")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(!ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.cpp.pch");
     CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h -c");
   }
 }
@@ -777,14 +779,14 @@ TEST_CASE("MSVC PCH options with empty -Yc")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Yc /Fppch.cpp.pch /FIpch.h /Fopch.cpp.obj /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.cpp.pch");
     CHECK(ctx.args_info.output_obj == "pch.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yc /Fppch.cpp.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Yc /Fppch.cpp.pch /FIpch.h -c");
   }
 
@@ -795,14 +797,14 @@ TEST_CASE("MSVC PCH options with empty -Yc")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(!ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.cpp.pch");
     CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h -c");
   }
 }
@@ -819,13 +821,13 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp")
   SUBCASE("Create PCH")
   {
     ctx.orig_args = Args::from_string("cl.exe /Yc /Fopch.cpp.obj /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "pch.cpp.obj");
-    CHECK(result.preprocessor_args.to_string() == "cl.exe /Yc");
-    CHECK(result.compiler_args.to_string() == "cl.exe /Yc -c");
+    CHECK(result->preprocessor_args.to_string() == "cl.exe /Yc");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Yc -c");
   }
 
   util::write_file("pch.pch", "");
@@ -835,14 +837,14 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Yupch.h /Fppch.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(!ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h -c");
   }
 }
@@ -859,13 +861,13 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp and -Fo")
   SUBCASE("Create PCH")
   {
     ctx.orig_args = Args::from_string("cl.exe /Yc /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "pch.obj");
-    CHECK(result.preprocessor_args.to_string() == "cl.exe /Yc");
-    CHECK(result.compiler_args.to_string() == "cl.exe /Yc -c");
+    CHECK(result->preprocessor_args.to_string() == "cl.exe /Yc");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Yc -c");
   }
 
   util::write_file("pch.pch", "");
@@ -875,14 +877,14 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp and -Fo")
   {
     ctx.orig_args = Args::from_string(
       "cl.exe /Yupch.h /Fppch.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
+    const auto result = process_args(ctx);
+    REQUIRE(result);
     CHECK(!ctx.args_info.generating_pch);
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
-    CHECK(result.preprocessor_args.to_string()
+    CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h");
-    CHECK(result.compiler_args.to_string()
+    CHECK(result->compiler_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h -c");
   }
 }
@@ -899,8 +901,8 @@ TEST_CASE("MSVC PCH unsupported options")
   {
     ctx.orig_args =
       Args::from_string("cl.exe /Yc /FpE:\\foo\\bar\\ /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::could_not_use_precompiled_header);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::could_not_use_precompiled_header);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.orig_included_pch_file == "E:\\foo\\bar\\");
     CHECK(ctx.args_info.output_obj == "pch.obj");
@@ -909,8 +911,8 @@ TEST_CASE("MSVC PCH unsupported options")
   SUBCASE("/Fp with relative folder path")
   {
     ctx.orig_args = Args::from_string("cl.exe /Yc /Fpfolder\\ /c pch.cpp");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::could_not_use_precompiled_header);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::could_not_use_precompiled_header);
     CHECK(ctx.args_info.generating_pch);
     CHECK(ctx.args_info.orig_included_pch_file == "folder\\");
     CHECK(ctx.args_info.output_obj == "pch.obj");
@@ -927,40 +929,40 @@ TEST_CASE("MSVC debug information format options")
   SUBCASE("Only /Z7")
   {
     ctx.orig_args = Args::from_string("cl.exe /c foo.c /Z7");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
-    CHECK(result.preprocessor_args.to_string() == "cl.exe /Z7");
-    CHECK(result.compiler_args.to_string() == "cl.exe /Z7 -c");
+    const auto result = process_args(ctx);
+    REQUIRE(result);
+    CHECK(result->preprocessor_args.to_string() == "cl.exe /Z7");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Z7 -c");
   }
 
   SUBCASE("Only /Zi")
   {
     ctx.orig_args = Args::from_string("cl.exe /c foo.c /Zi");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::unsupported_compiler_option);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::unsupported_compiler_option);
   }
 
   SUBCASE("Only /ZI")
   {
     ctx.orig_args = Args::from_string("cl.exe /c foo.c /ZI");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::unsupported_compiler_option);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::unsupported_compiler_option);
   }
 
   SUBCASE("/Z7 + /Zi")
   {
     ctx.orig_args = Args::from_string("cl.exe /Z7 /c foo.c /Zi");
-    const ProcessArgsResult result = process_args(ctx);
-    CHECK(result.error == Statistic::unsupported_compiler_option);
+    const auto result = process_args(ctx);
+    CHECK(result.error() == Statistic::unsupported_compiler_option);
   }
 
   SUBCASE("/Zi + /Z7")
   {
     ctx.orig_args = Args::from_string("cl.exe /Zi /c foo.c /Z7");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
-    CHECK(result.preprocessor_args.to_string() == "cl.exe /Zi /Z7");
-    CHECK(result.compiler_args.to_string() == "cl.exe /Zi /Z7 -c");
+    const auto result = process_args(ctx);
+    REQUIRE(result);
+    CHECK(result->preprocessor_args.to_string() == "cl.exe /Zi /Z7");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Zi /Z7 -c");
   }
 }
 
@@ -976,17 +978,17 @@ TEST_CASE("ClangCL Debug information options")
   SUBCASE("/Z7")
   {
     ctx.orig_args = Args::from_string("clang-cl.exe /c foo.c /Z7");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
-    CHECK(result.preprocessor_args.to_string() == "clang-cl.exe /Z7");
+    const auto result = process_args(ctx);
+    REQUIRE(result);
+    CHECK(result->preprocessor_args.to_string() == "clang-cl.exe /Z7");
   }
 
   SUBCASE("/Zi")
   {
     ctx.orig_args = Args::from_string("clang-cl.exe /c foo.c /Zi");
-    const ProcessArgsResult result = process_args(ctx);
-    REQUIRE(!result.error);
-    CHECK(result.preprocessor_args.to_string() == "clang-cl.exe /Zi");
+    const auto result = process_args(ctx);
+    REQUIRE(result);
+    CHECK(result->preprocessor_args.to_string() == "clang-cl.exe /Zi");
   }
 }