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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits