[libcxx-commits] [libcxx] [libc++] Make std::filesystem::canonical throw when given empty path (PR #77223)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jan 14 01:14:25 PST 2024


https://github.com/Yuki-cpp updated https://github.com/llvm/llvm-project/pull/77223

>From 19f5fb22944f79c672e192c75b90b3d94ad542c8 Mon Sep 17 00:00:00 2001
From: Leo <leo.ghafari at gmail.com>
Date: Sun, 7 Jan 2024 10:46:46 +0900
Subject: [PATCH 1/5] [libc++] Make std::filesystem::canonical throw when given
 empty path

Adds a check for empty paths in std::filesystem::canonical and raise an
error accordingly. Since std::filesystem::weakly_canonical uses this function
and should not raise an error when given an empty path, it was updated
to return an empty path instead of the results of canonical.
---
 libcxx/src/filesystem/operations.cpp          |  6 +++--
 .../fs.op.canonical/canonical.pass.cpp        | 26 +++++++++++++++++++
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 6bee340e0d15c8..8f5b70a071373d 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -82,6 +82,8 @@ path __canonical(path const& orig_p, error_code* ec) {
   path cwd;
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
+  if (orig_p.empty())
+    return err.report(capture_errno());
   path p = __do_absolute(orig_p, &cwd, ec);
 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
@@ -912,7 +914,7 @@ path __weakly_canonical(const path& p, error_code* ec) {
   ErrorHandler<path> err("weakly_canonical", ec, &p);
 
   if (p.empty())
-    return __canonical("", ec);
+    return __current_path(ec);
 
   path result;
   path tmp;
@@ -935,7 +937,7 @@ path __weakly_canonical(const path& p, error_code* ec) {
     --PP;
   }
   if (PP.State == PathParser::PS_BeforeBegin)
-    result = __canonical("", ec);
+    result = __current_path(ec);
   if (ec)
     ec->clear();
   if (DNEParts.empty())
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
index 0098fe8ee698ef..000fb47096ba19 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
@@ -92,6 +92,31 @@ static void test_dne_path()
     }
 }
 
+static void test_empty_path()
+{
+    std::error_code ec = GetTestEC();
+    {
+        const path ret = canonical(path{}, ec);
+        assert(ec != GetTestEC());
+        assert(ec);
+        assert(ret == path{});
+    }
+    {
+        TEST_THROWS_TYPE(filesystem_error, canonical(path{}));
+    }
+
+    ec = GetTestEC();
+    {
+        const path ret = canonical("", ec);
+        assert(ec != GetTestEC());
+        assert(ec);
+        assert(ret == path{});
+    }
+    {
+        TEST_THROWS_TYPE(filesystem_error, canonical(""));
+    }
+}
+
 static void test_exception_contains_paths()
 {
 #ifndef TEST_HAS_NO_EXCEPTIONS
@@ -121,6 +146,7 @@ int main(int, char**) {
     signature_test();
     test_canonical();
     test_dne_path();
+    test_empty_path();
     test_exception_contains_paths();
 
     return 0;

>From 614ec4b47f0c986092a95fc64e9f9f1b59de3f84 Mon Sep 17 00:00:00 2001
From: Leo <leo.ghafari at gmail.com>
Date: Sun, 7 Jan 2024 17:25:26 +0900
Subject: [PATCH 2/5] fixup! [libc++] Make std::filesystem::canonical throw
 when given empty path

---
 .../fs.op.canonical/canonical.pass.cpp        | 39 ++++++++-----------
 1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
index 000fb47096ba19..c5935f20510844 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
@@ -92,29 +92,24 @@ static void test_dne_path()
     }
 }
 
-static void test_empty_path()
-{
-    std::error_code ec = GetTestEC();
-    {
-        const path ret = canonical(path{}, ec);
-        assert(ec != GetTestEC());
-        assert(ec);
-        assert(ret == path{});
-    }
-    {
-        TEST_THROWS_TYPE(filesystem_error, canonical(path{}));
-    }
+static void test_empty_path() {
+  std::error_code ec = GetTestEC();
+  {
+    const path ret = canonical(path{}, ec);
+    assert(ec != GetTestEC());
+    assert(ec);
+    assert(ret == path{});
+  }
+  { TEST_THROWS_TYPE(filesystem_error, canonical(path{})); }
 
-    ec = GetTestEC();
-    {
-        const path ret = canonical("", ec);
-        assert(ec != GetTestEC());
-        assert(ec);
-        assert(ret == path{});
-    }
-    {
-        TEST_THROWS_TYPE(filesystem_error, canonical(""));
-    }
+  ec = GetTestEC();
+  {
+    const path ret = canonical("", ec);
+    assert(ec != GetTestEC());
+    assert(ec);
+    assert(ret == path{});
+  }
+  { TEST_THROWS_TYPE(filesystem_error, canonical("")); }
 }
 
 static void test_exception_contains_paths()

>From fbc66968ccdafb828aa1f3bd6522c9fb75e2aff5 Mon Sep 17 00:00:00 2001
From: Leo <leo.ghafari at gmail.com>
Date: Wed, 10 Jan 2024 21:17:52 +0900
Subject: [PATCH 3/5] fixup! fixup! [libc++] Make std::filesystem::canonical
 throw when given empty path

---
 libcxx/src/filesystem/operations.cpp          | 10 +++++----
 .../fs.op.absolute/absolute.pass.cpp          | 20 ++++++++++++-----
 .../fs.op.canonical/canonical.pass.cpp        |  9 --------
 .../fs.op.proximate/proximate.pass.cpp        | 22 +++++++++----------
 .../fs.op.relative/relative.pass.cpp          |  2 +-
 .../weakly_canonical.pass.cpp                 |  8 +++----
 6 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 8f5b70a071373d..51b8f14619fb2a 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -63,6 +63,10 @@ using parser::PathParser;
 using parser::string_view_t;
 
 static path __do_absolute(const path& p, path* cwd, error_code* ec) {
+  ErrorHandler<path> err("absolute", ec, &p);
+
+  if (p.empty())
+    return err.report(errc::invalid_argument);
   if (ec)
     ec->clear();
   if (p.is_absolute())
@@ -82,8 +86,6 @@ path __canonical(path const& orig_p, error_code* ec) {
   path cwd;
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
-  if (orig_p.empty())
-    return err.report(capture_errno());
   path p = __do_absolute(orig_p, &cwd, ec);
 #if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
@@ -914,7 +916,7 @@ path __weakly_canonical(const path& p, error_code* ec) {
   ErrorHandler<path> err("weakly_canonical", ec, &p);
 
   if (p.empty())
-    return __current_path(ec);
+    return p;
 
   path result;
   path tmp;
@@ -937,7 +939,7 @@ path __weakly_canonical(const path& p, error_code* ec) {
     --PP;
   }
   if (PP.State == PathParser::PS_BeforeBegin)
-    result = __current_path(ec);
+    result = path{};
   if (ec)
     ec->clear();
   if (DNEParts.empty())
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp
index 7a60d1ab29f4ad..9c88613abc6a94 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.absolute/absolute.pass.cpp
@@ -39,12 +39,8 @@ static void basic_test()
     const struct {
       std::string input;
       fs::path expect;
-    } TestCases [] = {
-        {"", cwd / ""},
-        {"foo", cwd / "foo"},
-        {"foo/", cwd / "foo/"},
-        {"/already_absolute", cwd.root_name() / "/already_absolute"}
-    };
+    } TestCases[] = {
+        {"foo", cwd / "foo"}, {"foo/", cwd / "foo/"}, {"/already_absolute", cwd.root_name() / "/already_absolute"}};
     for (auto& TC : TestCases) {
         std::error_code ec = GetTestEC();
         const path ret = absolute(TC.input, ec);
@@ -55,8 +51,20 @@ static void basic_test()
     }
 }
 
+static void test_empty_path() {
+  std::error_code ec = GetTestEC();
+  {
+    const path ret = absolute(path{}, ec);
+    assert(ec != GetTestEC());
+    assert(ec);
+    assert(ret == path{});
+  }
+  { TEST_THROWS_TYPE(filesystem_error, absolute(path{})); }
+}
+
 int main(int, char**) {
     absolute_signature_test();
+    test_empty_path();
     basic_test();
 
     return 0;
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
index c5935f20510844..e5c968b8a5ebc2 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
@@ -101,15 +101,6 @@ static void test_empty_path() {
     assert(ret == path{});
   }
   { TEST_THROWS_TYPE(filesystem_error, canonical(path{})); }
-
-  ec = GetTestEC();
-  {
-    const path ret = canonical("", ec);
-    assert(ec != GetTestEC());
-    assert(ec);
-    assert(ret == path{});
-  }
-  { TEST_THROWS_TYPE(filesystem_error, canonical("")); }
 }
 
 static void test_exception_contains_paths()
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
index d2e9c1e6e8bf0d..cb97ee4a0ae8ac 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
@@ -66,16 +66,16 @@ static void basic_test() {
     fs::path expect;
   } TestCases[] = {
       {"", "", "."},
-      {cwd, "a", ".."},
-      {parent_cwd, "a", "../.."},
+      {cwd, "a", cwd},
+      {parent_cwd, "a", parent_cwd},
       {"a", cwd, "a"},
-      {"a", parent_cwd, curdir / "a"},
-      {"/", "a", dot_dot_to_root / ".."},
-      {"/", "a/b", dot_dot_to_root / "../.."},
-      {"/", "a/b/", dot_dot_to_root / "../.."},
-      {"a", "/", relative_cwd / "a"},
-      {"a/b", "/", relative_cwd / "a/b"},
-      {"a", "/net", ".." / relative_cwd / "a"},
+      {"a", parent_cwd, "a"},
+      {"/", "a", "/"},
+      {"/", "a/b", "/"},
+      {"/", "a/b/", "/"},
+      {"a", "/", "a"},
+      {"a/b", "/", "a/b"},
+      {"a", "/net", "a"},
 #ifdef _WIN32
       {"//foo/", "//foo", "//foo/"},
       {"//foo", "//foo/", "//foo"},
@@ -104,10 +104,10 @@ static void basic_test() {
       {"X:a", "Y:/b", "X:a"},
       {"X:a", "Y:b", "X:a"},
 #else
-      {"//foo", "a", dot_dot_to_root / "../foo"},
+      {"//foo", "a", "/foo"},
       {"//foo/a", "//bar", "../foo/a"},
       {"//foo/a", "//bar/", "../foo/a"},
-      {"//foo/a", "b", dot_dot_to_root / "../foo/a"},
+      {"//foo/a", "b", "/foo/a"},
       {"//foo/a", "/b", "../foo/a"},
       {"//foo/a", "//bar/b", "../../foo/a"},
       {"X:/a", "X:/b", "../a"},
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
index 9bd4051f790218..92209425d64288 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
@@ -30,7 +30,7 @@ namespace fs = std::filesystem;
 static void test_signature_0() {
   fs::path p("");
   const fs::path output = fs::weakly_canonical(p);
-  assert(output == fs::path::string_type(fs::current_path()));
+  assert(output == fs::path::string_type(""));
 }
 
 static void test_signature_1() {
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
index 035e875e4ec83d..b981b25af455a8 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
@@ -36,15 +36,15 @@ int main(int, char**) {
     fs::path input;
     fs::path expect;
   } TestCases[] = {
-      {"", fs::current_path()},
+      {"", ""},
       {".", fs::current_path()},
       {"/", root},
       {"/foo", root / "foo"},
       {"/.", root},
       {"/./", root},
-      {"a/b", fs::current_path() / "a/b"},
-      {"a", fs::current_path() / "a"},
-      {"a/b/", fs::current_path() / "a/b/"},
+      {"a/b", "a/b"},
+      {"a", "a"},
+      {"a/b/", "a/b/"},
       {static_env.File, static_env.File},
       {static_env.Dir, static_env.Dir},
       {static_env.SymlinkToDir, static_env.Dir},

>From 6edd46609dc3a056a6fd5fabd6eecbe54863cf20 Mon Sep 17 00:00:00 2001
From: Leo <leo.ghafari at gmail.com>
Date: Wed, 10 Jan 2024 22:43:24 +0900
Subject: [PATCH 4/5] fixup! fixup! fixup! [libc++] Make
 std::filesystem::canonical throw when given empty path

---
 .../filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
index 92209425d64288..0fe2cf02b3f1c8 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
@@ -30,7 +30,7 @@ namespace fs = std::filesystem;
 static void test_signature_0() {
   fs::path p("");
   const fs::path output = fs::weakly_canonical(p);
-  assert(output == fs::path::string_type(""));
+  assert(output == fs::path::string_type(p));
 }
 
 static void test_signature_1() {

>From 7ac0f47a7c837575bebe76c912dae4d1efd7cac9 Mon Sep 17 00:00:00 2001
From: Leo <leo.ghafari at gmail.com>
Date: Sun, 14 Jan 2024 18:12:12 +0900
Subject: [PATCH 5/5] fixup! fixup! fixup! fixup! [libc++] Make
 std::filesystem::canonical throw when given empty path

---
 .../fs.op.funcs/fs.op.proximate/proximate.pass.cpp          | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
index cb97ee4a0ae8ac..b4bd90317ba4ad 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.proximate/proximate.pass.cpp
@@ -70,9 +70,6 @@ static void basic_test() {
       {parent_cwd, "a", parent_cwd},
       {"a", cwd, "a"},
       {"a", parent_cwd, "a"},
-      {"/", "a", "/"},
-      {"/", "a/b", "/"},
-      {"/", "a/b/", "/"},
       {"a", "/", "a"},
       {"a/b", "/", "a/b"},
       {"a", "/net", "a"},
@@ -80,6 +77,9 @@ static void basic_test() {
       {"//foo/", "//foo", "//foo/"},
       {"//foo", "//foo/", "//foo"},
 #else
+      {"/", "a", "/"},
+      {"/", "a/b", "/"},
+      {"/", "a/b/", "/"},
       {"//foo/", "//foo", "."},
       {"//foo", "//foo/", "."},
 #endif



More information about the libcxx-commits mailing list