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 @@ -25,7 +25,13 @@ namespace sys { namespace path { -enum class Style { windows, posix, native }; +enum class Style { + native, + posix, + windows_slash, + windows_backslash, + windows = windows_backslash, // deprecated +}; /// Check if \p S uses POSIX path rules. constexpr bool is_style_posix(Style S) { @@ -257,6 +263,17 @@ /// @param path A path that is transformed to native format. void native(SmallVectorImpl &path, Style style = Style::native); +/// For Windows path styles, convert path to use the preferred path separators. +/// For other styles, do nothing. +/// +/// @param path A path that is transformed to preferred format. +inline void make_preferred(SmallVectorImpl &path, + Style style = Style::native) { + if (!is_style_windows(style)) + return; + native(path, style); +} + /// Replaces backslashes with slashes if Windows. /// /// @param path processed 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 @@ -37,13 +37,15 @@ using llvm::sys::path::Style; inline Style real_style(Style style) { + if (style != Style::native) + return style; if (is_style_posix(style)) return Style::posix; return Style::windows; } inline const char *separators(Style style) { - if (real_style(style) == Style::windows) + if (is_style_windows(style)) return "\\/"; return "/"; } @@ -547,7 +549,9 @@ if (Path.empty()) return; if (is_style_windows(style)) { - std::replace(Path.begin(), Path.end(), '/', '\\'); + for (char &Ch : Path) + if (is_separator(Ch, style)) + Ch = preferred_separator(style); if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { SmallString<128> PathHome; home_directory(PathHome); @@ -601,7 +605,7 @@ } StringRef get_separator(Style style) { - if (is_style_windows(style)) + if (real_style(style) == Style::windows) return "\\"; return "/"; } 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 @@ -75,7 +75,9 @@ // Check platform-independent results. EXPECT_TRUE(is_style_posix(Style::posix)); EXPECT_TRUE(is_style_windows(Style::windows)); + EXPECT_TRUE(is_style_windows(Style::windows_slash)); EXPECT_FALSE(is_style_posix(Style::windows)); + EXPECT_FALSE(is_style_posix(Style::windows_slash)); EXPECT_FALSE(is_style_windows(Style::posix)); // Check platform-dependent results. @@ -95,12 +97,19 @@ EXPECT_FALSE(path::is_separator(' ')); EXPECT_TRUE(path::is_separator('\\', path::Style::windows)); + EXPECT_TRUE(path::is_separator('\\', path::Style::windows_slash)); EXPECT_FALSE(path::is_separator('\\', path::Style::posix)); EXPECT_EQ(path::is_style_windows(path::Style::native), path::is_separator('\\')); } +TEST(get_separator, Works) { + EXPECT_EQ(path::get_separator(path::Style::posix), "/"); + EXPECT_EQ(path::get_separator(path::Style::windows_backslash), "\\"); + EXPECT_EQ(path::get_separator(path::Style::windows_slash), "/"); +} + TEST(is_absolute_gnu, Works) { // Test tuple . const std::tuple Paths[] = { @@ -383,6 +392,8 @@ testing::ElementsAre("/", ".c", ".d", "..", ".")); EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows), testing::ElementsAre("c:", "\\", "c", "e", "foo.txt")); + EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_slash), + testing::ElementsAre("c:", "\\", "c", "e", "foo.txt")); EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/")); EXPECT_THAT(GetComponents("//net/c/foo.txt"), testing::ElementsAre("//net", "/", "c", "foo.txt")); @@ -1425,10 +1436,25 @@ for (auto &T : Tests) { SmallString<64> Win(std::get<0>(T)); SmallString<64> Posix(Win); + SmallString<64> WinSlash(Win); path::native(Win, path::Style::windows); path::native(Posix, path::Style::posix); + path::native(WinSlash, path::Style::windows_slash); EXPECT_EQ(std::get<1>(T), Win); EXPECT_EQ(std::get<2>(T), Posix); + EXPECT_EQ(std::get<2>(T), WinSlash); + } + + for (auto &T : Tests) { + SmallString<64> WinBackslash(std::get<0>(T)); + SmallString<64> Posix(WinBackslash); + SmallString<64> WinSlash(WinBackslash); + path::make_preferred(WinBackslash, path::Style::windows_backslash); + path::make_preferred(Posix, path::Style::posix); + path::make_preferred(WinSlash, path::Style::windows_slash); + EXPECT_EQ(std::get<1>(T), WinBackslash); + EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged here + EXPECT_EQ(std::get<2>(T), WinSlash); } #if defined(_WIN32) @@ -1437,10 +1463,15 @@ const char *Path7a = "~/aaa"; SmallString<64> Path7(Path7a); - path::native(Path7); + path::native(Path7, path::Style::windows_backslash); EXPECT_TRUE(Path7.endswith("\\aaa")); EXPECT_TRUE(Path7.startswith(PathHome)); EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1)); + Path7 = Path7a; + path::native(Path7, path::Style::windows_slash); + EXPECT_TRUE(Path7.endswith("/aaa")); + EXPECT_TRUE(Path7.startswith(PathHome)); + EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1)); const char *Path8a = "~"; SmallString<64> Path8(Path8a); @@ -1454,7 +1485,7 @@ const char *Path10a = "aaa/~/b"; SmallString<64> Path10(Path10a); - path::native(Path10); + path::native(Path10, path::Style::windows_backslash); EXPECT_EQ(Path10, "aaa\\~\\b"); #endif }