Skip to content

Commit a8b5483

Browse files
committed
[Support] Add a new path style for Windows with forward slashes
This behaves just like the regular Windows style, with both separator forms accepted, but with get_separator() returning forward slashes. Add a more descriptive name for the existing style, keeping the old name around as an alias initially. Add a new function `make_preferred()` (like the C++17 `std::filesystem::path` function with the same name), which converts windows paths to the preferred separator form (while this one works on any platform and takes a `path::Style` argument). Contrary to `native()` (just like `make_preferred()` in `std::filesystem`), this doesn't do anything at all on Posix, it doesn't try to reinterpret backslashes into forward slashes there. Differential Revision: https://reviews.llvm.org/D111879
1 parent f95bd18 commit a8b5483

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

llvm/include/llvm/Support/Path.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ namespace llvm {
2525
namespace sys {
2626
namespace path {
2727

28-
enum class Style { windows, posix, native };
28+
enum class Style {
29+
native,
30+
posix,
31+
windows_slash,
32+
windows_backslash,
33+
windows = windows_backslash, // deprecated
34+
};
2935

3036
/// Check if \p S uses POSIX path rules.
3137
constexpr bool is_style_posix(Style S) {
@@ -257,6 +263,17 @@ void native(const Twine &path, SmallVectorImpl<char> &result,
257263
/// @param path A path that is transformed to native format.
258264
void native(SmallVectorImpl<char> &path, Style style = Style::native);
259265

266+
/// For Windows path styles, convert path to use the preferred path separators.
267+
/// For other styles, do nothing.
268+
///
269+
/// @param path A path that is transformed to preferred format.
270+
inline void make_preferred(SmallVectorImpl<char> &path,
271+
Style style = Style::native) {
272+
if (!is_style_windows(style))
273+
return;
274+
native(path, style);
275+
}
276+
260277
/// Replaces backslashes with slashes if Windows.
261278
///
262279
/// @param path processed path

llvm/lib/Support/Path.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ namespace {
3737
using llvm::sys::path::Style;
3838

3939
inline Style real_style(Style style) {
40+
if (style != Style::native)
41+
return style;
4042
if (is_style_posix(style))
4143
return Style::posix;
4244
return Style::windows;
4345
}
4446

4547
inline const char *separators(Style style) {
46-
if (real_style(style) == Style::windows)
48+
if (is_style_windows(style))
4749
return "\\/";
4850
return "/";
4951
}
@@ -547,7 +549,9 @@ void native(SmallVectorImpl<char> &Path, Style style) {
547549
if (Path.empty())
548550
return;
549551
if (is_style_windows(style)) {
550-
std::replace(Path.begin(), Path.end(), '/', '\\');
552+
for (char &Ch : Path)
553+
if (is_separator(Ch, style))
554+
Ch = preferred_separator(style);
551555
if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) {
552556
SmallString<128> PathHome;
553557
home_directory(PathHome);
@@ -601,7 +605,7 @@ bool is_separator(char value, Style style) {
601605
}
602606

603607
StringRef get_separator(Style style) {
604-
if (is_style_windows(style))
608+
if (real_style(style) == Style::windows)
605609
return "\\";
606610
return "/";
607611
}

llvm/unittests/Support/Path.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ TEST(is_style_Style, Works) {
7575
// Check platform-independent results.
7676
EXPECT_TRUE(is_style_posix(Style::posix));
7777
EXPECT_TRUE(is_style_windows(Style::windows));
78+
EXPECT_TRUE(is_style_windows(Style::windows_slash));
7879
EXPECT_FALSE(is_style_posix(Style::windows));
80+
EXPECT_FALSE(is_style_posix(Style::windows_slash));
7981
EXPECT_FALSE(is_style_windows(Style::posix));
8082

8183
// Check platform-dependent results.
@@ -95,12 +97,19 @@ TEST(is_separator, Works) {
9597
EXPECT_FALSE(path::is_separator(' '));
9698

9799
EXPECT_TRUE(path::is_separator('\\', path::Style::windows));
100+
EXPECT_TRUE(path::is_separator('\\', path::Style::windows_slash));
98101
EXPECT_FALSE(path::is_separator('\\', path::Style::posix));
99102

100103
EXPECT_EQ(path::is_style_windows(path::Style::native),
101104
path::is_separator('\\'));
102105
}
103106

107+
TEST(get_separator, Works) {
108+
EXPECT_EQ(path::get_separator(path::Style::posix), "/");
109+
EXPECT_EQ(path::get_separator(path::Style::windows_backslash), "\\");
110+
EXPECT_EQ(path::get_separator(path::Style::windows_slash), "/");
111+
}
112+
104113
TEST(is_absolute_gnu, Works) {
105114
// Test tuple <Path, ExpectedPosixValue, ExpectedWindowsValue>.
106115
const std::tuple<StringRef, bool, bool> Paths[] = {
@@ -383,6 +392,8 @@ TEST(Support, PathIterator) {
383392
testing::ElementsAre("/", ".c", ".d", "..", "."));
384393
EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),
385394
testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
395+
EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_slash),
396+
testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
386397
EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));
387398
EXPECT_THAT(GetComponents("//net/c/foo.txt"),
388399
testing::ElementsAre("//net", "/", "c", "foo.txt"));
@@ -1425,10 +1436,25 @@ TEST(Support, NormalizePath) {
14251436
for (auto &T : Tests) {
14261437
SmallString<64> Win(std::get<0>(T));
14271438
SmallString<64> Posix(Win);
1439+
SmallString<64> WinSlash(Win);
14281440
path::native(Win, path::Style::windows);
14291441
path::native(Posix, path::Style::posix);
1442+
path::native(WinSlash, path::Style::windows_slash);
14301443
EXPECT_EQ(std::get<1>(T), Win);
14311444
EXPECT_EQ(std::get<2>(T), Posix);
1445+
EXPECT_EQ(std::get<2>(T), WinSlash);
1446+
}
1447+
1448+
for (auto &T : Tests) {
1449+
SmallString<64> WinBackslash(std::get<0>(T));
1450+
SmallString<64> Posix(WinBackslash);
1451+
SmallString<64> WinSlash(WinBackslash);
1452+
path::make_preferred(WinBackslash, path::Style::windows_backslash);
1453+
path::make_preferred(Posix, path::Style::posix);
1454+
path::make_preferred(WinSlash, path::Style::windows_slash);
1455+
EXPECT_EQ(std::get<1>(T), WinBackslash);
1456+
EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged here
1457+
EXPECT_EQ(std::get<2>(T), WinSlash);
14321458
}
14331459

14341460
#if defined(_WIN32)
@@ -1437,10 +1463,15 @@ TEST(Support, NormalizePath) {
14371463

14381464
const char *Path7a = "~/aaa";
14391465
SmallString<64> Path7(Path7a);
1440-
path::native(Path7);
1466+
path::native(Path7, path::Style::windows_backslash);
14411467
EXPECT_TRUE(Path7.endswith("\\aaa"));
14421468
EXPECT_TRUE(Path7.startswith(PathHome));
14431469
EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));
1470+
Path7 = Path7a;
1471+
path::native(Path7, path::Style::windows_slash);
1472+
EXPECT_TRUE(Path7.endswith("/aaa"));
1473+
EXPECT_TRUE(Path7.startswith(PathHome));
1474+
EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));
14441475

14451476
const char *Path8a = "~";
14461477
SmallString<64> Path8(Path8a);
@@ -1454,7 +1485,7 @@ TEST(Support, NormalizePath) {
14541485

14551486
const char *Path10a = "aaa/~/b";
14561487
SmallString<64> Path10(Path10a);
1457-
path::native(Path10);
1488+
path::native(Path10, path::Style::windows_backslash);
14581489
EXPECT_EQ(Path10, "aaa\\~\\b");
14591490
#endif
14601491
}

0 commit comments

Comments
 (0)