diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -143,8 +143,8 @@ "missing argument to '%0'">; def err_drv_invalid_libcxx_deployment : Error< "invalid deployment target for -stdlib=libc++ (requires %0 or later)">; -def err_drv_invalid_argument_to_fdebug_prefix_map : Error< - "invalid argument '%0' to -fdebug-prefix-map">; +def err_drv_invalid_argument_to_option : Error< + "invalid argument '%0' to -%1">; def err_drv_malformed_sanitizer_blacklist : Error< "malformed sanitizer blacklist: '%0'">; def err_drv_duplicate_config : Error< diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1870,10 +1870,16 @@ Flags<[CC1Option]>, HelpText<"Provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF">; def fno_split_dwarf_inlining: Flag<["-"], "fno-split-dwarf-inlining">, Group, Flags<[CC1Option]>; +def ffile_prefix_map_EQ + : Joined<["-"], "ffile-prefix-map=">, Group, Flags<[CC1Option]>, + HelpText<"remap file source paths in debug info and predefined preprocessor macros">; def fdebug_prefix_map_EQ : Joined<["-"], "fdebug-prefix-map=">, Group, Flags<[CC1Option,CC1AsOption]>, HelpText<"remap file source paths in debug info">; +def fmacro_prefix_map_EQ + : Joined<["-"], "fmacro-prefix-map=">, Group, Flags<[CC1Option]>, + HelpText<"remap file source paths in predefined preprocessor macros">; def g_Flag : Flag<["-"], "g">, Group, HelpText<"Generate source-level debug information">; def gline_tables_only : Flag<["-"], "gline-tables-only">, Group, diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -169,6 +169,9 @@ /// build it again. std::shared_ptr FailedModules; + /// A prefix map for __FILE__ and __BASE_FILE__ + std::map> MacroPrefixMap; + public: PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {} diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -83,7 +83,7 @@ /// Cache of previously constructed Types. llvm::DenseMap TypeCache; - llvm::SmallDenseMap DebugPrefixMap; + std::map> DebugPrefixMap; /// Cache that maps VLA types to size expressions for that type, /// represented by instantiated Metadata nodes. diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -475,10 +475,13 @@ } std::string CGDebugInfo::remapDIPath(StringRef Path) const { + SmallString<256> p = Path; for (const auto &Entry : DebugPrefixMap) - if (Path.startswith(Entry.first)) - return (Twine(Entry.second) + Path.substr(Entry.first.size())).str(); - return Path.str(); + if (llvm::sys::path::replace_path_prefix(p, Entry.first, Entry.second, true)) + break; + while (llvm::sys::path::is_separator(p.back())) + p.set_size(p.size() - 1); + return p.str(); } unsigned CGDebugInfo::getLineNumber(SourceLocation Loc) { @@ -607,7 +610,7 @@ if (CSKind) CSInfo.emplace(*CSKind, Checksum); llvm::DIFile *CUFile = DBuilder.createFile( - remapDIPath(MainFileName), remapDIPath(getCurrentDirname()), CSInfo, + MainFileName, remapDIPath(getCurrentDirname()), CSInfo, getSource(SM, SM.getMainFileID())); // Create new compile unit. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -611,16 +611,30 @@ /// Add a CC1 and CC1AS option to specify the debug file path prefix map. static void addDebugPrefixMapArg(const Driver &D, const ArgList &Args, ArgStringList &CmdArgs) { - for (const Arg *A : Args.filtered(options::OPT_fdebug_prefix_map_EQ)) { + for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ, + options::OPT_fdebug_prefix_map_EQ)) { StringRef Map = A->getValue(); if (Map.find('=') == StringRef::npos) - D.Diag(diag::err_drv_invalid_argument_to_fdebug_prefix_map) << Map; + D.Diag(diag::err_drv_invalid_argument_to_option) << Map << A->getOption().getName(); else CmdArgs.push_back(Args.MakeArgString("-fdebug-prefix-map=" + Map)); A->claim(); } } +/// Add a CC1 and CC1AS option to specify the macro file path prefix map. +static void addMacroPrefixMapArg(const Driver &D, const ArgList &Args, ArgStringList &CmdArgs) { + for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ, + options::OPT_fmacro_prefix_map_EQ)) { + StringRef Map = A->getValue(); + if (Map.find('=') == StringRef::npos) + D.Diag(diag::err_drv_invalid_argument_to_option) << Map << A->getOption().getName(); + else + CmdArgs.push_back(Args.MakeArgString("-fmacro-prefix-map=" + Map)); + A->claim(); + } +} + /// Vectorize at all optimization levels greater than 1 except for -Oz. /// For -Oz the loop vectorizer is disabled, while the slp vectorizer is /// enabled. @@ -1305,6 +1319,8 @@ // For IAMCU add special include arguments. getToolChain().AddIAMCUIncludeArgs(Args, CmdArgs); } + + addMacroPrefixMapArg(D, Args, CmdArgs); } // FIXME: Move to target hook. diff --git a/clang/lib/Driver/ToolChains/FreeBSD.cpp b/clang/lib/Driver/ToolChains/FreeBSD.cpp --- a/clang/lib/Driver/ToolChains/FreeBSD.cpp +++ b/clang/lib/Driver/ToolChains/FreeBSD.cpp @@ -12,6 +12,7 @@ #include "Arch/Sparc.h" #include "CommonArgs.h" #include "clang/Driver/Compilation.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "llvm/Option/ArgList.h" @@ -30,6 +31,7 @@ const char *LinkingOutput) const { claimNoWarnArgs(Args); ArgStringList CmdArgs; + const auto &D = getToolChain().getDriver(); // When building 32-bit code on FreeBSD/amd64, we have to explicitly // instruct as in the base system to assemble 32-bit code. @@ -103,6 +105,18 @@ } } + for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ, + options::OPT_fdebug_prefix_map_EQ)) { + StringRef Map = A->getValue(); + if (Map.find('=') == StringRef::npos) + D.Diag(diag::err_drv_invalid_argument_to_option) << Map << A->getOption().getName(); + else { + CmdArgs.push_back(Args.MakeArgString("--debug-prefix-map")); + CmdArgs.push_back(Args.MakeArgString(Map)); + } + A->claim(); + } + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); CmdArgs.push_back("-o"); diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -868,6 +868,18 @@ } } + for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ, + options::OPT_fdebug_prefix_map_EQ)) { + StringRef Map = A->getValue(); + if (Map.find('=') == StringRef::npos) + D.Diag(diag::err_drv_invalid_argument_to_option) << Map << A->getOption().getName(); + else { + CmdArgs.push_back(Args.MakeArgString("--debug-prefix-map")); + CmdArgs.push_back(Args.MakeArgString(Map)); + } + A->claim(); + } + Args.AddAllArgs(CmdArgs, options::OPT_I); Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3205,6 +3205,9 @@ for (const auto *A : Args.filtered(OPT_error_on_deserialized_pch_decl)) Opts.DeserializedPCHDeclsToErrorOn.insert(A->getValue()); + for (const auto &A : Args.getAllArgValues(OPT_fmacro_prefix_map_EQ)) + Opts.MacroPrefixMap.insert(StringRef(A).split('=')); + if (const Arg *A = Args.getLastArg(OPT_preamble_bytes_EQ)) { StringRef Value(A->getValue()); size_t Comma = Value.find(','); diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -28,6 +28,7 @@ #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorLexer.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Lex/Token.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" @@ -1436,6 +1437,15 @@ return TI.getTriple().getEnvironment() == Env.getEnvironment(); } +static void remapMacroPath( + SmallString<256> &Path, + const std::map> + &MacroPrefixMap) { + for (const auto &Entry : MacroPrefixMap) + if (llvm::sys::path::replace_path_prefix(Path, Entry.first, Entry.second, true)) + break; +} + /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded /// as a builtin macro, handle it and return the next token as 'Tok'. void Preprocessor::ExpandBuiltinMacro(Token &Tok) { @@ -1502,7 +1512,7 @@ } // Escape this filename. Turn '\' -> '\\' '"' -> '\"' - SmallString<128> FN; + SmallString<256> FN; if (PLoc.isValid()) { // __FILE_NAME__ is a Clang-specific extension that expands to the // the last part of __FILE__. @@ -1518,6 +1528,7 @@ FN += PLoc.getFilename(); } Lexer::Stringify(FN); + remapMacroPath(FN, PPOpts->MacroPrefixMap); OS << '"' << FN << '"'; } Tok.setKind(tok::string_literal); diff --git a/clang/test/CodeGen/debug-prefix-map.c b/clang/test/CodeGen/debug-prefix-map.c --- a/clang/test/CodeGen/debug-prefix-map.c +++ b/clang/test/CodeGen/debug-prefix-map.c @@ -2,6 +2,8 @@ // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH=empty %s -emit-llvm -o - | FileCheck %s -check-prefix CHECK-EVIL // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty %s -emit-llvm -o - -main-file-name debug-prefix-map.c | FileCheck %s // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty %s -emit-llvm -o - -fdebug-compilation-dir %p | FileCheck %s -check-prefix CHECK-COMPILATION-DIR +// RUN: %clang -g -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty -S -c %s -emit-llvm -o - | FileCheck %s +// RUN: %clang -g -ffile-prefix-map=%p=/UNLIKELY_PATH/empty -S -c %s -emit-llvm -o - | FileCheck %s #include "Inputs/stdio.h" diff --git a/clang/test/Driver/debug-prefix-map.S b/clang/test/Driver/debug-prefix-map.S --- a/clang/test/Driver/debug-prefix-map.S +++ b/clang/test/Driver/debug-prefix-map.S @@ -1,4 +1,5 @@ // RUN: %clang -### -g -fdebug-prefix-map=old=new %s 2>&1 | FileCheck %s +// RUN: %clang -### -g -ffile-prefix-map=old=new %s 2>&1 | FileCheck %s // CHECK: cc1as // CHECK-SAME: -fdebug-prefix-map=old=new diff --git a/clang/test/Driver/debug-prefix-map.c b/clang/test/Driver/debug-prefix-map.c --- a/clang/test/Driver/debug-prefix-map.c +++ b/clang/test/Driver/debug-prefix-map.c @@ -1,9 +1,28 @@ -// RUN: %clang -### -fdebug-prefix-map=old %s 2>&1 | FileCheck %s -check-prefix CHECK-INVALID -// RUN: %clang -### -fdebug-prefix-map=old=new %s 2>&1 | FileCheck %s -check-prefix CHECK-SIMPLE -// RUN: %clang -### -fdebug-prefix-map=old=n=ew %s 2>&1 | FileCheck %s -check-prefix CHECK-COMPLEX -// RUN: %clang -### -fdebug-prefix-map=old= %s 2>&1 | FileCheck %s -check-prefix CHECK-EMPTY - -// CHECK-INVALID: error: invalid argument 'old' to -fdebug-prefix-map -// CHECK-SIMPLE: fdebug-prefix-map=old=new -// CHECK-COMPLEX: fdebug-prefix-map=old=n=ew -// CHECK-EMPTY: fdebug-prefix-map=old= +// RUN: %clang -### -fdebug-prefix-map=old %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-INVALID +// RUN: %clang -### -fmacro-prefix-map=old %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-INVALID +// RUN: %clang -### -ffile-prefix-map=old %s 2>&1 | FileCheck %s -check-prefix CHECK-FILE-INVALID + +// RUN: %clang -### -fdebug-prefix-map=old=new %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-SIMPLE +// RUN: %clang -### -fmacro-prefix-map=old=new %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-SIMPLE +// RUN: %clang -### -ffile-prefix-map=old=new %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-SIMPLE +// RUN: %clang -### -ffile-prefix-map=old=new %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-SIMPLE + +// RUN: %clang -### -fdebug-prefix-map=old=n=ew %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-COMPLEX +// RUN: %clang -### -fmacro-prefix-map=old=n=ew %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-COMPLEX +// RUN: %clang -### -ffile-prefix-map=old=n=ew %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-COMPLEX +// RUN: %clang -### -ffile-prefix-map=old=n=ew %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-COMPLEX + +// RUN: %clang -### -fdebug-prefix-map=old= %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-EMPTY +// RUN: %clang -### -fmacro-prefix-map=old= %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-EMPTY +// RUN: %clang -### -ffile-prefix-map=old= %s 2>&1 | FileCheck %s -check-prefix CHECK-DEBUG-EMPTY +// RUN: %clang -### -ffile-prefix-map=old= %s 2>&1 | FileCheck %s -check-prefix CHECK-MACRO-EMPTY + +// CHECK-DEBUG-INVALID: error: invalid argument 'old' to -fdebug-prefix-map +// CHECK-MACRO-INVALID: error: invalid argument 'old' to -fmacro-prefix-map +// CHECK-FILE-INVALID: error: invalid argument 'old' to -ffile-prefix-map +// CHECK-DEBUG-SIMPLE: fdebug-prefix-map=old=new +// CHECK-MACRO-SIMPLE: fmacro-prefix-map=old=new +// CHECK-DEBUG-COMPLEX: fdebug-prefix-map=old=n=ew +// CHECK-MACRO-COMPLEX: fmacro-prefix-map=old=n=ew +// CHECK-DEBUG-EMPTY: fdebug-prefix-map=old= +// CHECK-MACRO-EMPTY: fmacro-prefix-map=old= diff --git a/clang/test/Preprocessor/file_test.h b/clang/test/Preprocessor/file_test.h new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/file_test.h @@ -0,0 +1,2 @@ +filename: __FILE__ +basefile: __BASE_FILE__ diff --git a/clang/test/Preprocessor/file_test.c b/clang/test/Preprocessor/file_test.c new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/file_test.c @@ -0,0 +1,22 @@ +// RUN: %clang -E -ffile-prefix-map=%p=/UNLIKELY_PATH/empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fmacro-prefix-map=%p=/UNLIKELY_PATH/empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fmacro-prefix-map=%p=/UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL +// RUN: %clang -E -fmacro-prefix-map=%p/= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE + +filename: __FILE__ +#include "file_test.h" + +// CHECK: filename: "/UNLIKELY_PATH/empty{{[/\\]}}file_test.c" +// CHECK: filename: "/UNLIKELY_PATH/empty{{[/\\]}}file_test.h" +// CHECK: basefile: "/UNLIKELY_PATH/empty{{[/\\]}}file_test.c" +// CHECK-NOT: filename: + +// CHECK-EVIL: filename: "/UNLIKELY_PATH=empty{{[/\\]}}file_test.c" +// CHECK-EVIL: filename: "/UNLIKELY_PATH=empty{{[/\\]}}file_test.h" +// CHECK-EVIL: basefile: "/UNLIKELY_PATH=empty{{[/\\]}}file_test.c" +// CHECK-EVIL-NOT: filename: + +// CHECK-REMOVE: filename: "file_test.c" +// CHECK-REMOVE: filename: "file_test.h" +// CHECK-REMOVE: basefile: "file_test.c" +// CHECK-REMOVE-NOT: filename: diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h --- a/llvm/include/llvm/Support/Path.h +++ b/llvm/include/llvm/Support/Path.h @@ -150,18 +150,35 @@ /// /// @code /// /foo, /old, /new => /foo +/// /old, /old, /new => /new +/// /old, /old/, /new, false => /old +/// /old, /old/, /new, true => /new /// /old/foo, /old, /new => /new/foo +/// /old/foo, /old/, /new => /new/foo +/// /old/foo, /old/, /new/ => /new/foo +/// /oldfoo, /old, /new => /oldfoo /// /foo, , /new => /new/foo -/// /old/foo, /old, => /foo +/// /foo, , new => new/foo +/// /old/foo, /old, , false => /foo +/// /old/foo, /old, , true => foo /// @endcode /// /// @param Path If \a Path starts with \a OldPrefix modify to instead /// start with \a NewPrefix. -/// @param OldPrefix The path prefix to strip from \a Path. +/// @param OldPrefix The path prefix to strip from \a Path. Any trailing +/// path separator is ignored if strict is true. /// @param NewPrefix The path prefix to replace \a NewPrefix with. -void replace_path_prefix(SmallVectorImpl &Path, +/// @param style The path separator style +/// @param strict Strict prefix path checking +/// @result true if \a Path begins with OldPrefix +bool replace_path_prefix(SmallVectorImpl &Path, const StringRef &OldPrefix, const StringRef &NewPrefix, - Style style = Style::native); + Style style = Style::native, bool strict = false); +static inline bool replace_path_prefix(SmallVectorImpl &Path, + const StringRef &OldPrefix, const StringRef &NewPrefix, + bool strict, Style style = Style::native) { + return replace_path_prefix(Path, OldPrefix, NewPrefix, style, strict); +} /// Append to path. /// diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -496,27 +496,53 @@ path.append(ext.begin(), ext.end()); } -void replace_path_prefix(SmallVectorImpl &Path, +bool replace_path_prefix(SmallVectorImpl &Path, const StringRef &OldPrefix, const StringRef &NewPrefix, - Style style) { + Style style, bool strict) { if (OldPrefix.empty() && NewPrefix.empty()) - return; + return false; StringRef OrigPath(Path.begin(), Path.size()); - if (!OrigPath.startswith(OldPrefix)) - return; + StringRef OldPrefixDir; + + if (!strict && OldPrefix.size() > OrigPath.size()) + return false; + + if (!strict && OldPrefix.size() > OrigPath.size()) + return false; + + // Ensure OldPrefixDir does not have a trailing separator. + if (!OldPrefix.empty() && is_separator(OldPrefix.back())) + OldPrefixDir = parent_path(OldPrefix, style); + else + OldPrefixDir = OldPrefix; + + if (!OrigPath.startswith(OldPrefixDir)) + return false; + + if (OrigPath.size() > OldPrefixDir.size()) + if (!is_separator(OrigPath[OldPrefixDir.size()], style) && strict) + return false; // If prefixes have the same size we can simply copy the new one over. - if (OldPrefix.size() == NewPrefix.size()) { + if (OldPrefixDir.size() == NewPrefix.size() && !strict) { llvm::copy(NewPrefix, Path.begin()); - return; + return true; } - StringRef RelPath = OrigPath.substr(OldPrefix.size()); + StringRef RelPath = OrigPath.substr(OldPrefixDir.size()); SmallString<256> NewPath; path::append(NewPath, style, NewPrefix); - path::append(NewPath, style, RelPath); + if (!RelPath.empty()) { + if (!is_separator(RelPath[0], style) || !strict) + path::append(NewPath, style, RelPath); + else + path::append(NewPath, style, relative_path(RelPath, style)); + } + Path.swap(NewPath); + + return true; } void native(const Twine &path, SmallVectorImpl &result, Style style) { diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -1219,7 +1219,9 @@ TEST(Support, ReplacePathPrefix) { SmallString<64> Path1("/foo"); SmallString<64> Path2("/old/foo"); + SmallString<64> Path3("/oldnew/foo"); SmallString<64> OldPrefix("/old"); + SmallString<64> OldPrefixSep("/old/"); SmallString<64> NewPrefix("/new"); SmallString<64> NewPrefix2("/longernew"); SmallString<64> EmptyPrefix(""); @@ -1239,6 +1241,33 @@ Path = Path2; path::replace_path_prefix(Path, OldPrefix, EmptyPrefix); EXPECT_EQ(Path, "/foo"); + Path = Path2; + path::replace_path_prefix(Path, OldPrefix, EmptyPrefix, true); + EXPECT_EQ(Path, "foo"); + Path = Path3; + path::replace_path_prefix(Path, OldPrefix, NewPrefix, false); + EXPECT_EQ(Path, "/newnew/foo"); + Path = Path3; + path::replace_path_prefix(Path, OldPrefix, NewPrefix, true); + EXPECT_EQ(Path, "/oldnew/foo"); + Path = Path3; + path::replace_path_prefix(Path, OldPrefixSep, NewPrefix, true); + EXPECT_EQ(Path, "/oldnew/foo"); + Path = Path1; + path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + EXPECT_EQ(Path, "/new/foo"); + Path = OldPrefix; + path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_EQ(Path, "/new"); + Path = OldPrefixSep; + path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_EQ(Path, "/new/"); + Path = OldPrefix; + path::replace_path_prefix(Path, OldPrefixSep, NewPrefix, false); + EXPECT_EQ(Path, "/old"); + Path = OldPrefix; + path::replace_path_prefix(Path, OldPrefixSep, NewPrefix, true); + EXPECT_EQ(Path, "/new"); } TEST_F(FileSystemTest, OpenFileForRead) {