[libcxx] r337664 - Implement filesystem_error::what() and improve reporting.

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Sun Jul 22 19:00:53 PDT 2018


Author: ericwf
Date: Sun Jul 22 19:00:52 2018
New Revision: 337664

URL: http://llvm.org/viewvc/llvm-project?rev=337664&view=rev
Log:
Implement filesystem_error::what() and improve reporting.

This patch implements the `what()` for filesystem errors. The message
includes the 'what_arg', any paths that were specified, and the
error code message.

Additionally this patch refactors how errors are created, making it easier
to report them correctly.

Added:
    libcxx/trunk/test/support/format_string.hpp
Modified:
    libcxx/trunk/include/experimental/filesystem
    libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp
    libcxx/trunk/src/experimental/filesystem/filesystem_common.h
    libcxx/trunk/src/experimental/filesystem/operations.cpp
    libcxx/trunk/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp
    libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
    libcxx/trunk/test/support/filesystem_test_helper.hpp
    libcxx/trunk/www/cxx2a_status.html

Modified: libcxx/trunk/include/experimental/filesystem
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/experimental/filesystem?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/include/experimental/filesystem (original)
+++ libcxx/trunk/include/experimental/filesystem Sun Jul 22 19:00:52 2018
@@ -1265,40 +1265,51 @@ public:
     _LIBCPP_INLINE_VISIBILITY
     filesystem_error(const string& __what, error_code __ec)
         : system_error(__ec, __what),
-          __paths_(make_shared<_Storage>(path(), path()))
-    {}
+          __storage_(make_shared<_Storage>(path(), path())) {
+      __create_what(0);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     filesystem_error(const string& __what, const path& __p1, error_code __ec)
         : system_error(__ec, __what),
-        __paths_(make_shared<_Storage>(__p1, path()))
-    {}
+          __storage_(make_shared<_Storage>(__p1, path())) {
+      __create_what(1);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     filesystem_error(const string& __what, const path& __p1, const path& __p2,
                      error_code __ec)
         : system_error(__ec, __what),
-          __paths_(make_shared<_Storage>(__p1, __p2))
-    {}
+          __storage_(make_shared<_Storage>(__p1, __p2)) {
+      __create_what(2);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
-    const path& path1() const _NOEXCEPT {
-        return __paths_->first;
-    }
+    const path& path1() const _NOEXCEPT { return __storage_->__p1_; }
 
     _LIBCPP_INLINE_VISIBILITY
-    const path& path2() const _NOEXCEPT {
-        return __paths_->second;
-    }
+    const path& path2() const _NOEXCEPT { return __storage_->__p2_; }
 
     ~filesystem_error() override; // key function
 
-    // TODO(ericwf): Create a custom error message.
-    //const char* what() const _NOEXCEPT;
+    _LIBCPP_INLINE_VISIBILITY
+    const char* what() const _NOEXCEPT override {
+      return __storage_->__what_.c_str();
+    }
+
+    _LIBCPP_FUNC_VIS
+    void __create_what(int __num_paths);
 
-private:
-    typedef pair<path, path> _Storage;
-    shared_ptr<_Storage> __paths_;
+  private:
+    struct _Storage {
+      _LIBCPP_INLINE_VISIBILITY
+      _Storage(const path& __p1, const path& __p2) : __p1_(__p1), __p2_(__p2) {}
+
+      path __p1_;
+      path __p2_;
+      string __what_;
+    };
+    shared_ptr<_Storage> __storage_;
 };
 
 template <class... _Args>
@@ -1315,7 +1326,6 @@ void __throw_filesystem_error(_Args&&...
 }
 #endif
 
-
 // operational functions
 
 _LIBCPP_FUNC_VIS
@@ -2226,12 +2236,13 @@ private:
         return;
       }
       if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
-        __throw_filesystem_error(__msg, __p_, _Path{}, __ec);
+        __throw_filesystem_error(__msg, __p_, __ec);
     }
 
     _LIBCPP_INLINE_VISIBILITY
     void __refresh(error_code* __ec = nullptr) {
-      __handle_error("refresh", __ec, __do_refresh(), /*allow_dne*/ true);
+      __handle_error("in directory_entry::refresh", __ec, __do_refresh(),
+                     /*allow_dne*/ true);
     }
 
     _LIBCPP_INLINE_VISIBILITY
@@ -2322,11 +2333,11 @@ private:
       case _RefreshNonSymlink: {
         error_code __m_ec;
         file_status __st(__get_ft(&__m_ec));
-        __handle_error("directory_entry::file_size", __ec, __m_ec);
+        __handle_error("in directory_entry::file_size", __ec, __m_ec);
         if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) {
           errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory
                                                          : errc::not_supported;
-          __handle_error("directory_entry::file_size", __ec,
+          __handle_error("in directory_entry::file_size", __ec,
                          make_error_code(__err_kind));
         }
         return __data_.__size_;
@@ -2347,7 +2358,7 @@ private:
       case _RefreshNonSymlink: {
         error_code __m_ec;
         (void)__get_ft(&__m_ec);
-        __handle_error("directory_entry::hard_link_count", __ec, __m_ec);
+        __handle_error("in directory_entry::hard_link_count", __ec, __m_ec);
         return __data_.__nlink_;
       }
       }
@@ -2366,10 +2377,10 @@ private:
       case _RefreshNonSymlink: {
         error_code __m_ec;
         file_status __st(__get_ft(&__m_ec));
-        __handle_error("directory_entry::last_write_time", __ec, __m_ec);
+        __handle_error("in directory_entry::last_write_time", __ec, __m_ec);
         if (_VSTD_FS::exists(__st) &&
             __data_.__write_time_ == file_time_type::min())
-          __handle_error("directory_entry::last_write_time", __ec,
+          __handle_error("in directory_entry::last_write_time", __ec,
                          make_error_code(errc::value_too_large));
         return __data_.__write_time_;
       }

Modified: libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp Sun Jul 22 19:00:52 2018
@@ -88,7 +88,7 @@ static file_time_type get_write_time(con
 } // namespace
 } // namespace detail
 
