Repository: swiftlang/llvm-project
Pull request #10723: New option format for prefix mapping
Author: login: sina-mahdavi
Created: 2025-05-21T20:04:25Z | Merged: 2025-05-22T22:40:45Z

The current option for specifying prefix mappings `-fdepscan-prefix-map=<old>=<new>` fails to handle paths containing `=` characters properly. This PR adds a 2nd form of that option `-fdepscan-prefix-map old new` while keeping the old format as well to fix this problem.


Diff:
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index cefab5df86db0..81e11415d7763 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8836,11 +8836,15 @@ def fdepscan_include_tree : Flag<["-"], "fdepscan-include-tree">,
 //
 // FIXME: Add DepscanOption flag.
 def fdepscan_prefix_map_EQ : Joined<["-"], "fdepscan-prefix-map=">,
-    Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+    Group<f_Group>, Visibility<[ClangOption]>,
     MetaVarName<"<old>=<new>">,
     HelpText<"With -fdepscan, seamlessly filter the CAS filesystem to"
-             " apply the given prefix, updating the command-line to match.">,
-    MarshallingInfoStringVector<FrontendOpts<"PathPrefixMappings">>;
+             " apply the given prefix, updating the command-line to match.">;
+def fdepscan_prefix_map : MultiArg<["-"], "fdepscan-prefix-map", 2>,
+    Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+    MetaVarName<"<old> <new>">,
+    HelpText<"With -fdepscan, seamlessly filter the CAS filesystem to"
+             " apply the given prefix, updating the command-line to match.">;
 def fdepscan_prefix_map_sdk_EQ :
     Joined<["-"], "fdepscan-prefix-map-sdk=">,
     Group<f_Group>, MetaVarName<"<new>">,
