]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactoring: Use return syntax for process_args (#652)
authorAlexander Lanin <alex@lanin.de>
Fri, 4 Sep 2020 06:32:45 +0000 (08:32 +0200)
committerGitHub <noreply@github.com>
Fri, 4 Sep 2020 06:32:45 +0000 (08:32 +0200)
src/argprocessing.cpp
src/argprocessing.hpp
src/ccache.cpp
unittest/test_argprocessing.cpp

index 83a776732c17f040e5c9b1d684bcc58c32c5f22e..40ba4a9e293c6629ad1da1082f21eb4e89630017 100644 (file)
@@ -920,11 +920,8 @@ handle_dependency_environment_variables(Context& ctx,
 
 } // namespace
 
-optional<Statistic>
-process_args(Context& ctx,
-             Args& preprocessor_args,
-             Args& extra_args_to_hash,
-             Args& compiler_args)
+ProcessArgsResult
+process_args(Context& ctx)
 {
   assert(!ctx.orig_args.empty());
 
@@ -942,7 +939,7 @@ process_args(Context& ctx,
   for (size_t i = 1; i < args.size(); i++) {
     auto error = process_arg(ctx, args, i, state);
     if (error) {
-      return error;
+      return *error;
     }
   }
 
@@ -1155,7 +1152,7 @@ process_args(Context& ctx,
     args_info.output_su = Util::make_relative_path(ctx, default_sufile_name);
   }
 
-  compiler_args = state.common_args;
+  Args compiler_args = state.common_args;
   compiler_args.push_back(state.compiler_only_args);
 
   if (config.run_second_cpp()) {
@@ -1196,7 +1193,7 @@ process_args(Context& ctx,
     compiler_args.push_back(arch);
   }
 
-  preprocessor_args = state.common_args;
+  Args preprocessor_args = state.common_args;
   preprocessor_args.push_back(state.cpp_args);
 
   if (config.run_second_cpp()) {
@@ -1211,10 +1208,10 @@ process_args(Context& ctx,
     preprocessor_args.push_back(state.dep_args);
   }
 
-  extra_args_to_hash = state.compiler_only_args;
+  Args extra_args_to_hash = state.compiler_only_args;
   if (config.run_second_cpp()) {
     extra_args_to_hash.push_back(state.dep_args);
   }
 
-  return nullopt;
+  return {preprocessor_args, extra_args_to_hash, compiler_args};
 }
index 6df33229d0d0abd31bac0daee73ba69293817c76..8424a95a9d468f1f4764275ec602c25b972b3287 100644 (file)
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include "Args.hpp"
 #include "stats.hpp"
 
 #include "third_party/nonstd/optional.hpp"
 class Context;
 class Args;
 
-// Process the compiler options into options suitable for passing to the
-// preprocessor (`preprocessor_args`) and the real compiler (`compiler_args`).
-// `preprocessor_args` doesn't include -E; this is added later.
-// `extra_args_to_hash` are the arguments that are not included in
-// `preprocessor_args` but that should be included in the hash.
-//
-// Returns nullopt on success, otherwise the statistics counter that should be
-// incremented.
-nonstd::optional<Statistic> process_args(Context& ctx,
-                                         Args& preprocessor_args,
-                                         Args& extra_args_to_hash,
-                                         Args& compiler_args);
+struct ProcessArgsResult
+{
+  ProcessArgsResult(Statistic err) : error(err)
+  {
+  }
+  ProcessArgsResult(Args preprocessor_args,
+                    Args extra_args_to_hash,
+                    Args compiler_args)
+    : preprocessor_args(preprocessor_args),
+      extra_args_to_hash(extra_args_to_hash),
+      compiler_args(compiler_args)
+  {
+  }
+
+  // nullopt on success, otherwise the statistics counter that should be
+  // incremented.
+  nonstd::optional<Statistic> error;
+
+  // Arguments (except -E) to send to the preprocessor.
+  Args preprocessor_args;
+
+  // Arguments not sent to the preprocessor but that should be part of the
+  // hash.
+  Args extra_args_to_hash;
+
+  // Arguments to send to the real compiler.
+  Args compiler_args;
+};
+
+ProcessArgsResult process_args(Context& ctx);
index 5d61f282c0a310c3ecd7a2b696dc0c298bd5e733..d1a97605ffbfa2822414220e3e7c9c34136897c6 100644 (file)
@@ -2024,19 +2024,11 @@ do_cache_compilation(Context& ctx, const char* const* argv)
   ctx.guessed_compiler = guess_compiler(ctx.orig_args[0]);
   MTR_END("main", "guess_compiler");
 
-  // Arguments (except -E) to send to the preprocessor.
-  Args preprocessor_args;
-  // Arguments not sent to the preprocessor but that should be part of the
-  // hash.
-  Args extra_args_to_hash;
-  // Arguments to send to the real compiler.
-  Args compiler_args;
   MTR_BEGIN("main", "process_args");
 
-  auto error =
-    process_args(ctx, preprocessor_args, extra_args_to_hash, compiler_args);
-  if (error) {
-    throw Failure(*error);
+  ProcessArgsResult processed = process_args(ctx);
+  if (processed.error) {
+    throw Failure(*processed.error);
   }
 
   MTR_END("main", "process_args");
@@ -2089,7 +2081,8 @@ do_cache_compilation(Context& ctx, const char* const* argv)
     ctx, common_hash, ctx.args_info.output_obj, 'c', "COMMON", debug_text_file);
 
   MTR_BEGIN("hash", "common_hash");
-  hash_common_info(ctx, preprocessor_args, common_hash, ctx.args_info);
+  hash_common_info(
+    ctx, processed.preprocessor_args, common_hash, ctx.args_info);
   MTR_END("hash", "common_hash");
 
   // Try to find the hash using the manifest.
@@ -2101,8 +2094,8 @@ do_cache_compilation(Context& ctx, const char* const* argv)
                   "DIRECT MODE",
                   debug_text_file);
 
-  Args args_to_hash = preprocessor_args;
-  args_to_hash.push_back(extra_args_to_hash);
+  Args args_to_hash = processed.preprocessor_args;
+  args_to_hash.push_back(processed.extra_args_to_hash);
 
   bool put_result_in_manifest = false;
   optional<Digest> result_name;
@@ -2152,7 +2145,7 @@ do_cache_compilation(Context& ctx, const char* const* argv)
 
     MTR_BEGIN("hash", "cpp_hash");
     result_name = calculate_result_name(
-      ctx, args_to_hash, preprocessor_args, cpp_hash, false);
+      ctx, args_to_hash, processed.preprocessor_args, cpp_hash, false);
     MTR_END("hash", "cpp_hash");
 
     // calculate_result_name does not return nullopt if the last (direct_mode)
@@ -2196,15 +2189,17 @@ do_cache_compilation(Context& ctx, const char* const* argv)
     throw Failure(Statistic::cache_miss);
   }
 
-  add_prefix(ctx, compiler_args, ctx.config.prefix_command());
+  add_prefix(ctx, processed.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.
   MTR_BEGIN("cache", "to_cache");
-  to_cache(
-    ctx, compiler_args, ctx.args_info.depend_extra_args, depend_mode_hash);
+  to_cache(ctx,
+           processed.compiler_args,
+           ctx.args_info.depend_extra_args,
+           depend_mode_hash);
   update_manifest_file(ctx);
   MTR_END("cache", "to_cache");
 
index 0d29e5076f6179943aac71e3579c45f6d206d667..5df0b884d88e6be137f0f3a50c8f38bda633ae6c 100644 (file)
@@ -74,15 +74,10 @@ TEST_CASE("dash_E_should_result_in_called_for_preprocessing")
   TestContext test_context;
 
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c foo.c -E");
-  Args preprocessed;
-  Args extra;
-  Args compiler;
 
   Util::write_file("foo.c", "");
-  CHECK(process_args(ctx, preprocessed, extra, compiler)
-        == Statistic::called_for_preprocessing);
+  CHECK(process_args(ctx).error == Statistic::called_for_preprocessing);
 }
 
 TEST_CASE("dash_M_should_be_unsupported")
@@ -90,266 +85,195 @@ TEST_CASE("dash_M_should_be_unsupported")
   TestContext test_context;
 
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c foo.c -M");
-  Args preprocessed;
-  Args extra;
-  Args compiler;
 
   Util::write_file("foo.c", "");
-  CHECK(process_args(ctx, preprocessed, extra, compiler)
-        == 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")
 {
   TestContext test_context;
-
+  const std::string dep_args =
+    "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+    " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
   Context ctx;
-
-#define DEP_ARGS                                                               \
-  "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"        \
-  " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf"
-  ctx.orig_args = Args::from_string("cc " DEP_ARGS " -c foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc " DEP_ARGS);
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -c");
-#undef DEP_ARGS
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
+  ctx.orig_args = Args::from_string("cc " + dep_args + " -c foo.c -o foo.o");
   Util::write_file("foo.c", "");
-
   ctx.config.set_run_second_cpp(false);
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc " + dep_args));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -c"));
 }
 
 TEST_CASE("dependency_args_to_compiler_if_run_second_cpp_is_true")
 {
   TestContext test_context;
-
+  const std::string dep_args =
+    "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+    " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
   Context ctx;
-
-#define DEP_ARGS                                                               \
-  "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"        \
-  " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf"
-  ctx.orig_args = Args::from_string("cc " DEP_ARGS " -c foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string(DEP_ARGS);
-  Args exp_cc = Args::from_string("cc -c " DEP_ARGS);
-#undef DEP_ARGS
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
+  ctx.orig_args = Args::from_string("cc " + dep_args + " -c foo.c -o foo.o");
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash == Args::from_string(dep_args));
+  CHECK(result.compiler_args == Args::from_string("cc -c " + dep_args));
 }
 
 TEST_CASE("cpp_only_args_to_preprocessor_if_run_second_cpp_is_false")
 {
   TestContext test_context;
-
+  const std::string cpp_args =
+    "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"
+    " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."
+    " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."
+    " -trigraphs -fworking-directory -fno-working-directory";
+  const std::string dep_args =
+    "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+    " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
   Context ctx;
-
-#define CPP_ARGS                                                               \
-  "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"      \
-  " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."         \
-  " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."      \
-  " -trigraphs -fworking-directory -fno-working-directory"
-#define DEP_ARGS                                                               \
-  "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"        \
-  " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf"
   ctx.orig_args =
-    Args::from_string("cc " CPP_ARGS " " DEP_ARGS " -c foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc " CPP_ARGS " " DEP_ARGS);
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -c");
-#undef DEP_ARGS
-#undef CPP_ARGS
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
+    Args::from_string("cc " + cpp_args + " " + dep_args + " -c foo.c -o foo.o");
   Util::write_file("foo.c", "");
-
   ctx.config.set_run_second_cpp(false);
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args
+        == Args::from_string("cc " + cpp_args + " " + dep_args));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -c"));
 }
 
 TEST_CASE(
   "cpp_only_args_to_preprocessor_and_compiler_if_run_second_cpp_is_true")
 {
   TestContext test_context;
-
+  const std::string cpp_args =
+    "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"
+    " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."
+    " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."
+    " -trigraphs -fworking-directory -fno-working-directory";
+  const std::string dep_args =
+    " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+    " -Wp,-MMD,wpmmd";
   Context ctx;
-
-#define CPP_ARGS                                                               \
-  "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"      \
-  " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."         \
-  " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."      \
-  " -trigraphs -fworking-directory -fno-working-directory"
-#define DEP_ARGS                                                               \
-  " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"       \
-  " -Wp,-MMD,wpmmd"
   ctx.orig_args =
-    Args::from_string("cc " CPP_ARGS " " DEP_ARGS " -c foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc " CPP_ARGS);
-  Args exp_extra = Args::from_string(DEP_ARGS);
-  Args exp_cc = Args::from_string("cc " CPP_ARGS " -c " DEP_ARGS);
-#undef DEP_ARGS
-#undef CPP_ARGS
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
+    Args::from_string("cc " + cpp_args + " " + dep_args + " -c foo.c -o foo.o");
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc " + cpp_args));
+  CHECK(result.extra_args_to_hash == Args::from_string(dep_args));
+  CHECK(result.compiler_args
+        == Args::from_string("cc " + cpp_args + " -c " + dep_args));
 }
 
 TEST_CASE(
   "dependency_args_that_take_an_argument_should_not_require_space_delimiter")
 {
   TestContext test_context;
-
+  const std::string dep_args = "-MMD -MFfoo.d -MT mt -MTmt -MQmq";
   Context ctx;
-
-#define DEP_ARGS "-MMD -MFfoo.d -MT mt -MTmt -MQmq"
-  ctx.orig_args = Args::from_string("cc -c " DEP_ARGS " foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string(DEP_ARGS);
-  Args exp_cc = Args::from_string("cc -c " DEP_ARGS);
-#undef DEP_ARGS
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
+  ctx.orig_args = Args::from_string("cc -c " + dep_args + " foo.c -o foo.o");
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash == Args::from_string(dep_args));
+  CHECK(result.compiler_args == Args::from_string("cc -c " + dep_args));
 }
 
 TEST_CASE("MQ_flag_should_not_be_added_if_run_second_cpp_is_true")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -MF foo.d -o foo.o");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MD -MF foo.d");