-using detail::set_or_throw;
+using detail::ErrorHandler;
 
 #if defined(_LIBCPP_WIN32API)
 class __dir_stream {
@@ -231,27 +231,30 @@ public:
 directory_iterator::directory_iterator(const path& p, error_code *ec,
                                        directory_options opts)
 {
-    std::error_code m_ec;
-    __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
-    if (ec) *ec = m_ec;
-    if (!__imp_->good()) {
-        __imp_.reset();
-        if (m_ec)
-            set_or_throw(m_ec, ec,
-                         "directory_iterator::directory_iterator(...)", p);
-    }
+  ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
+
+  std::error_code m_ec;
+  __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
+  if (ec)
+    *ec = m_ec;
+  if (!__imp_->good()) {
+    __imp_.reset();
+    if (m_ec)
+      err.report(m_ec);
+  }
 }
 
 directory_iterator& directory_iterator::__increment(error_code *ec)
 {
     _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
+    ErrorHandler<void> err("directory_iterator::operator++()", ec);
+
     std::error_code m_ec;
     if (!__imp_->advance(m_ec)) {
-        __imp_.reset();
-        if (m_ec)
-            set_or_throw(m_ec, ec, "directory_iterator::operator++()");
-    } else {
-        if (ec) ec->clear();
+      path root = std::move(__imp_->__root_);
+      __imp_.reset();
+      if (m_ec)
+        err.report(m_ec, "at root \"%s\"", root);
     }
     return *this;
 
@@ -273,15 +276,18 @@ recursive_directory_iterator::recursive_
     directory_options opt, error_code *ec)
     : __imp_(nullptr), __rec_(true)
 {
-    if (ec) ec->clear();
-    std::error_code m_ec;
-    __dir_stream new_s(p, opt, m_ec);
-    if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
-    if (m_ec || !new_s.good()) return;
-
-    __imp_ = _VSTD::make_shared<__shared_imp>();
-    __imp_->__options_ = opt;
-    __imp_->__stack_.push(_VSTD::move(new_s));
+  ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
+
+  std::error_code m_ec;
+  __dir_stream new_s(p, opt, m_ec);
+  if (m_ec)
+    err.report(m_ec);
+  if (m_ec || !new_s.good())
+    return;
+
+  __imp_ = _VSTD::make_shared<__shared_imp>();
+  __imp_->__options_ = opt;
+  __imp_->__stack_.push(_VSTD::move(new_s));
 }
 
 void recursive_directory_iterator::__pop(error_code* ec)
@@ -321,42 +327,50 @@ recursive_directory_iterator::__incremen
 }
 
 void recursive_directory_iterator::__advance(error_code* ec) {
-    // REQUIRES: ec must be cleared before calling this function.
-    const directory_iterator end_it;
-    auto& stack = __imp_->__stack_;
-    std::error_code m_ec;
-    while (stack.size() > 0) {
-        if (stack.top().advance(m_ec))
-            return;
-        if (m_ec) break;
-        stack.pop();
-    }
-    __imp_.reset();
+  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
+
+  const directory_iterator end_it;
+  auto& stack = __imp_->__stack_;
+  std::error_code m_ec;
+  while (stack.size() > 0) {
+    if (stack.top().advance(m_ec))
+      return;
     if (m_ec)
-        set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
+      break;
+    stack.pop();
+  }
+
+    if (m_ec) {
+      path root = std::move(stack.top().__root_);
+      __imp_.reset();
+      err.report(m_ec, "at root \"%s\"", root);
+    } else {
+      __imp_.reset();
+    }
 }
 
 bool recursive_directory_iterator::__try_recursion(error_code *ec) {
-    bool rec_sym =
-        bool(options() & directory_options::follow_directory_symlink);
+  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
 
-    auto& curr_it = __imp_->__stack_.top();
+  bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
 
-    bool skip_rec = false;
-    std::error_code m_ec;
-    if (!rec_sym) {
-      file_status st = curr_it.__entry_.symlink_status(m_ec);
-      if (m_ec && status_known(st))
-        m_ec.clear();
-      if (m_ec || is_symlink(st) || !is_directory(st))
-        skip_rec = true;
-    } else {
-      file_status st = curr_it.__entry_.status(m_ec);
-      if (m_ec && status_known(st))
-        m_ec.clear();
-      if (m_ec || !is_directory(st))
-        skip_rec = true;
-    }
+  auto& curr_it = __imp_->__stack_.top();
+
+  bool skip_rec = false;
+  std::error_code m_ec;
+  if (!rec_sym) {
+    file_status st = curr_it.__entry_.symlink_status(m_ec);
+    if (m_ec && status_known(st))
+      m_ec.clear();
+    if (m_ec || is_symlink(st) || !is_directory(st))
+      skip_rec = true;
+  } else {
+    file_status st = curr_it.__entry_.status(m_ec);
+    if (m_ec && status_known(st))
+      m_ec.clear();
+    if (m_ec || !is_directory(st))
+      skip_rec = true;
+  }
 
     if (!skip_rec) {
         __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
@@ -371,9 +385,9 @@ bool recursive_directory_iterator::__try
         if (m_ec.value() == EACCES && allow_eacess) {
           if (ec) ec->clear();
         } else {
+          path at_ent = std::move(curr_it.__entry_.__p_);
           __imp_.reset();
-          set_or_throw(m_ec, ec,
-                       "recursive_directory_iterator::operator++()");
+          err.report(m_ec, "attempting recursion into \"%s\"", at_ent);
         }
     }
     return false;

Modified: libcxx/trunk/src/experimental/filesystem/filesystem_common.h
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/filesystem_common.h?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/filesystem_common.h (original)
+++ libcxx/trunk/src/experimental/filesystem/filesystem_common.h Sun Jul 22 19:00:52 2018
@@ -11,6 +11,7 @@
 #define FILESYSTEM_COMMON_H
 
 #include "experimental/__config"
+#include "array"
 #include "chrono"
 #include "cstdlib"
 #include "climits"
@@ -66,27 +67,150 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FIL
 namespace detail {
 namespace {
 
+static std::string format_string_imp(const char* msg, ...) {
+  // we might need a second shot at this, so pre-emptivly make a copy
+  struct GuardVAList {
+    va_list& target;
+    bool active = true;
+    void clear() {
+      if (active)
+        va_end(target);
+      active = false;
+    }
+    ~GuardVAList() {
+      if (active)
+        va_end(target);
+    }
+  };
+  va_list args;
+  va_start(args, msg);
+  GuardVAList args_guard = {args};
+
+  va_list args_cp;
+  va_copy(args_cp, args);
+  GuardVAList args_copy_guard = {args_cp};
+
+  std::array<char, 256> local_buff;
+  std::size_t size = local_buff.size();
+  auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
+
+  args_copy_guard.clear();
+
+  // handle empty expansion
+  if (ret == 0)
+    return std::string{};
+  if (static_cast<std::size_t>(ret) < size)
+    return std::string(local_buff.data());
+
+  // we did not provide a long enough buffer on our first attempt.
+  // add 1 to size to account for null-byte in size cast to prevent overflow
+  size = static_cast<std::size_t>(ret) + 1;
+  auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
+  ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
+  return std::string(buff_ptr.get());
+}
+
+const char* unwrap(string const& s) { return s.c_str(); }
+const char* unwrap(path const& p) { return p.native().c_str(); }
+template <class Arg>
+Arg const& unwrap(Arg const& a) {
+  static_assert(!is_class<Arg>::value, "cannot pass class here");
+  return a;
+}
+
+template <class... Args>
+std::string format_string(const char* fmt, Args const&... args) {
+  return format_string_imp(fmt, unwrap(args)...);
+}
+
 std::error_code capture_errno() {
   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
   return std::error_code(errno, std::generic_category());
 }
 
-void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
-                  const char* msg, path const& p = {}, path const& p2 = {}) {
-  if (ec) {
-    *ec = m_ec;
-  } else {
-    string msg_s("std::experimental::filesystem::");
-    msg_s += msg;
-    __throw_filesystem_error(msg_s, p, p2, m_ec);
-  }
+template <class T>
+T error_value();
+template <>
+constexpr void error_value<void>() {}
+template <>
+constexpr bool error_value<bool>() {
+  return false;
 }
-
-void set_or_throw(std::error_code* ec, const char* msg, path const& p = {},
-                  path const& p2 = {}) {
-  return set_or_throw(capture_errno(), ec, msg, p, p2);
+template <>
+constexpr uintmax_t error_value<uintmax_t>() {
+  return uintmax_t(-1);
+}
+template <>
+constexpr file_time_type error_value<file_time_type>() {
+  return file_time_type::min();
+}
+template <>
+path error_value<path>() {
+  return {};
 }
 
+template <class T>
+struct ErrorHandler {
+  const char* func_name;
+  error_code* ec = nullptr;
+  const path* p1 = nullptr;
+  const path* p2 = nullptr;
+
+  ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
+               const path* p2 = nullptr)
+      : func_name(fname), ec(ec), p1(p1), p2(p2) {
+    if (ec)
+      ec->clear();
+  }
+
+  T report(const error_code& m_ec) const {
+    if (ec) {
+      *ec = m_ec;
+      return error_value<T>();
+    }
+    string what = string("in ") + func_name;
+    switch (bool(p1) + bool(p2)) {
+    case 0:
+      __throw_filesystem_error(what, m_ec);
+    case 1:
+      __throw_filesystem_error(what, *p1, m_ec);
+    case 2:
+      __throw_filesystem_error(what, *p1, *p2, m_ec);
+    }
+    _LIBCPP_UNREACHABLE();
+  }
+
+  template <class... Args>
+  T report(const error_code& m_ec, const char* msg, Args const&... args) const {
+    if (ec) {
+      *ec = m_ec;
+      return error_value<T>();
+    }
+    string what =
+        string("in ") + func_name + ": " + format_string(msg, args...);
+    switch (bool(p1) + bool(p2)) {
+    case 0:
+      __throw_filesystem_error(what, m_ec);
+    case 1:
+      __throw_filesystem_error(what, *p1, m_ec);
+    case 2:
+      __throw_filesystem_error(what, *p1, *p2, m_ec);
+    }
+    _LIBCPP_UNREACHABLE();
+  }
+
+  T report(errc const& err) const { return report(make_error_code(err)); }
+
+  template <class... Args>
+  T report(errc const& err, const char* msg, Args const&... args) const {
+    return report(make_error_code(err), msg, args...);
+  }
+
+private:
+  ErrorHandler(ErrorHandler const&) = delete;
+  ErrorHandler& operator=(ErrorHandler const&) = delete;
+};
+
 namespace time_util {
 
 using namespace chrono;

Modified: libcxx/trunk/src/experimental/filesystem/operations.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/operations.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/operations.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/operations.cpp Sun Jul 22 19:00:52 2018
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "experimental/filesystem"
+#include "array"
 #include "iterator"
 #include "fstream"
 #include "random"  /* for unique_path */
@@ -364,7 +365,8 @@ file_status create_file_status(std::erro
   if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
     return file_status(file_type::not_found);
   } else if (m_ec) {
-    set_or_throw(m_ec, ec, "posix_stat", p);
+    ErrorHandler<void> err("posix_stat", ec, &p);
+    err.report(m_ec, "failed to determine attributes for the specified path");
     return file_status(file_type::none);
   }
   // else
@@ -453,10 +455,30 @@ file_status FileDescriptor::refresh_stat
 }
 }} // end namespace detail
 
-using detail::set_or_throw;
-using parser::string_view_t;
-using parser::PathParser;
+using detail::capture_errno;
+using detail::ErrorHandler;
+using detail::StatT;
 using parser::createView;
+using parser::PathParser;
+using parser::string_view_t;
+
+void filesystem_error::__create_what(int __num_paths) {
+  const char* derived_what = system_error::what();
+  __storage_->__what_ = [&]() -> string {
+    const char* p1 = path1().native().empty() ? "\"\"" : path1().c_str();
+    const char* p2 = path2().native().empty() ? "\"\"" : path2().c_str();
+    switch (__num_paths) {
+    default:
+      return detail::format_string("filesystem error: %s", derived_what);
+    case 1:
+      return detail::format_string("filesystem error: %s [%s]", derived_what,
+                                   p1);
+    case 2:
+      return detail::format_string("filesystem error: %s [%s] [%s]",
+                                   derived_what, p1, p2);
+    }
+  }();
+}
 
 static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
   if (ec) ec->clear();
@@ -476,48 +498,46 @@ path __absolute(const path& p, std::erro
 path __canonical(path const & orig_p, std::error_code *ec)
 {
     path cwd;
+    ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
+
     path p = __do_absolute(orig_p, &cwd, ec);
     char buff[PATH_MAX + 1];
     char *ret;
-    if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
-        set_or_throw(ec, "canonical", orig_p, cwd);
-        return {};
-    }
-    if (ec) ec->clear();
+    if ((ret = ::realpath(p.c_str(), buff)) == nullptr)
+      return err.report(capture_errno());
     return {ret};
 }
 
 void __copy(const path& from, const path& to, copy_options options,
             std::error_code *ec)
 {
-    const bool sym_status = bool(options &
-        (copy_options::create_symlinks | copy_options::skip_symlinks));
+  ErrorHandler<void> err("copy", ec, &from, &to);
 
-    const bool sym_status2 = bool(options &
-        copy_options::copy_symlinks);
+  const bool sym_status = bool(
+      options & (copy_options::create_symlinks | copy_options::skip_symlinks));
 
-    std::error_code m_ec1;
-    struct ::stat f_st = {};
-    const file_status f = sym_status || sym_status2
-                                     ? detail::posix_lstat(from, f_st, &m_ec1)
-                                     : detail::posix_stat(from,  f_st, &m_ec1);
-    if (m_ec1)
-        return set_or_throw(m_ec1, ec, "copy", from, to);
-
-    struct ::stat t_st = {};
-    const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
-                                     : detail::posix_stat(to, t_st, &m_ec1);
-
-    if (not status_known(t))
-        return set_or_throw(m_ec1, ec, "copy", from, to);
-
-    if (!exists(f) || is_other(f) || is_other(t)
-        || (is_directory(f) && is_regular_file(t))
-        || detail::stat_equivalent(f_st, t_st))
-    {
-        return set_or_throw(make_error_code(errc::function_not_supported),
-                            ec, "copy", from, to);
-    }
+  const bool sym_status2 = bool(options & copy_options::copy_symlinks);
+
+  std::error_code m_ec1;
+  struct ::stat f_st = {};
+  const file_status f = sym_status || sym_status2
+                            ? detail::posix_lstat(from, f_st, &m_ec1)
+                            : detail::posix_stat(from, f_st, &m_ec1);
+  if (m_ec1)
+    return err.report(m_ec1);
+
+  struct ::stat t_st = {};
+  const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
+                                   : detail::posix_stat(to, t_st, &m_ec1);
+
+  if (not status_known(t))
+    return err.report(m_ec1);
+
+  if (!exists(f) || is_other(f) || is_other(t) ||
+      (is_directory(f) && is_regular_file(t)) ||
+      detail::stat_equivalent(f_st, t_st)) {
+    return err.report(errc::function_not_supported);
+  }
 
     if (ec) ec->clear();
 
@@ -527,8 +547,7 @@ void __copy(const path& from, const path
         } else if (not exists(t)) {
             __copy_symlink(from, to, ec);
         } else {
-            set_or_throw(make_error_code(errc::file_exists),
-                         ec, "copy", from, to);
+          return err.report(errc::file_exists);
         }
         return;
     }
@@ -550,7 +569,7 @@ void __copy(const path& from, const path
         return;
     }
     else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
-        return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy");
+      return err.report(errc::is_a_directory);
     }
     else if (is_directory(f) && (bool(copy_options::recursive & options) ||
              copy_options::none == options)) {
@@ -565,7 +584,9 @@ void __copy(const path& from, const path
         if (ec && *ec) { return; }
         std::error_code m_ec2;
         for (; it != directory_iterator(); it.increment(m_ec2)) {
-            if (m_ec2) return set_or_throw(m_ec2, ec, "copy", from, to);
+          if (m_ec2) {
+            return err.report(m_ec2);
+          }
             __copy(it->path(), to / it->path().filename(),
                    options | copy_options::__in_recursive_copy, ec);
             if (ec && *ec) { return; }
@@ -672,28 +693,20 @@ bool __copy_file(const path& from, const
                  std::error_code *ec)
 {
   using detail::FileDescriptor;
-  using detail::StatT;
-
-  if (ec)
-    ec->clear();
-
-  auto Error = [&](const error_code& error_ec) {
-    set_or_throw(error_ec, ec, "copy_file", from, to);
-    return false;
-  };
+  ErrorHandler<bool> err("copy_file", ec, &to, &from);
 
   std::error_code m_ec;
   FileDescriptor from_fd =
       FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK);
   if (m_ec)
-    return Error(m_ec);
+    return err.report(m_ec);
 
   auto from_st = from_fd.get_status();
   StatT const& from_stat = from_fd.get_stat();
   if (!is_regular_file(from_st)) {
     if (not m_ec)
       m_ec = make_error_code(errc::not_supported);
-    return Error(m_ec);
+    return err.report(m_ec);
   }
 
   const bool skip_existing = bool(copy_options::skip_existing & options);
@@ -704,14 +717,14 @@ bool __copy_file(const path& from, const
   StatT to_stat_path;
   file_status to_st = detail::posix_stat(to, to_stat_path, &m_ec);
   if (!status_known(to_st))
-    return Error(m_ec);
+    return err.report(m_ec);
 
   const bool to_exists = exists(to_st);
   if (to_exists && !is_regular_file(to_st))
-    return Error(make_error_code(errc::not_supported));
+    return err.report(make_error_code(errc::not_supported));
 
   if (to_exists && detail::stat_equivalent(from_stat, to_stat_path))
-    return Error(make_error_code(errc::file_exists));
+    return err.report(make_error_code(errc::file_exists));
 
   if (to_exists && skip_existing)
     return false;
@@ -729,7 +742,7 @@ bool __copy_file(const path& from, const
     }
     if (!to_exists || overwrite_existing)
       return true;
-    return Error(make_error_code(errc::file_exists));
+    return err.report(errc::file_exists);
   }();
   if (!ShouldCopy)
     return false;
@@ -742,25 +755,25 @@ bool __copy_file(const path& from, const
   FileDescriptor to_fd = FileDescriptor::create_with_status(
       &to, m_ec, to_open_flags, from_stat.st_mode);
   if (m_ec)
-    return Error(m_ec);
+    return err.report(m_ec);
 
   if (to_exists) {
     // Check that the file we initially stat'ed is equivalent to the one
     // we opened.
     // FIXME: report this better.
     if (!detail::stat_equivalent(to_stat_path, to_fd.get_stat()))
-      return Error(make_error_code(errc::bad_file_descriptor));
+      return err.report(errc::bad_file_descriptor);
 
     // Set the permissions and truncate the file we opened.
     if (!detail::posix_fchmod(to_fd, from_stat, m_ec))
-      return Error(m_ec);
+      return err.report(m_ec);
     if (!detail::posix_ftruncate(to_fd, 0, m_ec))
-      return Error(m_ec);
+      return err.report(m_ec);
   }
 
   if (!copy_file_impl(from_fd, to_fd, m_ec)) {
     // FIXME: Remove the dest file if we failed, and it didn't exist previously.
-    return Error(m_ec);
+    return err.report(m_ec);
   }
 
   return true;
@@ -779,213 +792,192 @@ void __copy_symlink(const path& existing
 
 bool __create_directories(const path& p, std::error_code *ec)
 {
-    std::error_code m_ec;
-    auto const st = detail::posix_stat(p, &m_ec);
-    if (!status_known(st)) {
-        set_or_throw(m_ec, ec, "create_directories", p);
-        return false;
-    }
-    else if (is_directory(st)) {
-        if (ec) ec->clear();
-        return false;
-    }
-    else if (exists(st)) {
-        set_or_throw(make_error_code(errc::file_exists),
-                     ec, "create_directories", p);
-        return false;
-    }
+  ErrorHandler<bool> err("create_directories", ec, &p);
 
-    const path parent = p.parent_path();
-    if (!parent.empty()) {
-        const file_status parent_st = status(parent, m_ec);
-        if (not status_known(parent_st)) {
-            set_or_throw(m_ec, ec, "create_directories", p);
-            return false;
-        }
-        if (not exists(parent_st)) {
-            __create_directories(parent, ec);
-            if (ec && *ec) { return false; }
-        }
+  std::error_code m_ec;
+  auto const st = detail::posix_stat(p, &m_ec);
+  if (!status_known(st))
+    return err.report(m_ec);
+  else if (is_directory(st))
+    return false;
+  else if (exists(st))
+    return err.report(errc::file_exists);
+
+  const path parent = p.parent_path();
+  if (!parent.empty()) {
+    const file_status parent_st = status(parent, m_ec);
+    if (not status_known(parent_st))
+      return err.report(m_ec);
+    if (not exists(parent_st)) {
+      __create_directories(parent, ec);
+      if (ec && *ec) {
+        return false;
+      }
     }
+  }
     return __create_directory(p, ec);
 }
 
 bool __create_directory(const path& p, std::error_code *ec)
 {
-    if (ec) ec->clear();
-    if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
-        return true;
-    if (errno != EEXIST || !is_directory(p))
-        set_or_throw(ec, "create_directory", p);
-    return false;
+  ErrorHandler<bool> err("create_directory", ec, &p);
+
+  if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
+    return true;
+  if (errno != EEXIST || !is_directory(p))
+    err.report(capture_errno());
+  return false;
 }
 
 bool __create_directory(path const & p, path const & attributes,
                         std::error_code *ec)
 {
-    struct ::stat attr_stat;
-    std::error_code mec;
-    auto st = detail::posix_stat(attributes, attr_stat, &mec);
-    if (!status_known(st)) {
-        set_or_throw(mec, ec, "create_directory", p, attributes);
-        return false;
-    }
-    if (ec) ec->clear();
-    if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
-        return true;
-    if (errno != EEXIST || !is_directory(p))
-        set_or_throw(ec, "create_directory", p, attributes);
-    return false;
+  ErrorHandler<bool> err("create_directory", ec, &p, &attributes);
+
+  StatT attr_stat;
+  std::error_code mec;
+  auto st = detail::posix_stat(attributes, attr_stat, &mec);
+  if (!status_known(st))
+    return err.report(mec);
+
+  if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
+    return true;
+  if (errno != EEXIST || !is_directory(p))
+    err.report(capture_errno());
+  return false;
 }
 
-void __create_directory_symlink(path const & from, path const & to,
-                                std::error_code *ec){
-    if (::symlink(from.c_str(), to.c_str()) != 0)
-        set_or_throw(ec, "create_directory_symlink", from, to);
-    else if (ec)
-        ec->clear();
+void __create_directory_symlink(path const& from, path const& to,
+                                std::error_code* ec) {
+  ErrorHandler<void> err("create_directory_symlink", ec, &from, &to);
+  if (::symlink(from.c_str(), to.c_str()) != 0)
+    return err.report(capture_errno());
 }
 
 void __create_hard_link(const path& from, const path& to, std::error_code *ec){
-    if (::link(from.c_str(), to.c_str()) == -1)
-        set_or_throw(ec, "create_hard_link", from, to);
-    else if (ec)
-        ec->clear();
+  ErrorHandler<void> err("create_hard_link", ec, &from, &to);
+  if (::link(from.c_str(), to.c_str()) == -1)
+    return err.report(capture_errno());
 }
 
 void __create_symlink(path const & from, path const & to, std::error_code *ec) {
-
-    if (::symlink(from.c_str(), to.c_str()) == -1)
-        set_or_throw(ec, "create_symlink", from, to);
-    else if (ec)
-        ec->clear();
+  ErrorHandler<void> err("create_symlink", ec, &from, &to);
+  if (::symlink(from.c_str(), to.c_str()) == -1)
+    return err.report(capture_errno());
 }
 
 path __current_path(std::error_code *ec) {
-    auto size = ::pathconf(".", _PC_PATH_MAX);
-    _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
+  ErrorHandler<path> err("current_path", ec);
 
-    auto buff = std::unique_ptr<char[]>(new char[size + 1]);
-    char* ret;
-    if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) {
-        set_or_throw(ec, "current_path");
-        return {};
-    }
-    if (ec) ec->clear();
-    return {buff.get()};
+  auto size = ::pathconf(".", _PC_PATH_MAX);
+  _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
+
+  auto buff = std::unique_ptr<char[]>(new char[size + 1]);
+  char* ret;
+  if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr)
+    return err.report(capture_errno(), "call to getcwd failed");
+
+  return {buff.get()};
 }
 
 void __current_path(const path& p, std::error_code *ec) {
-    if (::chdir(p.c_str()) == -1)
-        set_or_throw(ec, "current_path", p);
-    else if (ec)
-        ec->clear();
+  ErrorHandler<void> err("current_path", ec, &p);
+  if (::chdir(p.c_str()) == -1)
+    err.report(capture_errno());
 }
 
 bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
 {
-    auto make_unsupported_error = [&]() {
-      set_or_throw(make_error_code(errc::not_supported), ec,
-                     "equivalent", p1, p2);
-      return false;
-    };
-    std::error_code ec1, ec2;
-    struct ::stat st1 = {};
-    struct ::stat st2 = {};
-    auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
-    if (!exists(s1))
-      return make_unsupported_error();
-    auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
-    if (!exists(s2))
-      return make_unsupported_error();
-    if (ec) ec->clear();
-    return detail::stat_equivalent(st1, st2);
+  ErrorHandler<bool> err("equivalent", ec, &p1, &p2);
+
+  std::error_code ec1, ec2;
+  StatT st1 = {}, st2 = {};
+  auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
+  if (!exists(s1))
+    return err.report(errc::not_supported);
+  auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
+  if (!exists(s2))
+    return err.report(errc::not_supported);
+
+  return detail::stat_equivalent(st1, st2);
 }
 
 
 std::uintmax_t __file_size(const path& p, std::error_code *ec)
 {
-    std::error_code m_ec;
-    struct ::stat st;
-    file_status fst = detail::posix_stat(p, st, &m_ec);
-    if (!exists(fst) || !is_regular_file(fst)) {
-      errc error_kind =
-          is_directory(fst) ? errc::is_a_directory : errc::not_supported;
-      if (!m_ec)
-        m_ec = make_error_code(error_kind);
-      set_or_throw(m_ec, ec, "file_size", p);
-      return static_cast<uintmax_t>(-1);
-    }
+  ErrorHandler<uintmax_t> err("file_size", ec, &p);
+
+  std::error_code m_ec;
+  struct ::stat st;
+  file_status fst = detail::posix_stat(p, st, &m_ec);
+  if (!exists(fst) || !is_regular_file(fst)) {
+    errc error_kind =
+        is_directory(fst) ? errc::is_a_directory : errc::not_supported;
+    if (!m_ec)
+      m_ec = make_error_code(error_kind);
+    return err.report(m_ec);
+  }
     // is_regular_file(p) == true
-    if (ec) ec->clear();
     return static_cast<std::uintmax_t>(st.st_size);
 }
 
 std::uintmax_t __hard_link_count(const path& p, std::error_code *ec)
 {
-    std::error_code m_ec;
-    struct ::stat st;
-    detail::posix_stat(p, st, &m_ec);
-    if (m_ec) {
-        set_or_throw(m_ec, ec, "hard_link_count", p);
-        return static_cast<std::uintmax_t>(-1);
-    }
-    if (ec) ec->clear();
-    return static_cast<std::uintmax_t>(st.st_nlink);
+  ErrorHandler<uintmax_t> err("hard_link_count", ec, &p);
+
+  std::error_code m_ec;
+  StatT st;
+  detail::posix_stat(p, st, &m_ec);
+  if (m_ec)
+    return err.report(m_ec);
+  return static_cast<std::uintmax_t>(st.st_nlink);
 }
 
 
 bool __fs_is_empty(const path& p, std::error_code *ec)
 {
-    if (ec) ec->clear();
-    std::error_code m_ec;
-    struct ::stat pst;
-    auto st = detail::posix_stat(p, pst, &m_ec);
-    if (m_ec) {
-        set_or_throw(m_ec, ec, "is_empty", p);
-        return false;
-    }
-    else if (!is_directory(st) && !is_regular_file(st)) {
-        m_ec = make_error_code(errc::not_supported);
-        set_or_throw(m_ec, ec, "is_empty");
-        return false;
-    }
-    else if (is_directory(st)) {
-        auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
-        if (ec && *ec)
-            return false;
-        return it == directory_iterator{};
-    }
-    else if (is_regular_file(st))
-        return static_cast<std::uintmax_t>(pst.st_size) == 0;
+  ErrorHandler<bool> err("is_empty", ec, &p);
 
-    _LIBCPP_UNREACHABLE();
+  std::error_code m_ec;
+  StatT pst;
+  auto st = detail::posix_stat(p, pst, &m_ec);
+  if (m_ec)
+    return err.report(m_ec);
+  else if (!is_directory(st) && !is_regular_file(st))
+    return err.report(errc::not_supported);
+  else if (is_directory(st)) {
+    auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
+    if (ec && *ec)
+      return false;
+    return it == directory_iterator{};
+  } else if (is_regular_file(st))
+    return static_cast<std::uintmax_t>(pst.st_size) == 0;
+
+  _LIBCPP_UNREACHABLE();
 }
 
-static file_time_type __extract_last_write_time(const path& p,
-                                                const struct ::stat& st,
+static file_time_type __extract_last_write_time(const path& p, const StatT& st,
                                                 error_code* ec) {
   using detail::FSTime;
+  ErrorHandler<file_time_type> err("last_write_time", ec, &p);
+
   auto ts = detail::extract_mtime(st);
-  if (!FSTime::is_representable(ts)) {
-    set_or_throw(make_error_code(errc::value_too_large), ec, "last_write_time",
-                 p);
-    return file_time_type::min();
-  }
+  if (!FSTime::is_representable(ts))
+    return err.report(errc::value_too_large);
+
   return FSTime::convert_timespec(ts);
 }
 
 file_time_type __last_write_time(const path& p, std::error_code *ec)
 {
     using namespace ::std::chrono;
+    ErrorHandler<file_time_type> err("last_write_time", ec, &p);
+
     std::error_code m_ec;
-    struct ::stat st;
+    StatT st;
     detail::posix_stat(p, st, &m_ec);
-    if (m_ec) {
-        set_or_throw(m_ec, ec, "last_write_time", p);
-        return file_time_type::min();
-    }
-    if (ec) ec->clear();
+    if (m_ec)
+      return err.report(m_ec);
     return __extract_last_write_time(p, st, ec);
 }
 
@@ -995,6 +987,8 @@ void __last_write_time(const path& p, fi
     using namespace std::chrono;
     using namespace detail;
 
+    ErrorHandler<void> err("last_write_time", ec, &p);
+
     std::error_code m_ec;
     TimeStructArray tbuf;
 #if !defined(_LIBCXX_USE_UTIMENSAT)
@@ -1003,99 +997,92 @@ void __last_write_time(const path& p, fi
     // ::utimes
     struct ::stat st;
     file_status fst = detail::posix_stat(p, st, &m_ec);
-    if (m_ec && !status_known(fst)) {
-        set_or_throw(m_ec, ec, "last_write_time", p);
-        return;
-    }
+    if (m_ec && !status_known(fst))
+      return err.report(m_ec);
     SetTimeStructTo(tbuf[0], detail::extract_atime(st));
 #else
     tbuf[0].tv_sec = 0;
     tbuf[0].tv_nsec = UTIME_OMIT;
 #endif
-    if (SetTimeStructTo(tbuf[1], new_time)) {
-      set_or_throw(make_error_code(errc::invalid_argument), ec,
-                   "last_write_time", p);
-      return;
-    }
+    if (SetTimeStructTo(tbuf[1], new_time))
+      return err.report(errc::invalid_argument);
 
     SetFileTimes(p, tbuf, m_ec);
     if (m_ec)
-        set_or_throw(m_ec, ec, "last_write_time", p);
-    else if (ec)
-        ec->clear();
+      return err.report(m_ec);
 }
 
 
 void __permissions(const path& p, perms prms, perm_options opts,
                    std::error_code *ec)
 {
-    auto has_opt = [&](perm_options o) { return bool(o & opts); };
-    const bool resolve_symlinks = !has_opt(perm_options::nofollow);
-    const bool add_perms = has_opt(perm_options::add);
-    const bool remove_perms = has_opt(perm_options::remove);
-    _LIBCPP_ASSERT(
-       (add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
-       "One and only one of the perm_options constants replace, add, or remove "
-        "is present in opts");
-
-    bool set_sym_perms = false;
-    prms &= perms::mask;
-    if (!resolve_symlinks || (add_perms || remove_perms)) {
-        std::error_code m_ec;
-        file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
-                                          : detail::posix_lstat(p, &m_ec);
-        set_sym_perms = is_symlink(st);
-        if (m_ec) return set_or_throw(m_ec, ec, "permissions", p);
-        _LIBCPP_ASSERT(st.permissions() != perms::unknown,
-                       "Permissions unexpectedly unknown");
-        if (add_perms)
-            prms |= st.permissions();
-        else if (remove_perms)
-           prms = st.permissions() & ~prms;
-    }
+  ErrorHandler<void> err("permissions", ec, &p);
+
+  auto has_opt = [&](perm_options o) { return bool(o & opts); };
+  const bool resolve_symlinks = !has_opt(perm_options::nofollow);
+  const bool add_perms = has_opt(perm_options::add);
+  const bool remove_perms = has_opt(perm_options::remove);
+  _LIBCPP_ASSERT(
+      (add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
+      "One and only one of the perm_options constants replace, add, or remove "
+      "is present in opts");
+
+  bool set_sym_perms = false;
+  prms &= perms::mask;
+  if (!resolve_symlinks || (add_perms || remove_perms)) {
+    std::error_code m_ec;
+    file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
+                                      : detail::posix_lstat(p, &m_ec);
+    set_sym_perms = is_symlink(st);
+    if (m_ec)
+      return err.report(m_ec);
+    _LIBCPP_ASSERT(st.permissions() != perms::unknown,
+                   "Permissions unexpectedly unknown");
+    if (add_perms)
+      prms |= st.permissions();
+    else if (remove_perms)
+      prms = st.permissions() & ~prms;
+  }
     const auto real_perms = detail::posix_convert_perms(prms);
 
 # if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
     const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
     if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
-        return set_or_throw(ec, "permissions", p);
+      return err.report(capture_errno());
     }
 # else
     if (set_sym_perms)
-        return set_or_throw(make_error_code(errc::operation_not_supported),
-                            ec, "permissions", p);
+      return err.report(errc::operation_not_supported);
     if (::chmod(p.c_str(), real_perms) == -1) {
-        return set_or_throw(ec, "permissions", p);
+      return err.report(capture_errno());
     }
 # endif
-    if (ec) ec->clear();
 }
 
 
 path __read_symlink(const path& p, std::error_code *ec) {
-    char buff[PATH_MAX + 1];
-    std::error_code m_ec;
-    ::ssize_t ret;
-    if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
-        set_or_throw(ec, "read_symlink", p);
-        return {};
-    }
+  ErrorHandler<path> err("read_symlink", ec, &p);
+
+  char buff[PATH_MAX + 1];
+  std::error_code m_ec;
+  ::ssize_t ret;
+  if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
+    return err.report(capture_errno());
+  }
     _LIBCPP_ASSERT(ret <= PATH_MAX, "TODO");
     _LIBCPP_ASSERT(ret > 0, "TODO");
-    if (ec) ec->clear();
     buff[ret] = 0;
     return {buff};
 }
 
 
 bool __remove(const path& p, std::error_code *ec) {
-    if (ec) ec->clear();
-
-    if (::remove(p.c_str()) == -1) {
-        if (errno != ENOENT)
-            set_or_throw(ec, "remove", p);
-        return false;
-    }
+  ErrorHandler<bool> err("remove", ec, &p);
+  if (::remove(p.c_str()) == -1) {
+    if (errno != ENOENT)
+      err.report(capture_errno());
+    return false;
+  }
     return true;
 }
 
@@ -1123,45 +1110,39 @@ std::uintmax_t remove_all_impl(path cons
 } // end namespace
 
 std::uintmax_t __remove_all(const path& p, std::error_code *ec) {
-    if (ec) ec->clear();
+  ErrorHandler<uintmax_t> err("remove_all", ec, &p);
 
-    std::error_code mec;
-    auto count = remove_all_impl(p, mec);
-    if (mec) {
-        if (mec == errc::no_such_file_or_directory) {
-            return 0;
-        } else {
-            set_or_throw(mec, ec, "remove_all", p);
-            return static_cast<std::uintmax_t>(-1);
-        }
-    }
+  std::error_code mec;
+  auto count = remove_all_impl(p, mec);
+  if (mec) {
+    if (mec == errc::no_such_file_or_directory)
+      return 0;
+    return err.report(mec);
+  }
     return count;
 }
 
 void __rename(const path& from, const path& to, std::error_code *ec) {
-    if (::rename(from.c_str(), to.c_str()) == -1)
-        set_or_throw(ec, "rename", from, to);
-    else if (ec)
-        ec->clear();
+  ErrorHandler<void> err("rename", ec, &from, &to);
+  if (::rename(from.c_str(), to.c_str()) == -1)
+    err.report(capture_errno());
 }
 
 void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
-    if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1)
-        set_or_throw(ec, "resize_file", p);
-    else if (ec)
-        ec->clear();
+  ErrorHandler<void> err("resize_file", ec, &p);
+  if (::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1)
+    return err.report(capture_errno());
 }
 
 space_info __space(const path& p, std::error_code *ec) {
-    space_info si;
-    struct statvfs m_svfs = {};
-    if (::statvfs(p.c_str(), &m_svfs) == -1)  {
-        set_or_throw(ec, "space", p);
-        si.capacity = si.free = si.available =
-            static_cast<std::uintmax_t>(-1);
-        return si;
-    }
-    if (ec) ec->clear();
+  ErrorHandler<void> err("space", ec, &p);
+  space_info si;
+  struct statvfs m_svfs = {};
+  if (::statvfs(p.c_str(), &m_svfs) == -1) {
+    err.report(capture_errno());
+    si.capacity = si.free = si.available = static_cast<std::uintmax_t>(-1);
+    return si;
+  }
     // Multiply with overflow checking.
     auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
       out = other * m_svfs.f_frsize;
@@ -1183,6 +1164,8 @@ file_status __symlink_status(const path&
 }
 
 path __temp_directory_path(std::error_code* ec) {
+  ErrorHandler<path> err("temp_directory_path", ec);
+
   const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
   const char* ret = nullptr;
 
@@ -1194,20 +1177,21 @@ path __temp_directory_path(std::error_co
 
   path p(ret);
   std::error_code m_ec;
-  if (!exists(p, m_ec) || !is_directory(p, m_ec)) {
-    if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory))
-      m_ec = make_error_code(errc::not_a_directory);
-    set_or_throw(m_ec, ec, "temp_directory_path");
-    return {};
-  }
+  file_status st = detail::posix_stat(p, &m_ec);
+  if (!status_known(st))
+    return err.report(m_ec, "cannot access path \"%s\"", p);
+
+  if (!exists(st) || !is_directory(st))
+    return err.report(errc::not_a_directory, "path \"%s\" is not a directory",
+                      p);
 
-  if (ec)
-    ec->clear();
   return p;
 }
 
 
 path __weakly_canonical(const path& p, std::error_code *ec) {
+  ErrorHandler<path> err("weakly_canonical", ec, &p);
+
   if (p.empty())
     return __canonical("", ec);
 
@@ -1223,8 +1207,7 @@ path __weakly_canonical(const path& p, s
     std::error_code m_ec;
     file_status st = __status(tmp, &m_ec);
     if (!status_known(st)) {
-      set_or_throw(m_ec, ec, "weakly_canonical", p);
-      return {};
+      return err.report(m_ec);
     } else if (exists(st)) {
       result = __canonical(tmp, ec);
       break;

Modified: libcxx/trunk/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp (original)
+++ libcxx/trunk/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp Sun Jul 22 19:00:52 2018
@@ -80,7 +80,8 @@ TEST_CASE(last_write_time_not_representa
     TEST_CHECK(last_write_time(file, ec) == file_time_type::min());
     TEST_CHECK(ErrorIs(ec, expected_err));
 
-    ExceptionChecker CheckExcept(file, expected_err);
+    ExceptionChecker CheckExcept(file, expected_err,
+                                 "directory_entry::last_write_time");
     TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept,
                             ent.last_write_time());
 

Modified: libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp Sun Jul 22 19:00:52 2018
@@ -168,7 +168,8 @@ TEST_CASE(refresh_cannot_resolve) {
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
     TEST_CHECK(ent.path() == file);
 
-    ExceptionChecker Checker(file, std::errc::permission_denied);
+    ExceptionChecker Checker(file, std::errc::permission_denied,
+                             "directory_entry::refresh");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
   }
   permissions(dir, old_perms);
@@ -182,7 +183,8 @@ TEST_CASE(refresh_cannot_resolve) {
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
     TEST_CHECK(ent.path() == sym_in_dir);
 
-    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+                             "directory_entry::refresh");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
   }
   permissions(dir, old_perms);

Modified: libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp Sun Jul 22 19:00:52 2018
@@ -102,7 +102,7 @@ TEST_CASE(not_regular_file) {
     TEST_CHECK(ec == other_ec);
     TEST_CHECK(ErrorIs(ec, TC.expected_err));
 
-    ExceptionChecker Checker(p, TC.expected_err);
+    ExceptionChecker Checker(p, TC.expected_err, "directory_entry::file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
   }
 }
@@ -134,7 +134,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::DNE,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
   }
   // test a dead symlink
@@ -156,7 +157,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::BadSymlink,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
   }
   // test a file w/o appropriate permissions.
@@ -174,7 +176,7 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(file, std::errc::permission_denied);
+    ExceptionChecker Checker(file, std::errc::permission_denied, "file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
 
     permissions(dir, old_perms);
@@ -199,7 +201,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+                             "file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
 
     permissions(dir, old_perms);
@@ -224,7 +227,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+                             "file_size");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
 
     permissions(dir, old_perms);

Modified: libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp Sun Jul 22 19:00:52 2018
@@ -133,7 +133,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::DNE,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::hard_link_count");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
   }
   // test a dead symlink
@@ -155,7 +156,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::BadSymlink,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::hard_link_count");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
   }
   // test a file w/o appropriate permissions.
@@ -173,7 +175,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(file, std::errc::permission_denied);
+    ExceptionChecker Checker(file, std::errc::permission_denied,
+                             "hard_link_count");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
 
     permissions(dir, old_perms);
@@ -198,7 +201,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+                             "hard_link_count");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
 
     permissions(dir, old_perms);
@@ -223,7 +227,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+                             "hard_link_count");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
 
     permissions(dir, old_perms);