diff --git a/clang/include/clang/Frontend/CompileJobCacheKey.h b/clang/include/clang/Frontend/CompileJobCacheKey.h
index b3959379e9249..6c644ac6c949f 100644
--- a/clang/include/clang/Frontend/CompileJobCacheKey.h
+++ b/clang/include/clang/Frontend/CompileJobCacheKey.h
@@ -48,7 +48,7 @@ struct CompileJobCachingOptions {
   /// See \c FrontendOptions::WriteOutputAsCASID.
   bool WriteOutputAsCASID;
   /// See \c FrontendOptions::PathPrefixMappings.
-  std::vector<std::string> PathPrefixMappings;
+  std::vector<std::pair<std::string, std::string>> PathPrefixMappings;
 };
 
 /// Create a cache key for the given \c CompilerInvocation as a \c CASID. If \p
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 625f16c8f1c17..f25d43d9ffb20 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -528,7 +528,7 @@ class FrontendOptions {
   /// When caching is enabled, represents remappings for all the file paths that
   /// the compilation may access. This is useful for canonicalizing the
   /// compilation for caching purposes.
-  std::vector<std::string> PathPrefixMappings;
+  std::vector<std::pair<std::string, std::string>> PathPrefixMappings;
 
   // Currently this is only used as part of the `-extract-api` action.
   // A comma separated list of files providing a list of APIs to
diff --git a/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h b/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h
index f885266d50766..56d520aafb42d 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h
@@ -48,7 +48,7 @@ struct DepscanPrefixMapping {
                                     llvm::PrefixMapper &Mapper);
 
   /// Add path mappings to the \p Mapper.
-  static void configurePrefixMapper(ArrayRef<std::string> PathPrefixMappings,
+  static void configurePrefixMapper(ArrayRef<std::pair<std::string, std::string>> PathPrefixMappings,
                                     llvm::PrefixMapper &Mapper);
 
   /// Apply the mappings from \p Mapper to \p Invocation.
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 877e27a772b2d..143e35a1ef4ce 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5059,8 +5059,9 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
 
   if (Arg *A = Args.getLastArg(options::OPT_fdepscan_prefix_map_sdk_EQ)) {
     if (IsPathApplicableAsPrefix(Sysroot)) {
-      CmdArgs.push_back(Args.MakeArgString(Twine("-fdepscan-prefix-map=") +
-                                           *Sysroot + "=" + A->getValue()));
+      CmdArgs.push_back("-fdepscan-prefix-map");
+      CmdArgs.push_back(Args.MakeArgString(*Sysroot));
+      CmdArgs.push_back(Args.MakeArgString(A->getValue()));
     } else {
       // FIXME: warning if we cannot infer sdk
     }
@@ -5075,22 +5076,31 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
       Guess = llvm::sys::path::parent_path(Guess);
     }
     if (IsPathApplicableAsPrefix(Guess)) {
-      CmdArgs.push_back(Args.MakeArgString(Twine("-fdepscan-prefix-map=") +
-                                           Guess + "=" + A->getValue()));
+      CmdArgs.push_back("-fdepscan-prefix-map");
+      CmdArgs.push_back(Args.MakeArgString(Guess));
+      CmdArgs.push_back(Args.MakeArgString(A->getValue()));
     } else {
       // FIXME: warning if we cannot infer toolchain
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) {
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map, options::OPT_fdepscan_prefix_map_EQ)) {
     A->claim();
-    StringRef Map = A->getValue();
-    StringRef Prefix = Map.split('=').first;
-    if (Prefix.size() == Map.size() || !IsPathApplicableAsPrefix(Prefix)) {
+    StringRef Prefix, MapTarget;
+    if (A->getOption().matches(options::OPT_fdepscan_prefix_map_EQ)) {
+      StringRef Map = A->getValue();
+      std::tie(Prefix, MapTarget) = Map.split('=');
+    } else {
+      Prefix = A->getValue(0);
+      MapTarget = A->getValue(1);
+    }
+    if (MapTarget.size() == 0 || !IsPathApplicableAsPrefix(Prefix)) {
       D.Diag(diag::err_drv_invalid_argument_to_option)
           << A->getValue() << A->getOption().getName();
     } else {
-      A->render(Args, CmdArgs);
+      CmdArgs.push_back("-fdepscan-prefix-map");
+      CmdArgs.push_back(Args.MakeArgString(Prefix));
+      CmdArgs.push_back(Args.MakeArgString(MapTarget));
     }
   }
 }
diff --git a/clang/lib/Frontend/CompileJobCache.cpp b/clang/lib/Frontend/CompileJobCache.cpp
index 3d9069c73fa20..ae36d45f81a6f 100644
--- a/clang/lib/Frontend/CompileJobCache.cpp
+++ b/clang/lib/Frontend/CompileJobCache.cpp
@@ -330,8 +330,7 @@ std::optional<int> CompileJobCache::initialize(CompilerInstance &Clang) {
 
   llvm::PrefixMapper PrefixMapper;
   llvm::SmallVector<llvm::MappedPrefix> Split;
-  llvm::MappedPrefix::transformJoinedIfValid(CacheOpts.PathPrefixMappings,
-                                             Split);
+  llvm::MappedPrefix::transformPairs(CacheOpts.PathPrefixMappings, Split);
   for (const auto &MappedPrefix : Split) {
     // We use the inverse mapping because the \p PrefixMapper will be used for
     // de-canonicalization of paths.
@@ -629,7 +628,7 @@ Expected<std::optional<int>> CompileJobCache::replayCachedResult(
 
   llvm::PrefixMapper PrefixMapper;
   llvm::SmallVector<llvm::MappedPrefix> Split;
-  llvm::MappedPrefix::transformJoinedIfValid(
+  llvm::MappedPrefix::transformPairs(
       Clang.getFrontendOpts().PathPrefixMappings, Split);
   for (const auto &MappedPrefix : Split) {
     // We use the inverse mapping because the \p PrefixMapper will be used for
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index e925cceb878eb..a0895fe69c736 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -86,6 +86,7 @@
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/PrefixMapper.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/VersionTuple.h"
@@ -3155,6 +3156,9 @@ static void GenerateFrontendArgs(const FrontendOptions &Opts,
   for (const auto &A : Opts.ModuleCacheKeys)
     GenerateMultiArg(Consumer, OPT_fmodule_file_cache_key, {A.first, A.second});
 
+  for (const auto &A : Opts.PathPrefixMappings)
+    GenerateMultiArg(Consumer, OPT_fdepscan_prefix_map, {A.first, A.second});
+
   if (Opts.AuxTargetCPU)
     GenerateArg(Consumer, OPT_aux_target_cpu, *Opts.AuxTargetCPU);
 
@@ -3381,6 +3385,13 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
+    ArrayRef<const char *> Values = A->getValues();
+    assert(Values.size() == 2);
+    Opts.PathPrefixMappings.emplace_back(Values[0], Values[1]);
+  }
+
   if (Opts.ProgramAction != frontend::GenerateModule && Opts.IsSystemModule)
     Diags.Report(diag::err_drv_argument_only_allowed_with) << "-fsystem-module"
                                                            << "-emit-module";
diff --git a/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp b/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp
index d2d67298e9fea..9e3377381a489 100644
--- a/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp
@@ -143,7 +143,7 @@ void DepscanPrefixMapping::remapInvocationPaths(CompilerInvocation &Invocation,
   // Pass the remappings so that we can map cached diagnostics to the local
   // paths during diagnostic rendering.
   for (const llvm::MappedPrefix &Map : Mapper.getMappings()) {
-    FrontendOpts.PathPrefixMappings.push_back(Map.Old + "=" + Map.New);
+    FrontendOpts.PathPrefixMappings.emplace_back(Map.Old, Map.New);
   }
 
   auto mapInPlaceAll = [&](std::vector<std::string> &Vector) {
@@ -244,12 +244,12 @@ void DepscanPrefixMapping::configurePrefixMapper(const CompilerInvocation &CI,
 }
 
 void DepscanPrefixMapping::configurePrefixMapper(
-    ArrayRef<std::string> PathPrefixMappings, llvm::PrefixMapper &Mapper) {
+    ArrayRef<std::pair<std::string, std::string>> PathPrefixMappings, llvm::PrefixMapper &Mapper) {
   if (PathPrefixMappings.empty())
     return;
 
   llvm::SmallVector<llvm::MappedPrefix> Split;
-  llvm::MappedPrefix::transformJoinedIfValid(PathPrefixMappings, Split);
+  llvm::MappedPrefix::transformPairs(PathPrefixMappings, Split);
   for (auto &MappedPrefix : Split)
     Mapper.add(MappedPrefix);
 
diff --git a/clang/test/CAS/cached-diagnostics.c b/clang/test/CAS/cached-diagnostics.c
index 4acfd43c5a264..2c5e9b696dd84 100644
--- a/clang/test/CAS/cached-diagnostics.c
+++ b/clang/test/CAS/cached-diagnostics.c
@@ -5,7 +5,7 @@
 // RUN: %clang_cc1 -triple x86_64-apple-macos12 -fsyntax-only %t/src/main.c -I %t/src/inc -Wunknown-pragmas 2> %t/regular-diags1.txt
 
 // RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t1.rsp -cc1-args \
-// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src=/^src \
+// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map %t/src /^src \
 // RUN:     -emit-obj %t/src/main.c -o %t/out/output.o -I %t/src/inc -Wunknown-pragmas
 
 // Compare diagnostics after a miss.
@@ -42,7 +42,7 @@
 // RUN: %clang_cc1 -triple x86_64-apple-macos12 -fsyntax-only %t/src2/main.c -I %t/src2/inc -Wunknown-pragmas 2> %t/regular-diags2.txt
 
 // RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t2.rsp -cc1-args \
-// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src2=/^src \
+// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map %t/src2 /^src \
 // RUN:     -emit-obj %t/src2/main.c -o %t/out2/output.o -I %t/src2/inc -Wunknown-pragmas
 // RUN: %clang @%t/t2.rsp -Rcompile-job-cache 2> %t/diags-hit2.txt
 
@@ -55,7 +55,7 @@
 // RUN: diff -u %t/regular-diags2.txt %t/cached-diags2.txt
 
 // RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/terr.rsp -cc1-args \
-// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src=/^src \
+// RUN:   -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map %t/src /^src \
 // RUN:     -emit-obj %t/src/main.c -o %t/out/output.o -I %t/src/inc -Rcompile-job-cache -DERROR
 
 // RUN: not %clang @%t/terr.rsp -ferror-limit 1 2> %t/diags_error1.txt
diff --git a/clang/test/CAS/depscan-prefix-map.c b/clang/test/CAS/depscan-prefix-map.c
index 570e9e59e7d0d..51f2364a63a38 100644
--- a/clang/test/CAS/depscan-prefix-map.c
+++ b/clang/test/CAS/depscan-prefix-map.c
@@ -11,11 +11,11 @@
 // RUN:              -internal-isystem %S/Inputs/toolchain_dir/usr/lib/clang/1000/include \
 // RUN:              -working-directory %t.d                              \
 // RUN:              -fcas-path %t.d/cas                                  \
-// RUN:              -fdepscan-prefix-map=%S=/^source                     \
-// RUN:              -fdepscan-prefix-map=%t.d=/^testdir                  \
-// RUN:              -fdepscan-prefix-map=%{objroot}=/^objroot            \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk             \
+// RUN:              -fdepscan-prefix-map %S /^source                     \
+// RUN:              -fdepscan-prefix-map %t.d /^testdir                  \
+// RUN:              -fdepscan-prefix-map %{objroot} /^objroot            \
+// RUN:              -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:              -fdepscan-prefix-map %S/Inputs/SDK /^sdk             \
 // RUN:              -fdepfile-entry=%t.d/extra                           \
 // RUN: | FileCheck %s -DPREFIX=%t.d
 // RUN: %clang -cc1depscan -dump-depscan-tree=%t.root -fdepscan=inline    \
@@ -25,11 +25,11 @@
 // RUN:              -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:              -working-directory %t.d                              \
 // RUN:              -fcas-path %t.d/cas                                  \
-// RUN:              -fdepscan-prefix-map=%S=/^source                     \
-// RUN:              -fdepscan-prefix-map=%t.d=/^testdir                  \
-// RUN:              -fdepscan-prefix-map=%{objroot}=/^objroot            \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk             \
+// RUN:              -fdepscan-prefix-map %S /^source                     \
+// RUN:              -fdepscan-prefix-map %t.d /^testdir                  \
+// RUN:              -fdepscan-prefix-map %{objroot} /^objroot            \
+// RUN:              -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:              -fdepscan-prefix-map %S/Inputs/SDK /^sdk             \
 // RUN:              -fdepfile-entry=%t.d/extra                           \
 // RUN: | FileCheck %s -DPREFIX=%t.d
 // RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t      \
@@ -42,11 +42,11 @@
 // RUN:              -internal-isystem %S/Inputs/toolchain_dir/usr/lib/clang/1000/include \
 // RUN:              -working-directory %t.d                              \
 // RUN:              -fcas-path %t.d/cas                                  \
-// RUN:              -fdepscan-prefix-map=%S=/^source                     \
-// RUN:              -fdepscan-prefix-map=%t.d=/^testdir                  \
-// RUN:              -fdepscan-prefix-map=%{objroot}=/^objroot            \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:              -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk             \
+// RUN:              -fdepscan-prefix-map %S /^source                     \
+// RUN:              -fdepscan-prefix-map %t.d /^testdir                  \
+// RUN:              -fdepscan-prefix-map %{objroot} /^objroot            \
+// RUN:              -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:              -fdepscan-prefix-map %S/Inputs/SDK /^sdk             \
 // RUN:              -fdepfile-entry=%t.d/extra                           \
 // RUN: | FileCheck %s -DPREFIX=%t.d
 //
diff --git a/clang/test/CAS/driver-cache-launcher.c b/clang/test/CAS/driver-cache-launcher.c
index 121a8245e53f7..b9ef76d6f2ac1 100644
--- a/clang/test/CAS/driver-cache-launcher.c
+++ b/clang/test/CAS/driver-cache-launcher.c
@@ -98,15 +98,15 @@
 // SESSION-CMAKE-PREFIX: note: setting LLVM_CACHE_BUILD_SESSION_ID=
 
 // CLANG-CMAKE-PREFIX: "-cc1depscan" "-fdepscan=daemon" "-fdepscan-share-identifier"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=[[INPUTS]]/SDK=/^sdk"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=[[INPUTS]]/toolchain_dir=/^toolchain"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/build=/^build"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/llvm=/^src"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/clang=/^src-clang"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/clang-tools-extra=/^src-clang-tools-extra"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/third-party/benchmark=/^src-benchmark"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/other/benchmark=/^src-benchmark-1"
-// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/another/benchmark=/^src-benchmark-2"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "[[INPUTS]]/SDK" "/^sdk"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "[[INPUTS]]/toolchain_dir" "/^toolchain"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/build" "/^build"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/llvm" "/^src"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/clang" "/^src-clang"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/clang-tools-extra" "/^src-clang-tools-extra"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/third-party/benchmark" "/^src-benchmark"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/other/benchmark" "/^src-benchmark-1"
+// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map" "/llvm/llvm-project/another/benchmark" "/^src-benchmark-2"
 
 // Make sure `cache-build-session` can invoke an executable script.
 // RUN: cache-build-session %t/clang -c %s -o %t.o 2>&1 | FileCheck %s -check-prefix=SESSION-SCRIPT -DSRC=%s -DPREFIX=%t
diff --git a/clang/test/CAS/fcas-include-tree-prefix-mapping.c b/clang/test/CAS/fcas-include-tree-prefix-mapping.c
index 088b8af61480d..4eaca1a762504 100644
--- a/clang/test/CAS/fcas-include-tree-prefix-mapping.c
+++ b/clang/test/CAS/fcas-include-tree-prefix-mapping.c
@@ -7,9 +7,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src /^src -fdepscan-prefix-map %t/out /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \
 // RUN:       -emit-llvm %t/src/main.c -o %t/out/output.ll -include %t/src/prefix.h -I %t/src/inc \
 // RUN:       -MT deps -dependency-file %t/t1.d
@@ -47,9 +47,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src2 /^src -fdepscan-prefix-map %t/out2 /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \
 // RUN:       -emit-llvm %t/src2/main.c -o %t/out2/output.ll -include %t/src2/prefix.h -I %t/src2/inc \
 // RUN:       -MT deps -dependency-file %t/t2.d
@@ -85,9 +85,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src /^src -fdepscan-prefix-map %t/out /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \
 // RUN:       -emit-pch -x c-header %t/src/prefix.h -o %t/out/prefix.h.pch -include %t/src/prefix.h -I %t/src/inc
 // RUN: %clang @%t/pch1.rsp
@@ -98,9 +98,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas2 -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src2 /^src -fdepscan-prefix-map %t/out2 /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \
 // RUN:       -emit-pch -x c-header %t/src2/prefix.h -o %t/out2/prefix.h.pch -include %t/src2/prefix.h -I %t/src2/inc
 // RUN: %clang @%t/pch2.rsp
@@ -112,9 +112,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src /^src -fdepscan-prefix-map %t/out /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \
 // RUN:       -emit-obj %t/src/main.c -o %t/out/main.o -include-pch %t/out/prefix.h.pch -I %t/src/inc \
 // RUN:       -MT deps -dependency-file %t/t1.pch.d
@@ -129,9 +129,9 @@
 // RUN:     -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \
 // RUN:       -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \
 // RUN:       -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \
-// RUN:       -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \
-// RUN:       -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \
+// RUN:       -fdepscan-prefix-map %t/src2 /^src -fdepscan-prefix-map %t/out2 /^out \
+// RUN:       -fdepscan-prefix-map %S/Inputs/toolchain_dir /^toolchain \
+// RUN:       -fdepscan-prefix-map %S/Inputs/SDK /^sdk \
 // RUN:       -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \
 // RUN:       -emit-obj %t/src2/main.c -o %t/out2/main.o -include-pch %t/out2/prefix.h.pch -I %t/src2/inc \
 // RUN:       -MT deps -dependency-file %t/t2.pch.d
diff --git a/clang/test/CAS/pgo-profile.c b/clang/test/CAS/pgo-profile.c
index 387b3e8fa7f70..cf0f024f2aae0 100644
--- a/clang/test/CAS/pgo-profile.c
+++ b/clang/test/CAS/pgo-profile.c
@@ -37,9 +37,9 @@
 // RUN: mkdir -p %t.dir/a && mkdir -p %t.dir/b
 // RUN: cp %t.profdata %t.dir/a/a.profdata
 // RUN: cp %t.profdata %t.dir/b/a.profdata
-// RUN: %clang -cc1depscan -fdepscan=inline -o %t4.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/a=/^testdir  \
+// RUN: %clang -cc1depscan -fdepscan=inline -o %t4.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map %t.dir/a /^testdir  \
 // RUN:   -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/a/a.profdata
-// RUN: %clang -cc1depscan -fdepscan=inline -o %t5.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/b=/^testdir \
+// RUN: %clang -cc1depscan -fdepscan=inline -o %t5.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map %t.dir/b /^testdir \
 // RUN:   -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/b/a.profdata
 // RUN: cat %t4.rsp | FileCheck %s --check-prefix=REMAP
 // RUN: %clang @%t4.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS
@@ -54,9 +54,9 @@
 // RUN: grep llvmcas %t.dir/cache-key1
 // RUN: diff -u %t.dir/cache-key1 %t.dir/cache-key2
 
-// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t4.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/a=/^testdir \
+// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t4.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map %t.dir/a /^testdir \
 // RUN:   -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/a/a.profdata
-// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t5.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/b=/^testdir \
+// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t5.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map %t.dir/b /^testdir \
 // RUN:   -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/b/a.profdata
 // RUN: cat %t4.inc.rsp | FileCheck %s --check-prefix=REMAP
 // RUN: %clang @%t4.inc.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS
diff --git a/clang/test/ClangScanDeps/include-tree-multiple-commands.c b/clang/test/ClangScanDeps/include-tree-multiple-commands.c
index 9fd17903fada3..e13a42f8d7727 100644
--- a/clang/test/ClangScanDeps/include-tree-multiple-commands.c
+++ b/clang/test/ClangScanDeps/include-tree-multiple-commands.c
@@ -218,7 +218,7 @@
 // CHECK-LIBCLANG-NEXT:       [[SRC]]/header.h
 // CHECK-LIBCLANG-NOT:             -fcas-input-file-cache-key
 // CHECK-LIBCLANG-NOT:             {{.*}}tu.c
-// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.i {{.*}} -E -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm
+// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.i {{.*}} -E -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] {{.*}} -x c {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm
 // CHECK-LIBCLANG-NEXT:   command 1:
 // CHECK-LIBCLANG-NEXT:     context-hash: {{.*}}
 // FIXME: This should be empty.
@@ -231,7 +231,7 @@
 // CHECK-LIBCLANG-NEXT:       [[SRC]]/header.h
 // CHECK-LIBCLANG-NOT:                  -fcas-include-tree
 // CHECK-LIBCLANG-NOT:                  {{.*}}tu.i
-// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.bc {{.*}} -fcas-input-file-cache-key [[CPP_CACHE_KEY]] {{.*}} -emit-llvm-bc -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c-cpp-output {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm
+// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.bc {{.*}} -fcas-input-file-cache-key [[CPP_CACHE_KEY]] {{.*}} -emit-llvm-bc -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] {{.*}} -x c-cpp-output {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm
 // CHECK-LIBCLANG-NEXT:   command 2:
 // CHECK-LIBCLANG-NEXT:     context-hash: {{.*}}
 // FIXME: This should be empty.
@@ -243,7 +243,7 @@
 // CHECK-LIBCLANG-NEXT:     file-deps:
 // CHECK-LIBCLANG-NEXT:       [[SRC]]/tu.c
 // CHECK-LIBCLANG-NEXT:       [[SRC]]/header.h
-// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.s {{.*}} -fcas-input-file-cache-key [[COMPILER_CACHE_KEY]] {{.*}} -S -x ir
+// CHECK-LIBCLANG-NEXT:     build-args: -cc1 {{.*}} -o [[DST]]/tu.s {{.*}} -fcas-input-file-cache-key [[COMPILER_CACHE_KEY]] {{.*}} -S {{.*}} -x ir
 // CHECK-LIBCLANG-NEXT:   command 3:
 // CHECK-LIBCLANG-NEXT:     context-hash: {{.*}}
 // FIXME: This should be empty.
diff --git a/clang/test/Driver/fdepscan-prefix-map-sdk.c b/clang/test/Driver/fdepscan-prefix-map-sdk.c
index 421148c1a1a6d..b2c61a6131a83 100644
--- a/clang/test/Driver/fdepscan-prefix-map-sdk.c
+++ b/clang/test/Driver/fdepscan-prefix-map-sdk.c
@@ -5,4 +5,4 @@
 
 // RUN: %clang -fdepscan-prefix-map-sdk=/^sdk -isysroot /sys/path -### %s 2>&1 | FileCheck %s
 // RUN: %clang -fdepscan-prefix-map-sdk=/^sdk --sysroot /sys/path -### %s 2>&1 | FileCheck %s
-// CHECK: -fdepscan-prefix-map=/sys/path=/^sdk
+// CHECK: "-fdepscan-prefix-map" "/sys/path" "/^sdk"
diff --git a/clang/test/Driver/fdepscan-prefix-map-toolchain.c b/clang/test/Driver/fdepscan-prefix-map-toolchain.c
index 1bdbdfe1ccd78..4f2853583840a 100644
--- a/clang/test/Driver/fdepscan-prefix-map-toolchain.c
+++ b/clang/test/Driver/fdepscan-prefix-map-toolchain.c
@@ -8,8 +8,8 @@
 // RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /tc/lib/clang/10 -### %s 2>&1 | FileCheck %s
 // RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /tc/usr/lib/clang/10 -### %s 2>&1 | FileCheck %s
 
-// CHECK: -fdepscan-prefix-map=/tc=/^tc
+// CHECK: "-fdepscan-prefix-map" "/tc" "/^tc"
 
 // Implicit resource-dir
 // RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -### %s 2>&1 | FileCheck %s -check-prefix=CHECK_IMPLICIT
-// CHECK_IMPLICIT: -fdepscan-prefix-map={{.*}}=/^tc
+// CHECK_IMPLICIT: "-fdepscan-prefix-map" "{{.*}}" "/^tc"
diff --git a/clang/test/Driver/fdepscan-prefix-map.c b/clang/test/Driver/fdepscan-prefix-map.c
index 0a4bc473ef1d6..d752e09b49d13 100644
--- a/clang/test/Driver/fdepscan-prefix-map.c
+++ b/clang/test/Driver/fdepscan-prefix-map.c
@@ -4,5 +4,8 @@
 // RUN: not %clang -fdepscan-prefix-map=/=/^bad -### %s 2>&1 | FileCheck %s -check-prefix=INVALID
 // INVALID: error: invalid argument '{{.*}}/^bad' to -fdepscan-prefix-map=
 
-// RUN: %clang -fdepscan-prefix-map=/good=/^good -### %s 2>&1 | FileCheck %s
-// CHECK: "-fdepscan-prefix-map=/good=/^good"
+// RUN: %clang -fdepscan-prefix-map=/good=/^good -### %s 2>&1 | FileCheck --check-prefix CHECK_CORRECT %s
+// CHECK_CORRECT: "-fdepscan-prefix-map" "/good" "/^good"
+
+// RUN %clang -fdepscan-prefix-map=/a=/^a -fdepscan-prefix-map /b /^b -fdepscan-prefix-map=/c=/^c -fdepscan-prefix-map /d /^d -### %s 2>&1 | FileCheck --check-prefix=CHECK_MIXED %s
+// CHECK_MIXED: "-fdepscan-prefix-map" "/a" "/^a" "-fdepscan-prefix-map" "/b" "/^b" "-fdepscan-prefix-map" "/c" "/^c" "-fdepscan-prefix-map" "/d" "/^d"
diff --git a/llvm/include/llvm/Support/PrefixMapper.h b/llvm/include/llvm/Support/PrefixMapper.h
index 2e306ea8b5c20..69782e4e35ea4 100644
--- a/llvm/include/llvm/Support/PrefixMapper.h
+++ b/llvm/include/llvm/Support/PrefixMapper.h
@@ -11,6 +11,7 @@
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Path.h"
 #include <optional>
@@ -48,6 +49,8 @@ struct MappedPrefix {
                                      SmallVectorImpl<MappedPrefix> &Split);
   static void transformJoinedIfValid(ArrayRef<std::string> Joined,
                                      SmallVectorImpl<MappedPrefix> &Split);
+  static void transformPairs(ArrayRef<std::pair<std::string, std::string>> Pairs,
+                             SmallVectorImpl<MappedPrefix> & Split);
 };
 
 /// Remap path prefixes.
diff --git a/llvm/lib/Support/PrefixMapper.cpp b/llvm/lib/Support/PrefixMapper.cpp
index 1ca562dfcb783..7105301e539a3 100644
--- a/llvm/lib/Support/PrefixMapper.cpp
+++ b/llvm/lib/Support/PrefixMapper.cpp
@@ -70,6 +70,13 @@ void MappedPrefix::transformJoinedIfValid(
   transformJoinedImpl</*StopOnInvalid*/ false>(Joined, Split);
 }
 
+void MappedPrefix::transformPairs(
+    ArrayRef<std::pair<std::string, std::string>> Pairs, SmallVectorImpl<MappedPrefix> &Split) {
+  for (auto &Pair : Pairs) {
+    Split.push_back(MappedPrefix{Pair.first, Pair.second});
+  }
+}
+
 /// FIXME: Copy/pasted from llvm/lib/Support/Path.cpp.
 static bool startsWith(StringRef Path, StringRef Prefix,
                        sys::path::Style PathStyle) {
diff --git a/llvm/unittests/Support/PrefixMapperTest.cpp b/llvm/unittests/Support/PrefixMapperTest.cpp
index 1ba281574a102..251ba48082436 100644
--- a/llvm/unittests/Support/PrefixMapperTest.cpp
+++ b/llvm/unittests/Support/PrefixMapperTest.cpp
@@ -125,6 +125,21 @@ TEST(MappedPrefixTest, transformJoinedIfValid) {
   EXPECT_EQ(ArrayRef(ExpectedSplit), ArrayRef(ComputedSplit));
 }
 
+TEST(PrefixMapperTest, transformPairs) {
+  SmallVector<std::pair<std::string, std::string>> PrefixMappings = {
+    {"", ""}, {"a", "b"}, {"", "a"}, {"a", ""}, {"=a=b=", "=c=d==="}
+  };
+  MappedPrefix ExpectedSplit[] = {
+    MappedPrefix{"", ""}, MappedPrefix{"a", "b"},
+    MappedPrefix{"", "a"}, MappedPrefix{"a", ""},
+    MappedPrefix{"=a=b=", "=c=d==="},
+  };
+
+  SmallVector<MappedPrefix> ComputedSplit;
+  MappedPrefix::transformPairs(PrefixMappings, ComputedSplit);
+  EXPECT_EQ(ArrayRef(ExpectedSplit), ArrayRef(ComputedSplit));
+}
+
 TEST(PrefixMapperTest, construct) {
   for (auto PathStyle : {
            sys::path::Style::posix,


Reviews:
user: login: benlangmuir
body: We should have a new Driver test that checks the new spelling and confirms the ordering when you mix it with the old spelling is preserved in the cc1 command line. 

state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:06:48Z
user: login: sina-mahdavi
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:27:39Z
user: login: sina-mahdavi
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:29:44Z
user: login: benlangmuir
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:32:18Z
user: login: sina-mahdavi
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:42:52Z
user: login: cachemeifyoucan
body: Current version looks good.

Can you add some lit tests to use the new flag you added and also mixing it together with the old flag?
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:55:55Z
user: login: benlangmuir
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T22:00:42Z
user: login: sina-mahdavi
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T22:06:54Z
user: login: benlangmuir
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T22:15:42Z
user: login: sina-mahdavi
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-22T00:55:02Z
user: login: benlangmuir
body: The implementation LGTM, so I think we just need a new driver test that checks the behaviour of mixing this with the old flag.
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-22T14:54:55Z
user: login: benlangmuir
body: LGTM assuming PR tests don't show any related failures. Thanks!
state: APPROVED
author_association: NONE
submitted_at: 2025-05-22T16:58:45Z

Review comments:
diff_hunk: @@ -5093,6 +5093,18 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
       A->render(Args, CmdArgs);
     }
   }
+
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: benlangmuir
body: We should make a single loop that handles both `OPT_fdepscan_prefix_map` and `OPT_fdepscan_prefix_map_EQ` so that the order is preserved with either spelling.
created_at: 2025-05-21T20:55:36Z
author_association: NONE
diff_hunk: @@ -3381,6 +3385,19 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
path: clang/lib/Frontend/CompilerInvocation.cpp
user: login: benlangmuir
body: Same issue here, it needs to be in a single loop combining both spellings, or this issue would become moot if you take my suggestion to make the `_EQ` version driver-only.
created_at: 2025-05-21T20:58:52Z
author_association: NONE
diff_hunk: @@ -3381,6 +3385,19 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
+    ArrayRef<const char *> Values = A->getValues();
+    assert(Values.size() == 2);
+    Opts.PathPrefixMappings.emplace_back(Values[0], Values[1]);
+  }
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map_EQ)) {
+    StringRef Val = A->getValue();
+    if (std::optional<llvm::MappedPrefix> mapping = llvm::MappedPrefix::getFromJoined(Val)) {
path: clang/lib/Frontend/CompilerInvocation.cpp
user: login: benlangmuir
body: This should probably have a diagnostic if it fails. I guess this was already a problem before this patch though?  Also moot if we make it driver-only.
created_at: 2025-05-21T21:00:33Z
author_association: NONE
diff_hunk: @@ -8839,8 +8839,12 @@ def fdepscan_prefix_map_EQ : Joined<["-"], "fdepscan-prefix-map=">,
     Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
     MetaVarName<"<old>=<new>">,
     HelpText<"With -fdepscan, seamlessly filter the CAS filesystem to"
-             " apply the given prefix, updating the command-line to match.">,
-    MarshallingInfoStringVector<FrontendOpts<"PathPrefixMappings">>;
+             " apply the given prefix, updating the command-line to match.">;
+def fdepscan_prefix_map : MultiArg<["-"], "fdepscan-prefix-map", 2>,
path: clang/include/clang/Driver/Options.td
user: login: benlangmuir
body: I suggest we remove `CC1Option` from `fdepscan_prefix_map_EQ` above and only support it in the driver.
created_at: 2025-05-21T21:05:07Z
author_association: NONE
diff_hunk: @@ -3381,6 +3385,19 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
path: clang/lib/Frontend/CompilerInvocation.cpp
user: login: sina-mahdavi
body: So if I make the `_EQ` version driver-only, I can remove the loop for `_EQ` in this file?
created_at: 2025-05-21T21:27:38Z
author_association: NONE
diff_hunk: @@ -3381,6 +3385,19 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
path: clang/lib/Frontend/CompilerInvocation.cpp
user: login: sina-mahdavi
body: Oh I see, yes we're already only using the non-`_EQ` variant to pass args to the frontend in `GenerateFrontendArgs`, so no need to parse the `_EQ` variant in `parseFrontendArgs` any more if we make it driver-only.
created_at: 2025-05-21T21:29:44Z
author_association: NONE
diff_hunk: @@ -3381,6 +3385,19 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
     Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]);
   }
 
+  // Handle path prefix mappings
+  for (const Arg *A : Args.filtered(OPT_fdepscan_prefix_map)) {
path: clang/lib/Frontend/CompilerInvocation.cpp
user: login: benlangmuir
body: Exactly 😸 
created_at: 2025-05-21T21:32:18Z
author_association: NONE
diff_hunk: @@ -5093,6 +5093,18 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
       A->render(Args, CmdArgs);
     }
   }
+
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: sina-mahdavi
body: I implemented this in my recent commit, could you please check that? 😄 
created_at: 2025-05-21T21:42:52Z
author_association: NONE
diff_hunk: @@ -5082,11 +5082,17 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) {
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map, options::OPT_fdepscan_prefix_map_EQ)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: benlangmuir
body: I think this will still produce the old spelling in the cc1 args, because of the use of `A->render()` below.  I think we want something like this (untested) for converting the old spelling to the new spelling:

```c++
CmdArgs.push_back("-fdepscan-prefix-map");
CmdArgs.push_back(Args.MakeArgString(Prefix));
CmdArgs.push_back(Args.MakeArgString(MapTarget));
```
created_at: 2025-05-21T21:59:50Z
author_association: NONE
diff_hunk: @@ -5082,11 +5082,17 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) {
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map, options::OPT_fdepscan_prefix_map_EQ)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: sina-mahdavi
body: Is there a way to change the render style of the argument to Option::RenderSeparateStyle in the td file? That looks like it's emitting the format we want?
created_at: 2025-05-21T22:06:54Z
author_association: NONE
diff_hunk: @@ -5082,11 +5082,17 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) {
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map, options::OPT_fdepscan_prefix_map_EQ)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: benlangmuir
body: Good question, I see there is this flag:
```
// RenderSeparate - The option should be rendered separately, even if joined
// (only sensible on joined options).
def RenderSeparate : OptionFlag;
```
created_at: 2025-05-21T22:15:42Z
author_association: NONE
diff_hunk: @@ -5082,11 +5082,17 @@ void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs,
     }
   }
 
