[libcxx-commits] [libcxx] aa427b1 - [libc++] Fix backslash as root dir breaks lexically_relative, lexically_proximate and hash_value on Windows (#99780)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 16 06:26:42 PDT 2024


Author: RichardLuo
Date: 2024-08-16T09:26:38-04:00
New Revision: aa427b1aae445ed46d9f60c5e2eaac61bdf76be3

URL: https://github.com/llvm/llvm-project/commit/aa427b1aae445ed46d9f60c5e2eaac61bdf76be3
DIFF: https://github.com/llvm/llvm-project/commit/aa427b1aae445ed46d9f60c5e2eaac61bdf76be3.diff

LOG: [libc++] Fix backslash as root dir breaks lexically_relative, lexically_proximate and hash_value on Windows (#99780)

Various functions like hash_value, lexically_proximate and lexically_relative
would incorrectly handle backslashes in the root directory on Windows, causing
behavior that is inconsistent with the equality comparison for a path.

Added: 
    

Modified: 
    libcxx/src/filesystem/path.cpp
    libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp
    libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index b2019521377ede..58742442bae6b7 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -267,7 +267,7 @@ path path::lexically_relative(const path& base) const {
   // Find the first mismatching element
   auto PP     = PathParser::CreateBegin(__pn_);
   auto PPBase = PathParser::CreateBegin(base.__pn_);
-  while (PP && PPBase && PP.State_ == PPBase.State_ && *PP == *PPBase) {
+  while (PP && PPBase && PP.State_ == PPBase.State_ && (*PP == *PPBase || PP.inRootDir())) {
     ++PP;
     ++PPBase;
   }
@@ -368,7 +368,8 @@ size_t hash_value(const path& __p) noexcept {
   size_t hash_value = 0;
   hash<string_view_t> hasher;
   while (PP) {
-    hash_value = __hash_combine(hash_value, hasher(*PP));
+    string_view_t Part = PP.inRootDir() ? PATHSTR("/") : *PP;
+    hash_value         = __hash_combine(hash_value, hasher(Part));
     ++PP;
   }
   return hash_value;

diff  --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp
index 99f55d3b17f1b0..3bebf38a2a7c34 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.compare.pass.cpp
@@ -47,13 +47,20 @@ struct PathCompareTest {
   int expect;
 };
 
-#define LONGA "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-#define LONGB "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
-#define LONGC "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
-#define LONGD "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
-const PathCompareTest CompareTestCases[] =
-{
-    {"", "",  0},
+#define LONGA                                                                                                          \
+  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
+  "AAAAAAAA"
+#define LONGB                                                                                                          \
+  "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" \
+  "BBBBBBBB"
+#define LONGC                                                                                                          \
+  "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" \
+  "CCCCCCCC"
+#define LONGD                                                                                                          \
+  "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" \
+  "DDDDDDDD"
+const PathCompareTest CompareTestCases[] = {
+    {"", "", 0},
     {"a", "", 1},
     {"", "a", -1},
     {"a/b/c", "a/b/c", 0},
@@ -62,14 +69,19 @@ const PathCompareTest CompareTestCases[] =
     {"a/b", "a/b/c", -1},
     {"a/b/c", "a/b", 1},
     {"a/b/", "a/b/.", -1},
-    {"a/b/", "a/b",    1},
+    {"a/b/", "a/b", 1},
     {"a/b//////", "a/b/////.", -1},
     {"a/.././b", "a///..//.////b", 0},
     {"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators
-    {"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory
-    {"/foo/bar/", "/foo/bar", 1}, // trailing separator
+    {"///foo/bar", "/foo/bar", 0},                 // "///" is not a root directory
+    {"/foo/bar/", "/foo/bar", 1},                  // trailing separator
     {"foo", "/foo", -1}, // if !this->has_root_directory() and p.has_root_directory(), a value less than 0.
-    {"/foo", "foo", 1}, //  if this->has_root_directory() and !p.has_root_directory(), a value greater than 0.
+    {"/foo", "foo", 1},  //  if this->has_root_directory() and !p.has_root_directory(), a value greater than 0.
+#ifdef _WIN32
+    {"C:/a", "C:\\a", 0},
+#else
+    {"C:/a", "C:\\a", -1},
+#endif
     {("//" LONGA "////" LONGB "/" LONGC "///" LONGD), ("//" LONGA "/" LONGB "/" LONGC "/" LONGD), 0},
     {(LONGA "/" LONGB "/" LONGC), (LONGA "/" LONGB "/" LONGB), 1}
 
@@ -79,23 +91,19 @@ const PathCompareTest CompareTestCases[] =
 #undef LONGC
 #undef LONGD
 
-static inline int normalize_ret(int ret)
-{
-  return ret < 0 ? -1 : (ret > 0 ? 1 : 0);
-}
+static inline int normalize_ret(int ret) { return ret < 0 ? -1 : (ret > 0 ? 1 : 0); }
 
-void test_compare_basic()
-{
+void test_compare_basic() {
   using namespace fs;
-  for (auto const & TC : CompareTestCases) {
+  for (auto const& TC : CompareTestCases) {
     const path p1(TC.LHS);
     const path p2(TC.RHS);
     std::string RHS(TC.RHS);
     const path::string_type R(RHS.begin(), RHS.end());
     const std::basic_string_view<path::value_type> RV(R);
-    const path::value_type *Ptr = R.c_str();
-    const int E = TC.expect;
-    { // compare(...) functions
+    const path::value_type* Ptr = R.c_str();
+    const int E                 = TC.expect;
+    {                           // compare(...) functions
       DisableAllocationGuard g; // none of these operations should allocate
 
       // check runtime results
@@ -113,7 +121,7 @@ void test_compare_basic()
       // check signatures
       ASSERT_NOEXCEPT(p1.compare(p2));
     }
-    { // comparison operators
+    {                           // comparison operators
       DisableAllocationGuard g; // none of these operations should allocate
 
       // check signatures
@@ -180,14 +188,14 @@ void test_compare_elements() {
 
   auto BuildPath = [](std::vector<std::string> const& Elems) {
     fs::path p;
-    for (auto &E : Elems)
+    for (auto& E : Elems)
       p /= E;
     return p;
   };
 
-  for (auto &TC : TestCases) {
-    fs::path LHS = BuildPath(TC.LHSElements);
-    fs::path RHS = BuildPath(TC.RHSElements);
+  for (auto& TC : TestCases) {
+    fs::path LHS        = BuildPath(TC.LHSElements);
+    fs::path RHS        = BuildPath(TC.RHSElements);
     const int ExpectCmp = CompareElements(TC.LHSElements, TC.RHSElements);
     assert(ExpectCmp == TC.Expect);
     const int GotCmp = normalize_ret(LHS.compare(RHS));

diff  --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp
index 42fa813ada4a5c..06954fe876e557 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_relative_and_proximate.pass.cpp
@@ -41,9 +41,11 @@ int main(int, char**) {
 #ifdef _WIN32
       {"//net/", "//net", ""},
       {"//net", "//net/", ""},
+      {"C:\\a\\b", "C:/a", "b"},
 #else
       {"//net/", "//net", "."},
       {"//net", "//net/", "."},
+      {"C:\\a\\b", "C:/a", "../../C:\\a\\b"},
 #endif
       {"//base", "a", ""},
       {"a", "a", "."},


        


More information about the libcxx-commits mailing list