Modified: libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp Sun Jul 22 19:00:52 2018
@@ -106,7 +106,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::DNE,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::last_write_time");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
   }
   // test a dead symlink
@@ -128,7 +129,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
 
     ExceptionChecker Checker(StaticEnv::BadSymlink,
-                             std::errc::no_such_file_or_directory);
+                             std::errc::no_such_file_or_directory,
+                             "directory_entry::last_write_time");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
   }
   // test a file w/o appropriate permissions.
@@ -146,7 +148,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(file, std::errc::permission_denied);
+    ExceptionChecker Checker(file, std::errc::permission_denied,
+                             "last_write_time");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
 
     permissions(dir, old_perms);
@@ -171,7 +174,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+                             "last_write_time");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
 
     permissions(dir, old_perms);
@@ -196,7 +200,8 @@ TEST_CASE(error_reporting) {
     TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
     TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
 
-    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+    ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+                             "last_write_time");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
 
     permissions(dir, old_perms);

Modified: libcxx/trunk/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp Sun Jul 22 19:00:52 2018
@@ -25,6 +25,8 @@
 #include "rapid-cxx-test.hpp"
 #include "filesystem_test_helper.hpp"
 
+#include <iostream>
+
 using namespace fs;
 
 TEST_SUITE(recursive_directory_iterator_increment_tests)