-  Args exp_cc = Args::from_string("cc -c -MD -MF foo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash == Args::from_string("-MD -MF foo.d"));
+  CHECK(result.compiler_args == Args::from_string("cc -c -MD -MF foo.d"));
 }
 
 TEST_CASE("MQ_flag_should_be_added_if_run_second_cpp_is_false")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -MF foo.d -o foo.o");
-  Args exp_cpp = Args::from_string("cc -MD -MF foo.d -MQ foo.o");
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
   Util::write_file("foo.c", "");
-
   ctx.config.set_run_second_cpp(false);
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args
+        == Args::from_string("cc -MD -MF foo.d -MQ foo.o"));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -c"));
 }
 
 TEST_CASE("MF_should_be_added_if_run_second_cpp_is_false")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc -MD -MF foo.d -MQ foo.o");
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
-
   ctx.config.set_run_second_cpp(false);
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args
+        == Args::from_string("cc -MD -MF foo.d -MQ foo.o"));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -c"));
 }
 
 TEST_CASE("MF_should_not_be_added_if_run_second_cpp_is_true")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c -MD foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MD");
-  Args exp_cc = Args::from_string("cc -c -MD");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash == Args::from_string("-MD"));
+  CHECK(result.compiler_args == Args::from_string("cc -c -MD"));
 }
 
 TEST_CASE("equal_sign_after_MF_should_be_removed")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -c -MF=path foo.c -o foo.o");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MFpath");
-  Args exp_cc = Args::from_string("cc -c -MFpath");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash == Args::from_string("-MFpath"));
+  CHECK(result.compiler_args == Args::from_string("cc -c -MFpath"));
 }
 
 TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
@@ -358,18 +282,15 @@ TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
 
   Context ctx;
 
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   ctx.config.set_base_dir(get_root());
   std::string arg_string =
     fmt::format("cc --sysroot={}/foo/bar -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(act_cpp[1] == "--sysroot=./foo/bar");
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args[1] == "--sysroot=./foo/bar");
 }
 
 TEST_CASE(
@@ -379,19 +300,16 @@ TEST_CASE(
 
   Context ctx;
 
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   ctx.config.set_base_dir(get_root());
   std::string arg_string =
     fmt::format("cc --sysroot {}/foo -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(act_cpp[1] == "--sysroot");
-  CHECK(act_cpp[2] == "./foo");
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args[1] == "--sysroot");
+  CHECK(result.preprocessor_args[2] == "./foo");
 }
 
 TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
@@ -399,22 +317,18 @@ TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
   TestContext test_context;
 
   Context ctx;
-
   ctx.orig_args =
     Args::from_string("cc -c foo.c -o foo.o -MMD -MT bar -MFfoo.d");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MMD -MT bar -MFfoo.d");
-  Args exp_cc = Args::from_string("cc -c -MMD -MT bar -MFfoo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
 
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MMD -MT bar -MFfoo.d"));
+  CHECK(result.compiler_args
+        == Args::from_string("cc -c -MMD -MT bar -MFfoo.d"));
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
@@ -422,22 +336,18 @@ TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
   TestContext test_context;
 
   Context ctx;
-
   ctx.orig_args =
     Args::from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MT foo -MTbar");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MMD -MFfoo.d -MT foo -MTbar");
-  Args exp_cc = Args::from_string("cc -c -MMD -MFfoo.d -MT foo -MTbar");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
 
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MMD -MFfoo.d -MT foo -MTbar"));
+  CHECK(result.compiler_args
+        == Args::from_string("cc -c -MMD -MFfoo.d -MT foo -MTbar"));
 }
 
 TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
@@ -445,110 +355,86 @@ TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
   TestContext test_context;
 
   Context ctx;
-
   ctx.orig_args =
     Args::from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MQ foo -MQbar");
-  Args exp_cpp = Args::from_string("cc");
-  Args exp_extra = Args::from_string("-MMD -MFfoo.d -MQ foo -MQbar");
-  Args exp_cc = Args::from_string("cc -c -MMD -MFfoo.d -MQ foo -MQbar");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
 
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MMD -MFfoo.d -MQ foo -MQbar"));
+  CHECK(result.compiler_args
+        == Args::from_string("cc -c -MMD -MFfoo.d -MQ foo -MQbar"));
 }
 
 TEST_CASE("MQ_flag_without_immediate_argument_should_not_add_MQobj")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c");
-  Args exp_cpp = Args::from_string("gcc");
-  Args exp_extra = Args::from_string("-MD -MP -MFfoo.d -MQ foo.d");
-  Args exp_cc = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("gcc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MD -MP -MFfoo.d -MQ foo.d"));
+  CHECK(result.compiler_args
+        == Args::from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d"));
 }
 
 TEST_CASE("MT_flag_without_immediate_argument_should_not_add_MTobj")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c");
-  Args exp_cpp = Args::from_string("gcc");
-  Args exp_extra = Args::from_string("-MD -MP -MFfoo.d -MT foo.d");
-  Args exp_cc = Args::from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("gcc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MD -MP -MFfoo.d -MT foo.d"));
+  CHECK(result.compiler_args
+        == Args::from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d"));
 }
 
 TEST_CASE("MQ_flag_with_immediate_argument_should_not_add_MQobj")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c");
-  Args exp_cpp = Args::from_string("gcc");
-  Args exp_extra = Args::from_string("-MD -MP -MFfoo.d -MQfoo.d");
-  Args exp_cc = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("gcc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MD -MP -MFfoo.d -MQfoo.d"));
+  CHECK(result.compiler_args
+        == Args::from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d"));
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c");
-  Args exp_cpp = Args::from_string("gcc");
-  Args exp_extra = Args::from_string("-MD -MP -MFfoo.d -MTfoo.d");
-  Args exp_cc = Args::from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("gcc"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string("-MD -MP -MFfoo.d -MTfoo.d"));
+  CHECK(result.compiler_args
+        == Args::from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d"));
 }
 
 TEST_CASE(
@@ -558,18 +444,15 @@ TEST_CASE(
 
   Context ctx;
 
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   ctx.config.set_base_dir(get_root());
   std::string arg_string =
     fmt::format("cc -isystem {}/foo -c foo.c", ctx.actual_cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK("./foo" == act_cpp[2]);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK("./foo" == result.preprocessor_args[2]);
 }
 
 TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
@@ -578,10 +461,6 @@ TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
 
   Context ctx;
 
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   ctx.config.set_base_dir("/"); // posix
   // Windows path doesn't work concatenated.
@@ -589,8 +468,9 @@ TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   std::string arg_string = fmt::format("cc -isystem{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK("-isystem./foo" == act_cpp[1]);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK("-isystem./foo" == result.preprocessor_args[1]);
 }
 
 TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
@@ -599,10 +479,6 @@ TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
 
   Context ctx;
 
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   ctx.config.set_base_dir("/"); // posix
   // Windows path doesn't work concatenated.
@@ -610,98 +486,77 @@ TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   std::string arg_string = fmt::format("cc -I{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK("-I./foo" == act_cpp[1]);
+  const ProcessArgsResult result = process_args(ctx);
+  CHECK(!result.error);
+  CHECK("-I./foo" == result.preprocessor_args[1]);
 }
 
 TEST_CASE("debug_flag_order_with_known_option_first")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -g1 -gsplit-dwarf foo.c -c");
-  Args exp_cpp = Args::from_string("cc -g1 -gsplit-dwarf");
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -g1 -gsplit-dwarf -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc -g1 -gsplit-dwarf"));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -g1 -gsplit-dwarf -c"));
 }
 
 TEST_CASE("debug_flag_order_with_known_option_last")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string("cc -gsplit-dwarf -g1 foo.c -c");
