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 @@ -451,10 +451,48 @@ /// Is path absolute? /// +/// According to cppreference.com, C++17 states: "An absolute path is a path +/// that unambiguously identifies the location of a file without reference to +/// an additional starting location." +/// +/// In other words, the rules are: +/// 1) POSIX style paths with nonempty root directory are absolute. +/// 2) Windows style paths with nonempty root name and root directory are +/// absolute. +/// 3) No other paths are absolute. +/// +/// \see has_root_name +/// \see has_root_directory +/// /// @param path Input path. /// @result True if the path is absolute, false if it is not. bool is_absolute(const Twine &path, Style style = Style::native); +/// Is path absolute using GNU rules? +/// +/// GNU rules are: +/// 1) Paths starting with a path separator are absolute. +/// 2) Windows style paths are also absolute if they start with a character +/// followed by ':'. +/// 3) No other paths are absolute. +/// +/// On Windows style the path "C:\Users\Default" has "C:" as root name and "\" +/// as root directory. +/// +/// Hence "C:" on Windows is absolute under GNU rules and not absolute under +/// C++17 because it has no root directory. Likewise "/" and "\" on Windows are +/// absolute under GNU and are not absolute under C++17 due to empty root name. +/// +/// \see has_root_name +/// \see has_root_directory +/// +/// @param path Input path. +/// @param style The style of \p path (e.g. Windows or POSIX). "native" style +/// means to derive the style from the host. +/// @result True if the path is absolute following GNU rules, false if it is +/// not. +bool is_absolute_gnu(const Twine &path, Style style = Style::native); + /// Is path relative? /// /// @param path Input 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 @@ -683,6 +683,24 @@ return rootDir && rootName; } +bool is_absolute_gnu(const Twine &path, Style style) { + SmallString<128> path_storage; + StringRef p = path.toStringRef(path_storage); + + // Handle '/' which is absolute for both Windows and POSIX systems. + // Handle '\\' on Windows. + if (!p.empty() && is_separator(p.front(), style)) + return true; + + if (real_style(style) == Style::windows) { + // Handle drive letter pattern (a character followed by ':') on Windows. + if (p.size() >= 2 && (p[0] && p[1] == ':')) + return true; + } + + return false; +} + bool is_relative(const Twine &path, Style style) { return !is_absolute(path, 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 @@ -85,6 +85,23 @@ #endif } +TEST(is_absolute_gnu, Works) { + // Test tuple . + const std::tuple Paths[] = { + {"", false, false}, {"/", true, true}, {"/foo", true, true}, + {"\\", false, true}, {"\\foo", false, true}, {"foo", false, false}, + {"c", false, false}, {"c:", false, true}, {"c:\\", false, true}, + {"!:", false, true}, {"xx:", false, false}, {"c:abc\\", false, true}, + {":", false, false}}; + + for (const auto &Path : Paths) { + EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::posix), + std::get<1>(Path)); + EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::windows), + std::get<2>(Path)); + } +} + TEST(Support, Path) { SmallVector paths; paths.push_back(""); @@ -171,6 +188,7 @@ (void)path::has_extension(*i); (void)path::extension(*i); (void)path::is_absolute(*i); + (void)path::is_absolute_gnu(*i); (void)path::is_relative(*i); SmallString<128> temp_store;