@@ -290,18 +292,15 @@ TEST_CASE(test_PR35078)
     }
     {
       bool SeenNestedFile = false;
-      recursive_directory_iterator it = SetupState(true, SeenNestedFile);
+      recursive_directory_iterator it = SetupState(false, SeenNestedFile);
       TEST_REQUIRE(it != endIt);
       TEST_REQUIRE(*it == nestedDir);
-      ec = GetTestEC();
-      it.increment(ec);
-      TEST_CHECK(!ec);
-      if (SeenNestedFile) {
-        TEST_CHECK(it == endIt);
-      } else {
-        TEST_REQUIRE(it != endIt);
-        TEST_CHECK(*it == nestedFile);
-      }
+
+      ExceptionChecker Checker(std::errc::permission_denied,
+                               "recursive_directory_iterator::operator++()",
+                               format_string("attempting recursion into \"%s\"",
+                                             nestedDir.native()));
+      TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it);
     }
 }
 

Modified: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp Sun Jul 22 19:00:52 2018
@@ -64,7 +64,7 @@ TEST_CASE(test_error_reporting) {
     TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing,
                              ec) == false);
     TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
-    ExceptionChecker Checker(file, file, std::errc::file_exists);
+    ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
 
   }
@@ -72,7 +72,7 @@ TEST_CASE(test_error_reporting) {
     std::error_code ec;
     TEST_CHECK(fs::copy_file(file, file2, ec) == false);
     TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
-    ExceptionChecker Checker(file, file, std::errc::file_exists);
+    ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
     TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
 
   }

Modified: libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp (original)
+++ libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp Sun Jul 22 19:00:52 2018
@@ -69,14 +69,15 @@ TEST_CASE(file_size_error_cases)
       {StaticEnv::Dir, std::errc::is_a_directory},
       {StaticEnv::SymlinkToDir, std::errc::is_a_directory},
       {StaticEnv::BadSymlink, std::errc::no_such_file_or_directory},
-      {StaticEnv::DNE, std::errc::no_such_file_or_directory}};
+      {StaticEnv::DNE, std::errc::no_such_file_or_directory},
+      {"", std::errc::no_such_file_or_directory}};
     const uintmax_t expect = static_cast<uintmax_t>(-1);
     for (auto& TC : TestCases) {
       std::error_code ec = GetTestEC();
       TEST_CHECK(file_size(TC.p, ec) == expect);
       TEST_CHECK(ErrorIs(ec, TC.expected_err));
 
-      ExceptionChecker Checker(TC.p, TC.expected_err);
+      ExceptionChecker Checker(TC.p, TC.expected_err, "file_size");
       TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p));
     }
 }

Modified: libcxx/trunk/test/support/filesystem_test_helper.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/filesystem_test_helper.hpp?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/test/support/filesystem_test_helper.hpp (original)
+++ libcxx/trunk/test/support/filesystem_test_helper.hpp Sun Jul 22 19:00:52 2018
@@ -9,8 +9,11 @@
 #include <random>
 #include <chrono>
 #include <vector>