-  Args exp_cpp = Args::from_string("cc -gsplit-dwarf -g1");
-  Args exp_extra;
-  Args exp_cc = Args::from_string("cc -gsplit-dwarf -g1 -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc -gsplit-dwarf -g1"));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("cc -gsplit-dwarf -g1 -c"));
 }
 
 TEST_CASE("options_not_to_be_passed_to_the_preprocessor")
 {
   TestContext test_context;
-
   Context ctx;
-
   ctx.orig_args = Args::from_string(
     "cc -Wa,foo foo.c -g -c -DX -Werror -Xlinker fie -Xlinker,fum -Wno-error");
-  Args exp_cpp = Args::from_string("cc -g -DX");
-  Args exp_extra =
-    Args::from_string(" -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error");
-  Args exp_cc = Args::from_string(
-    "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("cc -g -DX"));
+  CHECK(result.extra_args_to_hash
+        == Args::from_string(
+          " -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error"));
+  CHECK(result.compiler_args
+        == Args::from_string(
+          "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c"));
 }
 
 TEST_CASE("cuda_option_file")
 {
   TestContext test_context;
-
   Context ctx;
   ctx.guessed_compiler = GuessedCompiler::nvcc;
-
   ctx.orig_args = Args::from_string("nvcc -optf foo.optf,bar.optf");
-  Args exp_cpp = Args::from_string("nvcc -g -Wall -DX");
-  Args exp_extra = Args::from_string("");
-  Args exp_cc = Args::from_string("nvcc -g -Wall -DX -c");
-  Args act_cpp;
-  Args act_extra;
-  Args act_cc;
-
   Util::write_file("foo.c", "");
   Util::write_file("foo.optf", "-c foo.c -g -Wall -o");
   Util::write_file("bar.optf", "out -DX");
-  CHECK(!process_args(ctx, act_cpp, act_extra, act_cc));
-  CHECK(exp_cpp == act_cpp);
-  CHECK(exp_extra == act_extra);
-  CHECK(exp_cc == act_cc);
+
+  const ProcessArgsResult result = process_args(ctx);
+
+  CHECK(!result.error);
+  CHECK(result.preprocessor_args == Args::from_string("nvcc -g -Wall -DX"));
+  CHECK(result.extra_args_to_hash == Args::from_string(""));
+  CHECK(result.compiler_args == Args::from_string("nvcc -g -Wall -DX -c"));
 }
 
 TEST_SUITE_END();