-  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) {
+  for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map, options::OPT_fdepscan_prefix_map_EQ)) {
path: clang/lib/Driver/ToolChains/Clang.cpp
user: login: sina-mahdavi
body: Alright this should be fixed now :) `RenderSeparate` alone didn't end up working because it still considers the `<old>=<new>` to be one value for the arg and `render` will just copy that, so instead we have to manually split it and add it to `CmdArgs` as you said.
created_at: 2025-05-22T00:55:02Z
author_association: NONE

PR comments:
user: login: akyrtzi
created_at: 2025-05-21T20:10:57Z
body: @swift-ci Please test LLVM
author_association: NONE
user: login: sina-mahdavi
created_at: 2025-05-22T00:58:38Z
body: There are some `check-clang-clangscandeps` tests failing, and most of that comes from `clang-scan-deps` and the error message is just `unknown argument: -fdepscan-prefix-map=...`. I think there's some other place where the old format is being emitted that I also need to change. Edit: Yes, `-fdepscan-prefix-map-sdk` and `-fdepscan-prefix-map-toolchain` are still being translated to the old format.
author_association: NONE
user: login: benlangmuir
created_at: 2025-05-22T16:55:08Z
body: @swift-ci please test llvm
author_association: NONE
user: login: sina-mahdavi
created_at: 2025-05-22T20:16:36Z
body: > LGTM assuming PR tests don't show any related failures. Thanks!

I think there's some tests failing in `check-clang-cas` most of which seems like false positives because the inputs are directly running frontend commands with the old option format. I'm editing those now.

Edit: should be fixed now, `check-clang-cas` is passing locally for me.
author_association: NONE
user: login: sina-mahdavi
created_at: 2025-05-22T20:20:43Z
body: @swift-ci please test llvm
author_association: NONE
user: login: sina-mahdavi
created_at: 2025-05-22T22:20:27Z
body: Alright, I think the tests are okay now. The only failing tests are in `Clang :: CodeGen` and `LLVM :: Transforms` which I'm assuming are not related to this PR (+ I've Cmd-F'd their test files and they don't include any reference to `fdepscan-prefix-map`).
author_association: NONE