+#include <regex>
 
+#include "test_macros.h"
 #include "rapid-cxx-test.hpp"
+#include "format_string.hpp"
 
 // static test helpers
 
@@ -442,25 +445,77 @@ inline bool PathEq(fs::path const& LHS,
 }
 
 struct ExceptionChecker {
-  std::vector<std::errc> expected_err_list;
+  std::errc expected_err;
   fs::path expected_path1;
   fs::path expected_path2;
+  unsigned num_paths;
+  const char* func_name;
+  std::string opt_message;
+
+  explicit ExceptionChecker(std::errc first_err, const char* func_name,
+                            std::string opt_msg = {})
+      : expected_err{first_err}, num_paths(0), func_name(func_name),
+        opt_message(opt_msg) {}
+  explicit ExceptionChecker(fs::path p, std::errc first_err,
+                            const char* func_name, std::string opt_msg = {})
+      : expected_err(first_err), expected_path1(p), num_paths(1),
+        func_name(func_name), opt_message(opt_msg) {}
 
-  template <class... ErrcT>
-  explicit ExceptionChecker(fs::path p, std::errc first_err, ErrcT... rest_err)
-      : expected_err_list({first_err, rest_err...}), expected_path1(p) {}
-
-  template <class... ErrcT>
   explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
-                            ErrcT... rest_err)
-      : expected_err_list({first_err, rest_err...}), expected_path1(p1),
-        expected_path2(p2) {}
+                            const char* func_name, std::string opt_msg = {})
+      : expected_err(first_err), expected_path1(p1), expected_path2(p2),
+        num_paths(2), func_name(func_name), opt_message(opt_msg) {}
 
