Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -470,10 +470,14 @@ } std::string CGDebugInfo::remapDIPath(StringRef Path) const { + if (DebugPrefixMap.empty()) + return Path.str(); + + 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)) + break; + return P.str().str(); } unsigned CGDebugInfo::getLineNumber(SourceLocation Loc) { Index: clang/lib/Lex/PPMacroExpansion.cpp =================================================================== --- clang/lib/Lex/PPMacroExpansion.cpp +++ clang/lib/Lex/PPMacroExpansion.cpp @@ -1456,10 +1456,8 @@ const std::map> &MacroPrefixMap) { for (const auto &Entry : MacroPrefixMap) - if (Path.startswith(Entry.first)) { - Path = (Twine(Entry.second) + Path.substr(Entry.first.size())).str(); + if (llvm::sys::path::replace_path_prefix(Path, Entry.first, Entry.second)) break; - } } /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded @@ -1543,8 +1541,8 @@ } else { FN += PLoc.getFilename(); } - Lexer::Stringify(FN); remapMacroPath(FN, PPOpts->MacroPrefixMap); + Lexer::Stringify(FN); OS << '"' << FN << '"'; } Tok.setKind(tok::string_literal); Index: clang/test/Preprocessor/file_test.c =================================================================== --- clang/test/Preprocessor/file_test.c +++ clang/test/Preprocessor/file_test.c @@ -1,23 +1,23 @@ -// XFAIL: system-windows +// UNSUPPORTED: system-windows // 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" +#include "Inputs/include-file-test/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: filename: "/UNLIKELY_PATH/empty/file_test.c" +// CHECK: filename: "/UNLIKELY_PATH/empty/Inputs/include-file-test/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: filename: "/UNLIKELY_PATH=empty/file_test.c" +// CHECK-EVIL: filename: "/UNLIKELY_PATH=empty/Inputs/include-file-test/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: filename: "Inputs/include-file-test/file_test.h" // CHECK-REMOVE: basefile: "file_test.c" // CHECK-REMOVE-NOT: filename: Index: clang/test/Preprocessor/file_test_windows.c =================================================================== --- /dev/null +++ clang/test/Preprocessor/file_test_windows.c @@ -0,0 +1,29 @@ +// REQUIRES: system-windows +// RUN: %clang -E -ffile-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH\empty -c -o - %s | FileCheck %s +// RUN: %clang -E -fmacro-prefix-map=%p=A:\UNLIKELY_PATH=empty -c -o - %s | FileCheck %s -check-prefix CHECK-EVIL +// RUN: %clang -E -fmacro-prefix-map=%p/iNPUTS\=A:\UNLIKELY_PATH_INC\ -fmacro-prefix-map=%p/=A:\UNLIKELY_PATH_BASE\ -c -o - %s | FileCheck %s -check-prefix CHECK-CASE +// RUN: %clang -E -fmacro-prefix-map=%p\= -c -o - %s | FileCheck %s --check-prefix CHECK-REMOVE + +filename: __FILE__ +#include "Inputs/include-file-test/file_test.h" + +// CHECK: filename: "A:\\UNLIKELY_PATH\\empty\\file_test_windows.c" +// CHECK: filename: "A:\\UNLIKELY_PATH\\empty/Inputs/include-file-test/file_test.h" +// CHECK: basefile: "A:\\UNLIKELY_PATH\\empty\\file_test_windows.c" +// CHECK-NOT: filename: + +// CHECK-EVIL: filename: "A:\\UNLIKELY_PATH=empty\\file_test_windows.c" +// CHECK-EVIL: filename: "A:\\UNLIKELY_PATH=empty/Inputs/include-file-test/file_test.h" +// CHECK-EVIL: basefile: "A:\\UNLIKELY_PATH=empty\\file_test_windows.c" +// CHECK-EVIL-NOT: filename: + +// CHECK-CASE: filename: "A:\\UNLIKELY_PATH_BASE\\file_test_windows.c" +// CHECK-CASE: filename: "A:\\UNLIKELY_PATH_INC\\include-file-test/file_test.h" +// CHECK-CASE: basefile: "A:\\UNLIKELY_PATH_BASE\\file_test_windows.c" +// CHECK-CASE-NOT: filename: + +// CHECK-REMOVE: filename: "file_test_windows.c" +// CHECK-REMOVE: filename: "Inputs/include-file-test/file_test.h" +// CHECK-REMOVE: basefile: "file_test_windows.c" +// CHECK-REMOVE-NOT: filename: Index: llvm/include/llvm/Support/Path.h =================================================================== --- llvm/include/llvm/Support/Path.h +++ llvm/include/llvm/Support/Path.h @@ -167,9 +167,12 @@ /// start with \a NewPrefix. /// @param OldPrefix The path prefix to strip from \a Path. /// @param NewPrefix The path prefix to replace \a NewPrefix with. +/// @param style The style used to match the prefix. Exact match using +/// Posix style, case/separator insensitive match for Windows style. /// @result true if \a Path begins with OldPrefix bool replace_path_prefix(SmallVectorImpl &Path, StringRef OldPrefix, - StringRef NewPrefix); + StringRef NewPrefix, + Style style = Style::native); /// Append to path. /// Index: llvm/lib/DWARFLinker/DWARFLinker.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinker.cpp +++ llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -1920,10 +1920,14 @@ static std::string remapPath(StringRef Path, const objectPrefixMap &ObjectPrefixMap) { + if (ObjectPrefixMap.empty()) + return Path.str(); + + SmallString<256> p = Path; for (const auto &Entry : ObjectPrefixMap) - 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)) + break; + return p.str().str(); } bool DWARFLinker::registerModuleReference( Index: llvm/lib/MC/MCContext.cpp =================================================================== --- llvm/lib/MC/MCContext.cpp +++ llvm/lib/MC/MCContext.cpp @@ -642,13 +642,17 @@ void MCContext::RemapDebugPaths() { const auto &DebugPrefixMap = this->DebugPrefixMap; + if (DebugPrefixMap.empty()) + return; + const auto RemapDebugPath = [&DebugPrefixMap](std::string &Path) { - for (const auto &Entry : DebugPrefixMap) - if (StringRef(Path).startswith(Entry.first)) { - std::string RemappedPath = - (Twine(Entry.second) + Path.substr(Entry.first.size())).str(); - Path.swap(RemappedPath); + SmallString<256> P(Path); + for (const auto &Entry : DebugPrefixMap) { + if (llvm::sys::path::replace_path_prefix(P, Entry.first, Entry.second)) { + Path = P.str().str(); + break; } + } }; // Remap compilation directory. Index: llvm/lib/Support/Path.cpp =================================================================== --- llvm/lib/Support/Path.cpp +++ llvm/lib/Support/Path.cpp @@ -496,13 +496,32 @@ path.append(ext.begin(), ext.end()); } +static bool starts_with(StringRef Path, StringRef Prefix, + Style style = Style::native) { + // Windows prefix matching : case and separator insensitive + if (real_style(style) == Style::windows) { + if (Path.size() < Prefix.size()) + return false; + for (size_t I = 0, E = Prefix.size(); I != E; ++I) { + bool SepPath = is_separator(Path[I], style); + bool SepPrefix = is_separator(Prefix[I], style); + if (SepPath != SepPrefix) + return false; + if (!SepPath && toLower(Path[I]) != toLower(Prefix[I])) + return false; + } + return true; + } + return Path.startswith(Prefix); +} + bool replace_path_prefix(SmallVectorImpl &Path, StringRef OldPrefix, - StringRef NewPrefix) { + StringRef NewPrefix, Style style) { if (OldPrefix.empty() && NewPrefix.empty()) return false; StringRef OrigPath(Path.begin(), Path.size()); - if (!OrigPath.startswith(OldPrefix)) + if (!starts_with(OrigPath, OldPrefix, style)) return false; // If prefixes have the same size we can simply copy the new one over. Index: llvm/unittests/Support/Path.cpp =================================================================== --- llvm/unittests/Support/Path.cpp +++ llvm/unittests/Support/Path.cpp @@ -1280,48 +1280,73 @@ SmallString<64> Path1("/foo"); SmallString<64> Path2("/old/foo"); SmallString<64> Path3("/oldnew/foo"); + SmallString<64> Path4("C:\\old/foo\\bar"); SmallString<64> OldPrefix("/old"); SmallString<64> OldPrefixSep("/old/"); + SmallString<64> OldPrefixWin("c:/oLD/F"); SmallString<64> NewPrefix("/new"); SmallString<64> NewPrefix2("/longernew"); SmallString<64> EmptyPrefix(""); + bool Found; SmallString<64> Path = Path1; - path::replace_path_prefix(Path, OldPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_FALSE(Found); EXPECT_EQ(Path, "/foo"); Path = Path2; - path::replace_path_prefix(Path, OldPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/new/foo"); Path = Path2; - path::replace_path_prefix(Path, OldPrefix, NewPrefix2); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/longernew/foo"); Path = Path1; - path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/new/foo"); Path = Path2; - path::replace_path_prefix(Path, OldPrefix, EmptyPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, EmptyPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/foo"); Path = Path2; - path::replace_path_prefix(Path, OldPrefixSep, EmptyPrefix); + Found = path::replace_path_prefix(Path, OldPrefixSep, EmptyPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "foo"); Path = Path3; - path::replace_path_prefix(Path, OldPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/newnew/foo"); Path = Path3; - path::replace_path_prefix(Path, OldPrefix, NewPrefix2); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/longernewnew/foo"); Path = Path1; - path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/new/foo"); Path = OldPrefix; - path::replace_path_prefix(Path, OldPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/new"); Path = OldPrefixSep; - path::replace_path_prefix(Path, OldPrefix, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix); + EXPECT_TRUE(Found); EXPECT_EQ(Path, "/new/"); Path = OldPrefix; - path::replace_path_prefix(Path, OldPrefixSep, NewPrefix); + Found = path::replace_path_prefix(Path, OldPrefixSep, NewPrefix); + EXPECT_FALSE(Found); EXPECT_EQ(Path, "/old"); + Path = Path4; + Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix, + path::Style::windows); + EXPECT_TRUE(Found); + EXPECT_EQ(Path, "/newoo\\bar"); + Path = Path4; + Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix, + path::Style::posix); + EXPECT_FALSE(Found); + EXPECT_EQ(Path, "C:\\old/foo\\bar"); } TEST_F(FileSystemTest, OpenFileForRead) {