-  void operator()(fs::filesystem_error const& Err) const {
-    TEST_CHECK(ErrorIsImp(Err.code(), expected_err_list));
+  void operator()(fs::filesystem_error const& Err) {
+    TEST_CHECK(ErrorIsImp(Err.code(), {expected_err}));
     TEST_CHECK(Err.path1() == expected_path1);
     TEST_CHECK(Err.path2() == expected_path2);
+    LIBCPP_ONLY(check_libcxx_string(Err));
   }
+
+  void check_libcxx_string(fs::filesystem_error const& Err) {
+    std::string message = std::make_error_code(expected_err).message();
+
+    std::string additional_msg = "";
+    if (!opt_message.empty()) {
+      additional_msg = opt_message + ": ";
+    }
+    auto transform_path = [](const fs::path& p) {
+      if (p.native().empty())
+        return "\"\"";
+      return p.c_str();
+    };
+    std::string format = [&]() -> std::string {
+      switch (num_paths) {
+      case 0:
+        return format_string("filesystem error: in %s: %s%s", func_name,
+                             additional_msg, message);
+      case 1:
+        return format_string("filesystem error: in %s: %s%s [%s]", func_name,
+                             additional_msg, message,
+                             transform_path(expected_path1));
+      case 2:
+        return format_string("filesystem error: in %s: %s%s [%s] [%s]",
+                             func_name, additional_msg, message,
+                             transform_path(expected_path1),
+                             transform_path(expected_path2));
+      default:
+        TEST_CHECK(false && "unexpected case");
+        return "";
+      }
+    }();
+    TEST_CHECK(format == Err.what());
+    if (format != Err.what()) {
+      fprintf(stderr,
+              "filesystem_error::what() does not match expected output:\n");
+      fprintf(stderr, "  expected: \"%s\"\n", format.c_str());
+      fprintf(stderr, "  actual:   \"%s\"\n\n", Err.what());
+    }
+  }
+
+  ExceptionChecker(ExceptionChecker const&) = delete;
+  ExceptionChecker& operator=(ExceptionChecker const&) = delete;
+
 };
 
 #endif /* FILESYSTEM_TEST_HELPER_HPP */

Added: libcxx/trunk/test/support/format_string.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/format_string.hpp?rev=337664&view=auto
==============================================================================
--- libcxx/trunk/test/support/format_string.hpp (added)
+++ libcxx/trunk/test/support/format_string.hpp Sun Jul 22 19:00:52 2018
@@ -0,0 +1,68 @@
+#ifndef TEST_SUPPORT_FORMAT_STRING_HPP
+#define TEST_SUPPORT_FORMAT_STRING_HPP
+
+#include <cstdio>
+#include <string>
+#include <memory>
+#include <array>
+
+namespace format_string_detail {
+inline std::string format_string_imp(const char* msg, ...) {
+  // we might need a second shot at this, so pre-emptivly make a copy
+  struct GuardVAList {
+    va_list& target;
+    bool active = true;
+    void clear() {
+      if (active)
+        va_end(target);
+      active = false;
+    }
+    ~GuardVAList() {
+      if (active)
+        va_end(target);
+    }
+  };
+  va_list args;
+  va_start(args, msg);
+  GuardVAList args_guard = {args};
+
+  va_list args_cp;
+  va_copy(args_cp, args);
+  GuardVAList args_copy_guard = {args_cp};
+
+  std::array<char, 256> local_buff;
+  std::size_t size = local_buff.size();
+  auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
+
+  args_copy_guard.clear();
+
+  // handle empty expansion
+  if (ret == 0)
+    return std::string{};
+  if (static_cast<std::size_t>(ret) < size)
+    return std::string(local_buff.data());
+
+  // we did not provide a long enough buffer on our first attempt.
+  // add 1 to size to account for null-byte in size cast to prevent overflow
+  size = static_cast<std::size_t>(ret) + 1;
+  auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
+  ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
+  return std::string(buff_ptr.get());
+}
+
+const char* unwrap(std::string& s) { return s.c_str(); }
+template <class Arg>
+Arg const& unwrap(Arg& a) {
+  static_assert(!std::is_class<Arg>::value, "cannot pass class here");
+  return a;
+}
+
+} // namespace format_string_detail
+
+template <class... Args>
+std::string format_string(const char* fmt, Args const&... args) {
+  return format_string_detail::format_string_imp(
+      fmt, format_string_detail::unwrap(const_cast<Args&>(args))...);
+}
+
+#endif // TEST_SUPPORT_FORMAT_STRING_HPP

Modified: libcxx/trunk/www/cxx2a_status.html
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/www/cxx2a_status.html?rev=337664&r1=337663&r2=337664&view=diff
==============================================================================
--- libcxx/trunk/www/cxx2a_status.html (original)
+++ libcxx/trunk/www/cxx2a_status.html Sun Jul 22 19:00:52 2018
@@ -151,7 +151,7 @@
 	<tr><td><a href="https://wg21.link/LWG2953">2953</a></td><td>LWG 2853 should apply to deque::erase too</td><td>Albuquerque</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/LWG2958">2958</a></td><td>Moves improperly defined as deleted</td><td>Albuquerque</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/LWG2964">2964</a></td><td>Apparently redundant requirement for dynamic_pointer_cast</td><td>Albuquerque</td><td></td></tr>
-	<tr><td><a href="https://wg21.link/LWG2965">2965</a></td><td>Non-existing path::native_string() in filesystem_error::what() specification</td><td>Albuquerque</td><td></td></tr>
+	<tr><td><a href="https://wg21.link/LWG2965">2965</a></td><td>Non-existing path::native_string() in filesystem_error::what() specification</td><td>Albuquerque</td><td>Nothing to do</td></tr>
 	<tr><td><a href="https://wg21.link/LWG2972">2972</a></td><td>What is is_trivially_destructible_v<int>?</td><td>Albuquerque</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG2976">2976</a></td><td>Dangling uses_allocator specialization for packaged_task</td><td>Albuquerque</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG2977">2977</a></td><td>unordered_meow::merge() has incorrect Throws: clause</td><td>Albuquerque</td><td></td></tr>
@@ -196,7 +196,7 @@
 	<tr><td><a href="https://wg21.link/LWG3039">3039</a></td><td>Unnecessary <tt>decay</tt> in <tt>thread</tt> and <tt>packaged_task</tt></td><td>Jacksonville</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG3041">3041</a></td><td>Unnecessary <tt>decay</tt> in <tt>reference_wrapper</tt></td><td>Jacksonville</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG3042">3042</a></td><td><tt>is_literal_type_v</tt> should be inline</td><td>Jacksonville</td><td>Complete</td></tr>
-	<tr><td><a href="https://wg21.link/LWG3043">3043</a></td><td>Bogus postcondition for <tt>filesystem_error</tt> constructor</td><td>Jacksonville</td><td></td></tr>
+	<tr><td><a href="https://wg21.link/LWG3043">3043</a></td><td>Bogus postcondition for <tt>filesystem_error</tt> constructor</td><td>Jacksonville</td><td>Complete</td></tr>
 	<tr><td><a href="https://wg21.link/LWG3045">3045</a></td><td><tt>atomic<<i>floating-point</i>></tt> doesn't have <tt>value_type</tt> or <tt>difference_type</tt></td><td>Jacksonville</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/LWG3048">3048</a></td><td><tt>transform_reduce(exec, first1, last1, first2, init)</tt> discards execution policy</td><td>Jacksonville</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/LWG3051">3051</a></td><td>Floating point classifications were inadvertently changed in P0175</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>




More information about the cfe-commits mailing list