diff --git a/include/mrdocs/Dom/Function.hpp b/include/mrdocs/Dom/Function.hpp index 4595fe902c..a31695deb9 100644 --- a/include/mrdocs/Dom/Function.hpp +++ b/include/mrdocs/Dom/Function.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include namespace clang { diff --git a/include/mrdocs/Dom/Object.hpp b/include/mrdocs/Dom/Object.hpp index 2d8bc818fc..4b30bac0c6 100644 --- a/include/mrdocs/Dom/Object.hpp +++ b/include/mrdocs/Dom/Object.hpp @@ -13,7 +13,7 @@ #define MRDOCS_API_DOM_OBJECT_HPP #include -#include +#include #include #include #include diff --git a/include/mrdocs/Metadata/Info/Overloads.hpp b/include/mrdocs/Metadata/Info/Overloads.hpp index fceff866e4..7237b9bd19 100644 --- a/include/mrdocs/Metadata/Info/Overloads.hpp +++ b/include/mrdocs/Metadata/Info/Overloads.hpp @@ -30,6 +30,9 @@ struct OverloadsInfo final /// The members of the overload set. std::vector Members; + /// Info about the return type of this function. + Polymorphic ReturnType; + //-------------------------------------------- explicit OverloadsInfo(SymbolID const& ID) noexcept diff --git a/include/mrdocs/Support/Algorithm.hpp b/include/mrdocs/Support/Algorithm.hpp index 7b82f71277..4293af0667 100644 --- a/include/mrdocs/Support/Algorithm.hpp +++ b/include/mrdocs/Support/Algorithm.hpp @@ -90,6 +90,31 @@ contains_n(Range const& range, El const& el, std::size_t n) return false; } +/** Find the last element in a range that matches an element in the specified range. + @param range The range to search. + @param els The elements to search for. + @return An iterator to the last element found, or range.end() if not found. + */ +template +requires std::equality_comparable_with, std::ranges::range_value_t> +auto +find_last_of(Range&& range, Els&& els) +{ + if (std::ranges::empty(range)) + { + return std::ranges::end(range); + } + auto it = std::ranges::end(range); + do { + --it; + if (contains(els, *it)) + { + return it; + } + } while (it != std::ranges::begin(range)); + return std::ranges::end(range); +} + } // clang::mrdocs #endif \ No newline at end of file diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index 1d547180bf..aac800bb57 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -25,30 +24,7 @@ #include #include -namespace clang { -namespace mrdocs { - -//------------------------------------------------ - -/** Parameter type that adds a source location to a value. -*/ -template -struct Located -{ - T value; - source_location where; - - template - requires std::is_constructible_v - Located( - Arg&& arg, - source_location const& loc = - source_location::current()) - : value(std::forward(arg)) - , where(loc) - { - } -}; +namespace clang::mrdocs { //------------------------------------------------ // @@ -132,7 +108,7 @@ class MRDOCS_DECL This constructs a new error from a list of zero or more errors. If the list is empty, - or if all of the errors in the list indicate + or if all the errors in the list indicate success, then newly constructed object will indicate success. */ @@ -268,2682 +244,121 @@ class MRDOCS_DECL } }; -//------------------------------------------------ -// -// Expected -// -//------------------------------------------------ - -template -class Expected; - -template -class Unexpected; +} // clang::mrdocs -template -class BadExpectedAccess; - -template <> -class BadExpectedAccess : public std::exception +template<> +struct std::hash<::clang::mrdocs::Error> { -protected: - BadExpectedAccess() noexcept = default; - - BadExpectedAccess(BadExpectedAccess const&) = default; - - BadExpectedAccess(BadExpectedAccess&&) = default; - - BadExpectedAccess& - operator=(BadExpectedAccess const&) = default; - - BadExpectedAccess& - operator=(BadExpectedAccess&&) = default; - - ~BadExpectedAccess() override = default; -public: - [[nodiscard]] - char const* - what() const noexcept override + std::size_t operator()( + ::clang::mrdocs::Error const& err) const noexcept { - return "bad access to Expected without Expected value"; + return std::hash()(err.message()); } }; -template -class BadExpectedAccess : public BadExpectedAccess { - E unex_; -public: - explicit - BadExpectedAccess(E e) - : unex_(std::move(e)) { } - - [[nodiscard]] - E& - error() & noexcept - { - return unex_; - } - - [[nodiscard]] - E const& - error() const & noexcept - { - return unex_; - } - - [[nodiscard]] - E&& - error() && noexcept - { - return std::move(unex_); - } - - [[nodiscard]] - E const&& - error() const && noexcept +template<> +struct fmt::formatter + : fmt::formatter +{ + auto format( + clang::mrdocs::Error const& err, + fmt::format_context& ctx) const { - return std::move(unex_); + return fmt::formatter::format(err.message(), ctx); } }; -struct unexpect_t +template<> +struct fmt::formatter + : fmt::formatter { - explicit unexpect_t() = default; + auto format( + std::error_code const& ec, + fmt::format_context& ctx) const + { + return fmt::formatter::format(ec.message(), ctx); + } }; -inline constexpr unexpect_t unexpect{}; - -namespace detail -{ - template - constexpr bool isExpected = false; - template - constexpr bool isExpected> = true; - - template - constexpr bool isUnexpected = false; - template - constexpr bool isUnexpected> = true; - - template - using then_result = std::remove_cvref_t>; - template - using result_transform = std::remove_cv_t>; - template - using result0 = std::remove_cvref_t>; - template - using result0_xform = std::remove_cv_t>; - - template - concept can_beUnexpected = - std::is_object_v && - (!std::is_array_v) && - (!detail::isUnexpected) && - (!std::is_const_v) && - (!std::is_volatile_v); - - // Tag types for in-place construction from an invocation result. - struct in_place_inv { }; - struct unexpect_inv { }; -} +namespace clang::mrdocs { -template -class Unexpected +/** A source location with filename prettification. +*/ +class MRDOCS_DECL + SourceLocation { - static_assert(detail::can_beUnexpected); - E unex_; + std::string_view file_; + std::uint_least32_t line_; + std::uint_least32_t col_; + std::string_view func_; public: - constexpr - Unexpected(Unexpected const&) = default; - - constexpr - Unexpected(Unexpected&&) = default; - - template - requires - (!std::is_same_v, Unexpected>) && - (!std::is_same_v, std::in_place_t>) && - std::is_constructible_v - constexpr explicit - Unexpected(Er&& e) noexcept(std::is_nothrow_constructible_v) - : unex_(std::forward(e)) - {} - - template - requires std::is_constructible_v - constexpr explicit - Unexpected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : unex_(std::forward(args)...) - {} - - template - requires std::is_constructible_v&, Args...> - constexpr explicit - Unexpected( - std::in_place_t, - std::initializer_list il, - Args&&... args) - noexcept(std::is_nothrow_constructible_v< - E, std::initializer_list&, Args...>) - : unex_(il, std::forward(args)...) - {} - - constexpr Unexpected& operator=(Unexpected const&) = default; - constexpr Unexpected& operator=(Unexpected&&) = default; - - [[nodiscard]] - constexpr E const& - error() const & noexcept - { - return unex_; - } - - [[nodiscard]] - constexpr E& - error() & noexcept - { - return unex_; - } - - [[nodiscard]] - constexpr E const&& - error() const && noexcept - { - return std::move(unex_); - } + SourceLocation( + source_location const& loc) noexcept; - [[nodiscard]] - constexpr E&& - error() && noexcept + std::string_view file_name() const noexcept { - return std::move(unex_); + return file_; } - constexpr - void - swap(Unexpected& other) noexcept(std::is_nothrow_swappable_v) - requires std::is_swappable_v + std::uint_least32_t line() const noexcept { - using std::swap; - swap(unex_, other.unex_); + return line_; } - template - [[nodiscard]] - friend - constexpr - bool - operator==(Unexpected const& x, const Unexpected& y) + std::uint_least32_t column() const noexcept { - return x.unex_ == y.error(); + return col_; } - friend - constexpr - void - swap(Unexpected& x, Unexpected& y) - noexcept(noexcept(x.swap(y))) - requires std::is_swappable_v + std::string_view function_name() const noexcept { - x.swap(y); + return func_; } }; -template Unexpected(E) -> Unexpected; - -namespace detail +/** A format string with source location. +*/ +template +struct FormatString { template - constexpr - bool - failed(T const&t) - { - if constexpr (isExpected>) - { - return !t; - } - else if constexpr (std::same_as, Error>) - { - return t.failed(); - } - else if constexpr (requires (T const& t0) { t0.empty(); }) - { - return t.empty(); - } - else if constexpr (std::constructible_from) - { - return !t; - } - else - { - return false; - } - } - - template - constexpr - decltype(auto) - error(T const& t) + FormatString( + T const& fs_, + source_location loc_ = + source_location::current()) + : fs(fs_) + , loc(loc_) { - if constexpr (isExpected>) - { - return t.error(); - } - else if constexpr (std::same_as, Error>) - { - return t; - } - else if constexpr (requires (T const& t0) { t0.empty(); }) - { - return Error("Empty value"); - } - else if constexpr (std::constructible_from) - { - return Error("Invalid value"); - } + static_assert(std::is_constructible_v< + std::string_view, T const&>); } -} - -#ifndef MRDOCS_TRY -# define MRDOCS_MERGE_(a, b) a##b -# define MRDOCS_LABEL_(a) MRDOCS_MERGE_(expected_result_, a) -# define MRDOCS_UNIQUE_NAME MRDOCS_LABEL_(__LINE__) - -/// Try to retrive expected-like type -# define MRDOCS_TRY_VOID(expr) \ - auto MRDOCS_UNIQUE_NAME = expr; \ - if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ - return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \ - } \ - void(0) -# define MRDOCS_TRY_VAR(var, expr) \ - auto MRDOCS_UNIQUE_NAME = expr; \ - if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ - return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \ - } \ - var = *std::move(MRDOCS_UNIQUE_NAME) -# define MRDOCS_TRY_MSG(var, expr, msg) \ - auto MRDOCS_UNIQUE_NAME = expr; \ - if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ - return Unexpected(Error(msg)); \ - } \ - var = *std::move(MRDOCS_UNIQUE_NAME) -# define MRDOCS_TRY_GET_MACRO(_1, _2, _3, NAME, ...) NAME -# define MRDOCS_TRY(...) \ - MRDOCS_TRY_GET_MACRO(__VA_ARGS__, MRDOCS_TRY_MSG, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__) - -/// Check existing expected-like type -# define MRDOCS_CHECK_VOID(var) \ - if (detail::failed(var)) { \ - return Unexpected(detail::error(var)); \ - } \ - void(0) -# define MRDOCS_CHECK_MSG(var, msg) \ - if (detail::failed(var)) { \ - return Unexpected(Error(msg)); \ - } \ - void(0) -# define MRDOCS_CHECK_GET_MACRO(_1, _2, NAME, ...) NAME -# define MRDOCS_CHECK(...) \ - MRDOCS_CHECK_GET_MACRO(__VA_ARGS__, MRDOCS_CHECK_MSG, MRDOCS_CHECK_VOID)(__VA_ARGS__) -/// Check existing expected-like type and return custom value otherwise -# define MRDOCS_CHECK_OR_VOID(var) \ - if (detail::failed(var)) { \ - return; \ - } \ - void(0) -# define MRDOCS_CHECK_OR_VALUE(var, value) \ - if (detail::failed(var)) { \ - return value; \ - } \ - void(0) -# define MRDOCS_CHECK_GET_OR_MACRO(_1, _2, NAME, ...) NAME -# define MRDOCS_CHECK_OR(...) \ - MRDOCS_CHECK_GET_OR_MACRO(__VA_ARGS__, MRDOCS_CHECK_OR_VALUE, MRDOCS_CHECK_OR_VOID)(__VA_ARGS__) - -# define MRDOCS_CHECK_OR_CONTINUE(var) \ - if (detail::failed(var)) { \ - continue; \ - } \ - void(0) + std::string_view fs; + source_location loc; +}; -#endif +/** Return a formatted error. + @param fs The format string. This + must not be empty. -/// @cond undocumented -namespace detail + @param args Zero or more values to + substitute into the format string. +*/ +template +Error +formatError( + FormatString...> fs, + Args&&... args) { - template - class ExpectedGuard - { - static_assert( std::is_nothrow_move_constructible_v ); - T* guarded_; - T tmp_; - - public: - constexpr explicit - ExpectedGuard(T& x) - : guarded_(std::addressof(x)) - , tmp_(std::move(x)) - { - std::destroy_at(guarded_); - } - - constexpr - ~ExpectedGuard() - { - if (guarded_) [[unlikely]] - { - std::construct_at(guarded_, std::move(tmp_)); - } - } - - ExpectedGuard(ExpectedGuard const&) = delete; - - ExpectedGuard& operator=(ExpectedGuard const&) = delete; - - constexpr T&& - release() noexcept - { - guarded_ = nullptr; - return std::move(tmp_); - } - }; - - template - constexpr - void - ExpectedReinit(T* newval, U* oldval, Vp&& arg) - noexcept(std::is_nothrow_constructible_v) - { - if constexpr (std::is_nothrow_constructible_v) - { - std::destroy_at(oldval); - std::construct_at(newval, std::forward(arg)); - } - else if constexpr (std::is_nothrow_move_constructible_v) - { - T tmp(std::forward(arg)); // might throw - std::destroy_at(oldval); - std::construct_at(newval, std::move(tmp)); - } - else - { - ExpectedGuard guard(*oldval); - std::construct_at(newval, std::forward(arg)); // might throw - guard.release(); - } - } + std::string s; + fmt::vformat_to( + std::back_inserter(s), + fs.fs, fmt::make_format_args(args...)); + return Error(std::move(s), fs.loc); } -/// @endcond - -/** A container holding an error or a value. - */ -template -class Expected -{ - static_assert(!std::is_reference_v); - static_assert(!std::is_function_v); - static_assert(!std::is_same_v, std::in_place_t>); - static_assert(!std::is_same_v, unexpect_t>); - static_assert(!detail::isUnexpected>); - static_assert(detail::can_beUnexpected); - - template > - static constexpr bool constructible_from_expected = - std::constructible_from&> || - std::constructible_from> || - std::constructible_from&> || - std::constructible_from> || - std::convertible_to&, T> || - std::convertible_to, T> || - std::convertible_to&, T> || - std::convertible_to, T> || - std::constructible_from&> || - std::constructible_from> || - std::constructible_from&> || - std::constructible_from>; - - template - constexpr static bool explicit_conv - = (!std::convertible_to) || - (!std::convertible_to); - - template - static constexpr bool same_val - = std::is_same_v; - - template - static constexpr bool same_err - = std::is_same_v; - - template friend class Expected; - - union { - T val_; - E unex_; - }; - - bool has_value_; - -public: - using value_type = T; - using error_type = E; - using unexpected_type = Unexpected; - - template - using rebind = Expected; - - constexpr - Expected() - noexcept(std::is_nothrow_default_constructible_v) - requires std::is_default_constructible_v - : val_() - , has_value_(true) - {} - - Expected(Expected const&) = default; - - constexpr - Expected(Expected const& x) - noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_constructible_v) - requires - std::is_copy_constructible_v && - std::is_copy_constructible_v && - (!std::is_trivially_copy_constructible_v || - !std::is_trivially_copy_constructible_v) - : has_value_(x.has_value_) - { - if (has_value_) - { - std::construct_at(std::addressof(val_), x.val_); - } - else - { - std::construct_at(std::addressof(unex_), x.unex_); - } - } - - Expected(Expected&&) = default; - - constexpr - Expected(Expected&& x) - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v) - requires - std::is_move_constructible_v && - std::is_move_constructible_v && - (!std::is_trivially_move_constructible_v || - !std::is_trivially_move_constructible_v) - : has_value_(x.has_value_) - { - if (has_value_) - { - std::construct_at(std::addressof(val_), std::move(x).val_); - } - else - { - std::construct_at(std::addressof(unex_), std::move(x).unex_); - } - } - - template - requires - std::is_constructible_v && - std::is_constructible_v && - (!constructible_from_expected) - constexpr - explicit(explicit_conv) - Expected(const Expected& x) - noexcept( - std::is_nothrow_constructible_v && - std::is_nothrow_constructible_v) - : has_value_(x.has_value_) - { - if (has_value_) - { - std::construct_at(std::addressof(val_), x.val_); - } - else - { - std::construct_at(std::addressof(unex_), x.unex_); - } - } - - template - requires - std::is_constructible_v && - std::is_constructible_v && - (!constructible_from_expected) - constexpr - explicit(explicit_conv) - Expected(Expected&& x) - noexcept( - std::is_nothrow_constructible_v && - std::is_nothrow_constructible_v) - : has_value_(x.has_value_) - { - if (has_value_) - { - std::construct_at(std::addressof(val_), std::move(x).val_); - } - else - { - std::construct_at(std::addressof(unex_), std::move(x).unex_); - } - } - - template - requires - (!std::is_same_v, Expected>) && - (!std::is_same_v, std::in_place_t>) && - (!detail::isUnexpected>) && - std::is_constructible_v - constexpr - explicit(!std::is_convertible_v) - Expected(U&& v) - noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(v)) - , has_value_(true) - { } - - template - requires std::is_constructible_v - constexpr - explicit(!std::is_convertible_v) - Expected(const Unexpected& u) - noexcept(std::is_nothrow_constructible_v) - : unex_(u.error()) - , has_value_(false) - { } - - template - requires std::is_constructible_v - constexpr - explicit(!std::is_convertible_v) - Expected(Unexpected&& u) - noexcept(std::is_nothrow_constructible_v) - : unex_(std::move(u).error()) - , has_value_(false) - { } - - template - requires std::is_constructible_v - constexpr explicit - Expected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(args)...) - , has_value_(true) - { } - - template - requires - std::is_constructible_v&, Args...> - constexpr explicit - Expected( - std::in_place_t, - std::initializer_list il, - Args&&... args) - noexcept( - std::is_nothrow_constructible_v< - T, std::initializer_list&, Args...>) - : val_(il, std::forward(args)...) - , has_value_(true) - { } - - template - requires std::is_constructible_v - constexpr explicit - Expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : unex_(std::forward(args)...) - , has_value_(false) - { } - - template - requires std::is_constructible_v&, Args...> - constexpr explicit - Expected( - unexpect_t, - std::initializer_list il, - Args&&... args) - noexcept( - std::is_nothrow_constructible_v< - E, std::initializer_list&, Args...>) - : unex_(il, std::forward(args)...) - , has_value_(false) - { } - - constexpr ~Expected() = default; - - constexpr ~Expected() - requires - (!std::is_trivially_destructible_v) - || (!std::is_trivially_destructible_v) - { - if (has_value_) - { - std::destroy_at(std::addressof(val_)); - } - else - { - std::destroy_at(std::addressof(unex_)); - } - } - - Expected& - operator=(Expected const&) = delete; - - constexpr - Expected& - operator=(Expected const& x) - noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_assignable_v && - std::is_nothrow_copy_assignable_v) - requires - std::is_copy_assignable_v && - std::is_copy_constructible_v && - std::is_copy_assignable_v && - std::is_copy_constructible_v && - (std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - { - if (x.has_value_) - { - this->assign_val_impl(x.val_); - } - else - { - this->assign_unex_impl(x.unex_); - } - return *this; - } - constexpr - Expected& - operator=(Expected&& x) - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_assignable_v && - std::is_nothrow_move_assignable_v) - requires - std::is_move_assignable_v && - std::is_move_constructible_v && - std::is_move_assignable_v && - std::is_move_constructible_v && - (std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - { - if (x.has_value_) - { - assign_val_impl(std::move(x.val_)); - } - else - { - assign_unex_impl(std::move(x.unex_)); - } - return *this; - } - - template - requires - (!std::is_same_v>) && - (!detail::isUnexpected>) && - std::is_constructible_v && - std::is_assignable_v && - (std::is_nothrow_constructible_v || - std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - constexpr - Expected& - operator=(U&& v) - { - assign_val_impl(std::forward(v)); - return *this; - } - - template - requires - std::is_constructible_v && - std::is_assignable_v && - (std::is_nothrow_constructible_v || - std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - constexpr - Expected& - operator=(const Unexpected& e) - { - assign_unex_impl(e.error()); - return *this; - } - - template - requires - std::is_constructible_v && - std::is_assignable_v && - (std::is_nothrow_constructible_v || - std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - constexpr - Expected& - operator=(Unexpected&& e) - { - assign_unex_impl(std::move(e).error()); - return *this; - } - - template - requires std::is_nothrow_constructible_v - constexpr - T& - emplace(Args&&... args) noexcept - { - if (has_value_) - { - std::destroy_at(std::addressof(val_)); - } - else - { - std::destroy_at(std::addressof(unex_)); - has_value_ = true; - } - std::construct_at( - std::addressof(val_), - std::forward(args)...); - return val_; - } - - template - requires - std::is_nothrow_constructible_v< - T, std::initializer_list&, Args...> - constexpr - T& - emplace(std::initializer_list il, Args&&... args) noexcept - { - if (has_value_) - { - std::destroy_at(std::addressof(val_)); - } - else - { - std::destroy_at(std::addressof(unex_)); - has_value_ = true; - } - std::construct_at( - std::addressof(val_), il, - std::forward(args)...); - return val_; - } - - constexpr - void - swap(Expected& x) - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v && - std::is_nothrow_swappable_v) - requires - std::is_swappable_v && - std::is_swappable_v && - std::is_move_constructible_v && - std::is_move_constructible_v && - (std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - { - if (has_value_) - { - if (x.has_value_) - { - using std::swap; - swap(val_, x.val_); - } - else - { - this->swap_val_unex_impl(x); - } - } - else - { - if (x.has_value_) - { - x.swap_val_unex_impl(*this); - } - else - { - using std::swap; - swap(unex_, x.unex_); - } - } - } - - // observers - - [[nodiscard]] - constexpr - T const* - operator->() const noexcept - { - MRDOCS_ASSERT(has_value_); - return std::addressof(val_); - } - - [[nodiscard]] - constexpr - T* - operator->() noexcept - { - MRDOCS_ASSERT(has_value_); - return std::addressof(val_); - } - - [[nodiscard]] - constexpr - T const& - operator*() const & noexcept - { - MRDOCS_ASSERT(has_value_); - return val_; - } - - [[nodiscard]] - constexpr - T& - operator*() & noexcept - { - MRDOCS_ASSERT(has_value_); - return val_; - } - - [[nodiscard]] - constexpr - T const&& - operator*() const && noexcept - { - MRDOCS_ASSERT(has_value_); - return std::move(val_); - } - - [[nodiscard]] - constexpr - T&& - operator*() && noexcept - { - MRDOCS_ASSERT(has_value_); - return std::move(val_); - } - - [[nodiscard]] - constexpr explicit - operator bool() const noexcept - { - return has_value_; - } - - [[nodiscard]] - constexpr bool has_value() const noexcept - { - return has_value_; - } - - constexpr T const& - value() const & - { - if (has_value_) [[likely]] - { - return val_; - } - throw BadExpectedAccess(unex_); - } - - constexpr T& - value() & - { - if (has_value_) [[likely]] - { - return val_; - } - auto const& unex = unex_; - throw BadExpectedAccess(unex); - } - - constexpr T const&& - value() const && - { - if (has_value_) [[likely]] - { - return std::move(val_); - } - throw BadExpectedAccess(std::move(unex_)); - } - - constexpr T&& - value() && - { - if (has_value_) [[likely]] - { - return std::move(val_); - } - throw BadExpectedAccess(std::move(unex_)); - } - - constexpr E const& - error() const & noexcept - { - MRDOCS_ASSERT(!has_value_); - return unex_; - } - - constexpr E& - error() & noexcept - { - MRDOCS_ASSERT(!has_value_); - return unex_; - } - - constexpr E const&& - error() const && noexcept - { - MRDOCS_ASSERT(!has_value_); - return std::move(unex_); - } - - constexpr E&& - error() && noexcept - { - MRDOCS_ASSERT(!has_value_); - return std::move(unex_); - } - - template - constexpr - T - value_or(U&& v) const & - noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_convertible_v) - { - static_assert( std::is_copy_constructible_v ); - static_assert( std::is_convertible_v ); - if (has_value_) - { - return val_; - } - return static_cast(std::forward(v)); - } - - template - constexpr - T - value_or(U&& v) && - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_convertible_v) - { - static_assert( std::is_move_constructible_v ); - static_assert( std::is_convertible_v ); - if (has_value_) - { - return std::move(val_); - } - return static_cast(std::forward(v)); - } - - template - constexpr - E - error_or(G&& e) const& - { - static_assert(std::is_copy_constructible_v); - static_assert(std::is_convertible_v); - if (has_value_) - { - return std::forward(e); - } - return unex_; - } - - template - constexpr E - error_or(G&& e) && - { - static_assert(std::is_move_constructible_v); - static_assert(std::is_convertible_v); - if (has_value_) - { - return std::forward(e); - } - return std::move(unex_); - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) & - { - using U = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f), val_); - } - else - { - return U(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) const & - { - using U = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f), val_); - } - else - { - return U(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) && - { - using U = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f), std::move(val_)); - } - else - { - return U(unexpect, std::move(unex_)); - } - } - - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) const && - { - using U = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f), std::move(val_)); - } - else - { - return U(unexpect, std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - or_else(Fn&& f) & - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(std::in_place, val_); - } - else - { - return std::invoke(std::forward(f), unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - or_else(Fn&& f) const & - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(std::in_place, val_); - } - else - { - return std::invoke(std::forward(f), unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - or_else(Fn&& f) && - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(std::in_place, std::move(val_)); - } - else - { - return std::invoke(std::forward(f), std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - or_else(Fn&& f) const && - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(std::in_place, std::move(val_)); - } - else - { - return std::invoke(std::forward(f), std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) & - { - using U = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res( - in_place_inv{}, [&]() { - return std::invoke(std::forward(f), val_); - }); - } - else - { - return Res(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) const & - { - using U = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res( - in_place_inv{}, [&]() { - return std::invoke(std::forward(f), val_); - }); - } - else - { - return Res(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) && - { - using U = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res( - in_place_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(val_)); - }); - } - else - { - return Res(unexpect, std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) const && - { - using U = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res( - in_place_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(val_)); - }); - } - else - { - return Res(unexpect, std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform_error(Fn&& f) & - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(std::in_place, val_); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), unex_); - }); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform_error(Fn&& f) const & - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(std::in_place, val_); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), unex_); - }); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform_error(Fn&& f) && - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(std::in_place, std::move(val_)); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(unex_)); - }); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform_error(Fn&& f) const && - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(std::in_place, std::move(val_)); - } - else - { - return Res(unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(unex_)); - }); - } - } - - template - requires (!std::is_void_v) - friend - constexpr - bool - operator==(Expected const& x, const Expected& y) - noexcept( - noexcept(bool(*x == *y)) && - noexcept(bool(x.error() == y.error()))) - { - if (x.has_value()) - { - return y.has_value() && bool(*x == *y); - } - else - { - return !y.has_value() && bool(x.error() == y.error()); - } - } - - template - friend - constexpr - bool - operator==(Expected const& x, U const& v) - noexcept(noexcept(bool(*x == v))) - { - return x.has_value() && bool(*x == v); - } - - template - friend - constexpr - bool - operator==(Expected const& x, const Unexpected& e) - noexcept(noexcept(bool(x.error() == e.error()))) - { - return !x.has_value() && bool(x.error() == e.error()); - } - - friend - constexpr - void - swap(Expected& x, Expected& y) - noexcept(noexcept(x.swap(y))) - requires requires {x.swap(y);} - { - x.swap(y); - } - -private: - template - constexpr - void - assign_val_impl(Vp&& v) - { - if (has_value_) - { - val_ = std::forward(v); - } - else - { - detail::ExpectedReinit( - std::addressof(val_), - std::addressof(unex_), - std::forward(v)); - has_value_ = true; - } - } - - template - constexpr - void - assign_unex_impl(Vp&& v) - { - if (has_value_) - { - detail::ExpectedReinit( - std::addressof(unex_), - std::addressof(val_), - std::forward(v)); - has_value_ = false; - } - else - { - unex_ = std::forward(v); - } - } - - constexpr - void - swap_val_unex_impl(Expected& rhs) - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v) - { - if constexpr (std::is_nothrow_move_constructible_v) - { - detail::ExpectedGuard guard(rhs.unex_); - std::construct_at( - std::addressof(rhs.val_), - std::move(val_)); - rhs.has_value_ = true; - std::destroy_at(std::addressof(val_)); - std::construct_at(std::addressof(unex_), guard.release()); - has_value_ = false; - } - else - { - detail::ExpectedGuard guard(val_); - std::construct_at( - std::addressof(unex_), - std::move(rhs.unex_)); - has_value_ = false; - std::destroy_at(std::addressof(rhs.unex_)); - std::construct_at(std::addressof(rhs.val_), guard.release()); - rhs.has_value_ = true; - } - } - - using in_place_inv = detail::in_place_inv; - using unexpect_inv = detail::unexpect_inv; - - template - explicit constexpr - Expected(in_place_inv, Fn&& fn) - : val_(std::forward(fn)()), has_value_(true) - { } - - template - explicit constexpr - Expected(unexpect_inv, Fn&& fn) - : unex_(std::forward(fn)()), has_value_(false) - { } -}; - -template -requires std::is_void_v -class Expected -{ - static_assert( detail::can_beUnexpected ); - - template > - static constexpr bool constructible_from_expected = - std::is_constructible_v&> || - std::is_constructible_v> || - std::is_constructible_v&> || - std::is_constructible_v>; - - template - static constexpr bool same_val - = std::is_same_v; - - template - static constexpr bool same_err - = std::is_same_v; - - template friend class Expected; - - union { - struct { } void_; - E unex_; - }; - - bool has_value_; - -public: - using value_type = T; - using error_type = E; - using unexpected_type = Unexpected; - - template - using rebind = Expected; - - constexpr - Expected() noexcept - : void_() - , has_value_(true) - { } - - Expected(Expected const&) = default; - - constexpr - Expected(Expected const& x) - noexcept(std::is_nothrow_copy_constructible_v) - requires - std::is_copy_constructible_v && - (!std::is_trivially_copy_constructible_v) - : void_() - , has_value_(x.has_value_) - { - if (!has_value_) - { - std::construct_at(std::addressof(unex_), x.unex_); - } - } - - Expected(Expected&&) = default; - - constexpr - Expected(Expected&& x) - noexcept(std::is_nothrow_move_constructible_v) - requires - std::is_move_constructible_v && - (!std::is_trivially_move_constructible_v) - : void_(), has_value_(x.has_value_) - { - if (!has_value_) - { - std::construct_at(std::addressof(unex_), std::move(x).unex_); - } - } - - template - requires - std::is_void_v && - std::is_constructible_v && - (!constructible_from_expected) - constexpr - explicit(!std::is_convertible_v) - Expected(const Expected& x) - noexcept(std::is_nothrow_constructible_v) - : void_() - , has_value_(x.has_value_) - { - if (!has_value_) - { - std::construct_at(std::addressof(unex_), x.unex_); - } - } - - template - requires - std::is_void_v && - std::is_constructible_v && - (!constructible_from_expected) - constexpr - explicit(!std::is_convertible_v) - Expected(Expected&& x) - noexcept(std::is_nothrow_constructible_v) - : void_() - , has_value_(x.has_value_) - { - if (!has_value_) - { - std::construct_at(std::addressof(unex_), std::move(x).unex_); - } - } - - template - requires std::is_constructible_v - constexpr - explicit(!std::is_convertible_v) - Expected(const Unexpected& u) - noexcept(std::is_nothrow_constructible_v) - : unex_(u.error()) - , has_value_(false) - { } - - template - requires std::is_constructible_v - constexpr - explicit(!std::is_convertible_v) - Expected(Unexpected&& u) - noexcept(std::is_nothrow_constructible_v) - : unex_(std::move(u).error()), has_value_(false) - { } - - constexpr explicit - Expected(std::in_place_t) noexcept - : Expected() - { } - - template - requires std::is_constructible_v - constexpr explicit - Expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : unex_(std::forward(args)...) - , has_value_(false) - { } - - template - requires std::is_constructible_v&, Args...> - constexpr explicit - Expected(unexpect_t, std::initializer_list il, Args&&... args) - noexcept( - std::is_nothrow_constructible_v< - E, std::initializer_list&, Args...>) - : unex_(il, std::forward(args)...), has_value_(false) - { } - - constexpr ~Expected() = default; - - constexpr ~Expected() - requires (!std::is_trivially_destructible_v) - { - if (!has_value_) - { - std::destroy_at(std::addressof(unex_)); - } - } - - Expected& operator=(Expected const&) = delete; - - constexpr - Expected& - operator=(Expected const& x) - noexcept( - std::is_nothrow_copy_constructible_v && - std::is_nothrow_copy_assignable_v) - requires - std::is_copy_constructible_v && - std::is_copy_assignable_v - { - if (x.has_value_) - { - emplace(); - } - else - { - assign_unex_impl(x.unex_); - } - return *this; - } - - constexpr - Expected& - operator=(Expected&& x) - noexcept( - std::is_nothrow_move_constructible_v && - std::is_nothrow_move_assignable_v) - requires - std::is_move_constructible_v && - std::is_move_assignable_v - { - if (x.has_value_) - { - emplace(); - } - else - { - assign_unex_impl(std::move(x.unex_)); - } - return *this; - } - - template - requires - std::is_constructible_v && - std::is_assignable_v - constexpr - Expected& - operator=(const Unexpected& e) - { - assign_unex_impl(e.error()); - return *this; - } - - template - requires - std::is_constructible_v && - std::is_assignable_v - constexpr - Expected& - operator=(Unexpected&& e) - { - assign_unex_impl(std::move(e.error())); - return *this; - } - - constexpr - void - emplace() noexcept - { - if (!has_value_) - { - std::destroy_at(std::addressof(unex_)); - has_value_ = true; - } - } - - constexpr - void - swap(Expected& x) - noexcept( - std::is_nothrow_swappable_v && - std::is_nothrow_move_constructible_v) - requires - std::is_swappable_v && - std::is_move_constructible_v - { - if (has_value_) - { - if (!x.has_value_) - { - std::construct_at( - std::addressof(unex_), - std::move(x.unex_)); - std::destroy_at(std::addressof(x.unex_)); - has_value_ = false; - x.has_value_ = true; - } - } - else - { - if (x.has_value_) - { - std::construct_at( - std::addressof(x.unex_), - std::move(unex_)); - std::destroy_at(std::addressof(unex_)); - has_value_ = true; - x.has_value_ = false; - } - else - { - using std::swap; - swap(unex_, x.unex_); - } - } - } - - [[nodiscard]] - constexpr - explicit - operator bool() const noexcept - { - return has_value_; - } - - [[nodiscard]] - constexpr - bool has_value() const noexcept - { - return has_value_; - } - - constexpr - void - operator*() const noexcept { - MRDOCS_ASSERT(has_value_); - } - - constexpr - void - value() const& - { - if (has_value_) [[likely]] - { - return; - } - throw BadExpectedAccess(unex_); - } - - constexpr - void - value() && - { - if (has_value_) [[likely]] - { - return; - } - throw BadExpectedAccess(std::move(unex_)); - } - - constexpr E const& - error() const & noexcept - { - MRDOCS_ASSERT(!has_value_); - return unex_; - } - - constexpr E& - error() & noexcept - { - MRDOCS_ASSERT(!has_value_); - return unex_; - } - - constexpr E const&& - error() const && noexcept - { - MRDOCS_ASSERT(!has_value_); - return std::move(unex_); - } - - constexpr E&& - error() && noexcept - { - MRDOCS_ASSERT(!has_value_); - return std::move(unex_); - } - - template - constexpr E - error_or(G&& e) const& - { - static_assert( std::is_copy_constructible_v ); - static_assert( std::is_convertible_v ); - - if (has_value_) - { - return std::forward(e); - } - return unex_; - } - - template - constexpr E - error_or(G&& e) && - { - static_assert( std::is_move_constructible_v ); - static_assert( std::is_convertible_v ); - - if (has_value_) - { - return std::forward(e); - } - return std::move(unex_); - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) & - { - using U = detail::result0; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f)); - } - else - { - return U(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) const & - { - using U = detail::result0; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f)); - } - else - { - return U(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) && - { - using U = detail::result0; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f)); - } - else - { - return U(unexpect, std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - and_then(Fn&& f) const && - { - using U = detail::result0; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return std::invoke(std::forward(f)); - } - else - { - return U(unexpect, std::move(unex_)); - } - } - - template - constexpr - auto - or_else(Fn&& f) & - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(); - } - else - { - return std::invoke(std::forward(f), unex_); - } - } - - template - constexpr - auto - or_else(Fn&& f) const & - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(); - } - else - { - return std::invoke(std::forward(f), unex_); - } - } - - template - constexpr - auto - or_else(Fn&& f) && - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(); - } - else - { - return std::invoke(std::forward(f), std::move(unex_)); - } - } - - template - constexpr - auto - or_else(Fn&& f) const&& - { - using G = detail::then_result; - static_assert(detail::isExpected); - static_assert(std::is_same_v); - - if (has_value()) - { - return G(); - } - else - { - return std::invoke(std::forward(f), std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) & - { - using U = detail::result0_xform; - using Res = Expected; - - if (has_value()) - { - return Res(in_place_inv{}, std::forward(f)); - } - else - { - return Res(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) const & - { - using U = detail::result0_xform; - using Res = Expected; - - if (has_value()) - { - return Res(in_place_inv{}, std::forward(f)); - } - else - { - return Res(unexpect, unex_); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) && - { - using U = detail::result0_xform; - using Res = Expected; - - if (has_value()) - { - return Res(in_place_inv{}, std::forward(f)); - } - else - { - return Res(unexpect, std::move(unex_)); - } - } - - template - requires std::is_constructible_v - constexpr - auto - transform(Fn&& f) const && - { - using U = detail::result0_xform; - using Res = Expected; - - if (has_value()) - { - return Res(in_place_inv{}, std::forward(f)); - } - else - { - return Res(unexpect, std::move(unex_)); - } - } - - template - constexpr - auto - transform_error(Fn&& f) & - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), unex_); - }); - } - } - - template - constexpr - auto - transform_error(Fn&& f) const & - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), unex_); - }); - } - } - - template - constexpr - auto - transform_error(Fn&& f) && - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(unex_)); - }); - } - } - - template - constexpr - auto - transform_error(Fn&& f) const && - { - using G = detail::result_transform; - using Res = Expected; - - if (has_value()) - { - return Res(); - } - else - { - return Res( - unexpect_inv{}, [&]() { - return std::invoke(std::forward(f), std::move(unex_)); - }); - } - } - - template - requires std::is_void_v - friend - constexpr - bool - operator==(Expected const& x, const Expected& y) - noexcept(noexcept(bool(x.error() == y.error()))) - { - if (x.has_value()) - { - return y.has_value(); - } - else - { - return !y.has_value() && bool(x.error() == y.error()); - } - } - - template - friend - constexpr - bool - operator==(Expected const& x, const Unexpected& e) - noexcept(noexcept(bool(x.error() == e.error()))) - { - return !x.has_value() && bool(x.error() == e.error()); - } - - friend - constexpr - void - swap(Expected& x, Expected& y) - noexcept(noexcept(x.swap(y))) - requires requires { x.swap(y); } - { - x.swap(y); - } - -private: - template - constexpr - void - assign_unex_impl(Vp&& v) - { - if (has_value_) - { - std::construct_at( - std::addressof(unex_), - std::forward(v)); - has_value_ = false; - } - else - { - unex_ = std::forward(v); - } - } - - using in_place_inv = detail::in_place_inv; - using unexpect_inv = detail::unexpect_inv; - - template - explicit constexpr - Expected(in_place_inv, Fn&& fn) - : void_() - , has_value_(true) - { - std::forward(fn)(); - } - - template - explicit constexpr - Expected(unexpect_inv, Fn&& fn) - : unex_(std::forward(fn)()) - , has_value_(false) - { } -}; - -//------------------------------------------------ -// -// SourceLocation -// -//------------------------------------------------ - -/** A source location with filename prettification. -*/ -class MRDOCS_DECL - SourceLocation -{ - std::string_view file_; - std::uint_least32_t line_; - std::uint_least32_t col_; - std::string_view func_; - -public: - SourceLocation( - source_location const& loc) noexcept; - - std::string_view file_name() const noexcept - { - return file_; - } - - std::uint_least32_t line() const noexcept - { - return line_; - } - - std::uint_least32_t column() const noexcept - { - return col_; - } - - std::string_view function_name() const noexcept - { - return func_; - } -}; - -//------------------------------------------------ -// -// Implementation -// -//------------------------------------------------ - -template -struct FormatString -{ - template - FormatString( - T const& fs_, - source_location loc_ = - source_location::current()) - : fs(fs_) - , loc(loc_) - { - static_assert(std::is_constructible_v< - std::string_view, T const&>); - } - - std::string_view fs; - source_location loc; -}; - -/** Return a formatted error. - - @param fs The format string. This - must not be empty. - - @param args Zero or more values to - substitute into the format string. -*/ -template -Error -formatError( - FormatString...> fs, - Args&&... args) -{ - std::string s; - fmt::vformat_to( - std::back_inserter(s), - fs.fs, fmt::make_format_args(args...)); - return Error(std::move(s), fs.loc); } -//------------------------------------------------ -// -// Reporting -// -//------------------------------------------------ - -namespace report { - -/** Severity levels attached to reported messags. -*/ -enum class Level -{ - trace = 0, - debug, - info, - warn, - error, - fatal -}; - -/** Provides statistics on the number of reported messages. -*/ -struct Results -{ - std::size_t traceCount; - std::size_t debugCount; - std::size_t infoCount; - std::size_t warnCount; - std::size_t errorCount; - std::size_t fatalCount; -}; - -/** Holds current statistics on reported messages. -*/ -extern -MRDOCS_DECL -Results -results; - -/** Set the minimum threshold level for reporting. - - Messages below this level will not be printed. - A value of 5 will suppress all messages. Note - that messages will still be counted towards - result totals even if they are not displayed. -*/ -MRDOCS_DECL -void -setMinimumLevel(Level level) noexcept; - -MRDOCS_DECL -Level -getMinimumLevel() noexcept; - -/** If true, source location information will be - printed with warnings, errors, and fatal messages. -*/ -MRDOCS_DECL -void -setSourceLocationWarnings(bool b) noexcept; - -/** Report a message to the console. - - @param text The message to print. A - trailing newline will be added to the - message automatically. -*/ -MRDOCS_DECL -void -print( - std::string const& text); - -/** Report a message to the console. - - @param level 0 to 4 The severity of the - report. 0 is debug and 4 is fatal. - - @param text The message to print. A - trailing newline will be added to the - message automatically. - - @param loc The source location of the report. - If this value is null, no location is printed. -*/ -MRDOCS_DECL -void -print( - Level level, - std::string const& text, - source_location const* loc = nullptr, - Error const* e = nullptr); - -namespace detail { -template -requires (!std::same_as, Error>) -void -log_impl( - Level level, - Located fs, - Arg0&& arg0, - Args&&... args) -{ - std::string str = fmt::vformat( - fs.value, - fmt::make_format_args(arg0, args...)); - return print( - level, - str, - &fs.where); -} - -template -void -log_impl( - Level level, - Located fs, - Error const& e, - Args&&... args) -{ - // When the message is an error, we send split - // the information relevant to the user from - // the information relevant for bug tracking - // so that users can understand the message. - std::string str = fmt::vformat( - fs.value, - fmt::make_format_args(e.reason(), args...)); - return print( - level, - str, - &fs.where, - &e); -} - -inline -void -log_impl( - Level level, - Located fs) -{ - std::string str = fmt::vformat( - fs.value, fmt::make_format_args()); - return print( - level, - str, - &fs.where); -} -} - -/** Format a message to the console. - - @param level 0 to 4 The severity of the - report. 0 is debug and 4 is fatal. - - @param fs The format string. - - @param args... Optional additional arguments - used to format a message to print. A trailing - newline will be added to the message - automatically. -*/ -template -void -log( - Level level, - Located fs, - Args&&... args) -{ - return detail::log_impl( - level, - fs, - std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -trace( - Located format, - Args&&... args) -{ - return log(Level::trace, format, std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -debug( - Located format, - Args&&... args) -{ - return log(Level::debug, format, std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -info( - Located format, - Args&&... args) -{ - return log(Level::info, format, std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -warn( - Located format, - Args&&... args) -{ - return log(Level::warn, format, std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -error( - Located format, - Args&&... args) -{ - return log(Level::error, format, std::forward(args)...); -} - -/** Report a message to the console. -*/ -template -void -fatal( - Located format, - Args&&... args) -{ - return log(Level::fatal, format, std::forward(args)...); -} - -} // report - -} // mrdocs -} // clang - -//------------------------------------------------ - -template<> -struct std::hash<::clang::mrdocs::Error> -{ - std::size_t operator()( - ::clang::mrdocs::Error const& err) const noexcept - { - return std::hash()(err.message()); - } -}; - -//------------------------------------------------ - -template<> -struct fmt::formatter - : fmt::formatter -{ - auto format( - clang::mrdocs::Error const& err, - fmt::format_context& ctx) const - { - return fmt::formatter::format(err.message(), ctx); - } -}; - -template<> -struct fmt::formatter - : fmt::formatter -{ - auto format( - std::error_code const& ec, - fmt::format_context& ctx) const - { - return fmt::formatter::format(ec.message(), ctx); - } -}; - #endif diff --git a/include/mrdocs/Support/Expected.hpp b/include/mrdocs/Support/Expected.hpp new file mode 100644 index 0000000000..729d892682 --- /dev/null +++ b/include/mrdocs/Support/Expected.hpp @@ -0,0 +1,2342 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_SUPPORT_EXPECTED_HPP +#define MRDOCS_API_SUPPORT_EXPECTED_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clang::mrdocs { + +//------------------------------------------------ +// +// Expected +// +//------------------------------------------------ + +template +class Expected; + +template +class Unexpected; + +template +class BadExpectedAccess; + +template <> +class BadExpectedAccess : public std::exception +{ +protected: + BadExpectedAccess() noexcept = default; + + BadExpectedAccess(BadExpectedAccess const&) = default; + + BadExpectedAccess(BadExpectedAccess&&) = default; + + BadExpectedAccess& + operator=(BadExpectedAccess const&) = default; + + BadExpectedAccess& + operator=(BadExpectedAccess&&) = default; + + ~BadExpectedAccess() override = default; +public: + [[nodiscard]] + char const* + what() const noexcept override + { + return "bad access to Expected without Expected value"; + } +}; + +template +class BadExpectedAccess : public BadExpectedAccess { + E unex_; +public: + explicit + BadExpectedAccess(E e) + : unex_(std::move(e)) { } + + [[nodiscard]] + E& + error() & noexcept + { + return unex_; + } + + [[nodiscard]] + E const& + error() const & noexcept + { + return unex_; + } + + [[nodiscard]] + E&& + error() && noexcept + { + return std::move(unex_); + } + + [[nodiscard]] + E const&& + error() const && noexcept + { + return std::move(unex_); + } +}; + +struct unexpect_t +{ + explicit unexpect_t() = default; +}; + +inline constexpr unexpect_t unexpect{}; + +namespace detail +{ + template + constexpr bool isExpected = false; + template + constexpr bool isExpected> = true; + + template + constexpr bool isUnexpected = false; + template + constexpr bool isUnexpected> = true; + + template + using then_result = std::remove_cvref_t>; + template + using result_transform = std::remove_cv_t>; + template + using result0 = std::remove_cvref_t>; + template + using result0_xform = std::remove_cv_t>; + + template + concept can_beUnexpected = + std::is_object_v && + (!std::is_array_v) && + (!detail::isUnexpected) && + (!std::is_const_v) && + (!std::is_volatile_v); + + // Tag types for in-place construction from an invocation result. + struct in_place_inv { }; + struct unexpect_inv { }; +} + +template +class Unexpected +{ + static_assert(detail::can_beUnexpected); + E unex_; + +public: + constexpr + Unexpected(Unexpected const&) = default; + + constexpr + Unexpected(Unexpected&&) = default; + + template + requires + (!std::is_same_v, Unexpected>) && + (!std::is_same_v, std::in_place_t>) && + std::is_constructible_v + constexpr explicit + Unexpected(Er&& e) noexcept(std::is_nothrow_constructible_v) + : unex_(std::forward(e)) + {} + + template + requires std::is_constructible_v + constexpr explicit + Unexpected(std::in_place_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::forward(args)...) + {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit + Unexpected( + std::in_place_t, + std::initializer_list il, + Args&&... args) + noexcept(std::is_nothrow_constructible_v< + E, std::initializer_list&, Args...>) + : unex_(il, std::forward(args)...) + {} + + constexpr Unexpected& operator=(Unexpected const&) = default; + constexpr Unexpected& operator=(Unexpected&&) = default; + + [[nodiscard]] + constexpr E const& + error() const & noexcept + { + return unex_; + } + + [[nodiscard]] + constexpr E& + error() & noexcept + { + return unex_; + } + + [[nodiscard]] + constexpr E const&& + error() const && noexcept + { + return std::move(unex_); + } + + [[nodiscard]] + constexpr E&& + error() && noexcept + { + return std::move(unex_); + } + + constexpr + void + swap(Unexpected& other) noexcept(std::is_nothrow_swappable_v) + requires std::is_swappable_v + { + using std::swap; + swap(unex_, other.unex_); + } + + template + [[nodiscard]] + friend + constexpr + bool + operator==(Unexpected const& x, const Unexpected& y) + { + return x.unex_ == y.error(); + } + + friend + constexpr + void + swap(Unexpected& x, Unexpected& y) + noexcept(noexcept(x.swap(y))) + requires std::is_swappable_v + { + x.swap(y); + } +}; + +template Unexpected(E) -> Unexpected; + +namespace detail +{ + template + constexpr + bool + failed(T const&t) + { + if constexpr (isExpected>) + { + return !t; + } + else if constexpr (std::same_as, Error>) + { + return t.failed(); + } + else if constexpr (requires (T const& t0) { t0.empty(); }) + { + return t.empty(); + } + else if constexpr (std::constructible_from) + { + return !t; + } + else + { + return false; + } + } + + template + constexpr + decltype(auto) + error(T const& t) + { + if constexpr (isExpected>) + { + return t.error(); + } + else if constexpr (std::same_as, Error>) + { + return t; + } + else if constexpr (requires (T const& t0) { t0.empty(); }) + { + return Error("Empty value"); + } + else if constexpr (std::constructible_from) + { + return Error("Invalid value"); + } + } +} + +#ifndef MRDOCS_TRY +# define MRDOCS_MERGE_(a, b) a##b +# define MRDOCS_LABEL_(a) MRDOCS_MERGE_(expected_result_, a) +# define MRDOCS_UNIQUE_NAME MRDOCS_LABEL_(__LINE__) + +/// Try to retrive expected-like type +# define MRDOCS_TRY_VOID(expr) \ + auto MRDOCS_UNIQUE_NAME = expr; \ + if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ + return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \ + } \ + void(0) +# define MRDOCS_TRY_VAR(var, expr) \ + auto MRDOCS_UNIQUE_NAME = expr; \ + if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ + return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \ + } \ + var = *std::move(MRDOCS_UNIQUE_NAME) +# define MRDOCS_TRY_MSG(var, expr, msg) \ + auto MRDOCS_UNIQUE_NAME = expr; \ + if (detail::failed(MRDOCS_UNIQUE_NAME)) { \ + return Unexpected(Error(msg)); \ + } \ + var = *std::move(MRDOCS_UNIQUE_NAME) +# define MRDOCS_TRY_GET_MACRO(_1, _2, _3, NAME, ...) NAME +# define MRDOCS_TRY(...) \ + MRDOCS_TRY_GET_MACRO(__VA_ARGS__, MRDOCS_TRY_MSG, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__) + +/// Check existing expected-like type +# define MRDOCS_CHECK_VOID(var) \ + if (detail::failed(var)) { \ + return Unexpected(detail::error(var)); \ + } \ + void(0) +# define MRDOCS_CHECK_MSG(var, msg) \ + if (detail::failed(var)) { \ + return Unexpected(Error(msg)); \ + } \ + void(0) +# define MRDOCS_CHECK_GET_MACRO(_1, _2, NAME, ...) NAME +# define MRDOCS_CHECK(...) \ + MRDOCS_CHECK_GET_MACRO(__VA_ARGS__, MRDOCS_CHECK_MSG, MRDOCS_CHECK_VOID)(__VA_ARGS__) + +/// Check existing expected-like type and return custom value otherwise +# define MRDOCS_CHECK_OR_VOID(var) \ + if (detail::failed(var)) { \ + return; \ + } \ + void(0) +# define MRDOCS_CHECK_OR_VALUE(var, value) \ + if (detail::failed(var)) { \ + return value; \ + } \ + void(0) +# define MRDOCS_CHECK_GET_OR_MACRO(_1, _2, NAME, ...) NAME +# define MRDOCS_CHECK_OR(...) \ + MRDOCS_CHECK_GET_OR_MACRO(__VA_ARGS__, MRDOCS_CHECK_OR_VALUE, MRDOCS_CHECK_OR_VOID)(__VA_ARGS__) + +# define MRDOCS_CHECK_OR_CONTINUE(var) \ + if (detail::failed(var)) { \ + continue; \ + } \ + void(0) + +#endif + + +/// @cond undocumented +namespace detail +{ + template + class ExpectedGuard + { + static_assert( std::is_nothrow_move_constructible_v ); + T* guarded_; + T tmp_; + + public: + constexpr explicit + ExpectedGuard(T& x) + : guarded_(std::addressof(x)) + , tmp_(std::move(x)) + { + std::destroy_at(guarded_); + } + + constexpr + ~ExpectedGuard() + { + if (guarded_) [[unlikely]] + { + std::construct_at(guarded_, std::move(tmp_)); + } + } + + ExpectedGuard(ExpectedGuard const&) = delete; + + ExpectedGuard& operator=(ExpectedGuard const&) = delete; + + constexpr T&& + release() noexcept + { + guarded_ = nullptr; + return std::move(tmp_); + } + }; + + template + constexpr + void + ExpectedReinit(T* newval, U* oldval, Vp&& arg) + noexcept(std::is_nothrow_constructible_v) + { + if constexpr (std::is_nothrow_constructible_v) + { + std::destroy_at(oldval); + std::construct_at(newval, std::forward(arg)); + } + else if constexpr (std::is_nothrow_move_constructible_v) + { + T tmp(std::forward(arg)); // might throw + std::destroy_at(oldval); + std::construct_at(newval, std::move(tmp)); + } + else + { + ExpectedGuard guard(*oldval); + std::construct_at(newval, std::forward(arg)); // might throw + guard.release(); + } + } +} +/// @endcond + +/** A container holding an error or a value. + */ +template +class Expected +{ + static_assert(!std::is_reference_v); + static_assert(!std::is_function_v); + static_assert(!std::is_same_v, std::in_place_t>); + static_assert(!std::is_same_v, unexpect_t>); + static_assert(!detail::isUnexpected>); + static_assert(detail::can_beUnexpected); + + template > + static constexpr bool constructible_from_expected = + std::constructible_from&> || + std::constructible_from> || + std::constructible_from&> || + std::constructible_from> || + std::convertible_to&, T> || + std::convertible_to, T> || + std::convertible_to&, T> || + std::convertible_to, T> || + std::constructible_from&> || + std::constructible_from> || + std::constructible_from&> || + std::constructible_from>; + + template + constexpr static bool explicit_conv + = (!std::convertible_to) || + (!std::convertible_to); + + template + static constexpr bool same_val + = std::is_same_v; + + template + static constexpr bool same_err + = std::is_same_v; + + template friend class Expected; + + union { + T val_; + E unex_; + }; + + bool has_value_; + +public: + using value_type = T; + using error_type = E; + using unexpected_type = Unexpected; + + template + using rebind = Expected; + + constexpr + Expected() + noexcept(std::is_nothrow_default_constructible_v) + requires std::is_default_constructible_v + : val_() + , has_value_(true) + {} + + Expected(Expected const&) = default; + + constexpr + Expected(Expected const& x) + noexcept( + std::is_nothrow_copy_constructible_v && + std::is_nothrow_copy_constructible_v) + requires + std::is_copy_constructible_v && + std::is_copy_constructible_v && + (!std::is_trivially_copy_constructible_v || + !std::is_trivially_copy_constructible_v) + : has_value_(x.has_value_) + { + if (has_value_) + { + std::construct_at(std::addressof(val_), x.val_); + } + else + { + std::construct_at(std::addressof(unex_), x.unex_); + } + } + + Expected(Expected&&) = default; + + constexpr + Expected(Expected&& x) + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires + std::is_move_constructible_v && + std::is_move_constructible_v && + (!std::is_trivially_move_constructible_v || + !std::is_trivially_move_constructible_v) + : has_value_(x.has_value_) + { + if (has_value_) + { + std::construct_at(std::addressof(val_), std::move(x).val_); + } + else + { + std::construct_at(std::addressof(unex_), std::move(x).unex_); + } + } + + template + requires + std::is_constructible_v && + std::is_constructible_v && + (!constructible_from_expected) + constexpr + explicit(explicit_conv) + Expected(const Expected& x) + noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_constructible_v) + : has_value_(x.has_value_) + { + if (has_value_) + { + std::construct_at(std::addressof(val_), x.val_); + } + else + { + std::construct_at(std::addressof(unex_), x.unex_); + } + } + + template + requires + std::is_constructible_v && + std::is_constructible_v && + (!constructible_from_expected) + constexpr + explicit(explicit_conv) + Expected(Expected&& x) + noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_constructible_v) + : has_value_(x.has_value_) + { + if (has_value_) + { + std::construct_at(std::addressof(val_), std::move(x).val_); + } + else + { + std::construct_at(std::addressof(unex_), std::move(x).unex_); + } + } + + template + requires + (!std::is_same_v, Expected>) && + (!std::is_same_v, std::in_place_t>) && + (!detail::isUnexpected>) && + std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(U&& v) + noexcept(std::is_nothrow_constructible_v) + : val_(std::forward(v)) + , has_value_(true) + { } + + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(const Unexpected& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(u.error()) + , has_value_(false) + { } + + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(Unexpected&& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::move(u).error()) + , has_value_(false) + { } + + template + requires std::is_constructible_v + constexpr explicit + Expected(std::in_place_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : val_(std::forward(args)...) + , has_value_(true) + { } + + template + requires + std::is_constructible_v&, Args...> + constexpr explicit + Expected( + std::in_place_t, + std::initializer_list il, + Args&&... args) + noexcept( + std::is_nothrow_constructible_v< + T, std::initializer_list&, Args...>) + : val_(il, std::forward(args)...) + , has_value_(true) + { } + + template + requires std::is_constructible_v + constexpr explicit + Expected(unexpect_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::forward(args)...) + , has_value_(false) + { } + + template + requires std::is_constructible_v&, Args...> + constexpr explicit + Expected( + unexpect_t, + std::initializer_list il, + Args&&... args) + noexcept( + std::is_nothrow_constructible_v< + E, std::initializer_list&, Args...>) + : unex_(il, std::forward(args)...) + , has_value_(false) + { } + + constexpr ~Expected() = default; + + constexpr ~Expected() + requires + (!std::is_trivially_destructible_v) + || (!std::is_trivially_destructible_v) + { + if (has_value_) + { + std::destroy_at(std::addressof(val_)); + } + else + { + std::destroy_at(std::addressof(unex_)); + } + } + + Expected& + operator=(Expected const&) = delete; + + constexpr + Expected& + operator=(Expected const& x) + noexcept( + std::is_nothrow_copy_constructible_v && + std::is_nothrow_copy_constructible_v && + std::is_nothrow_copy_assignable_v && + std::is_nothrow_copy_assignable_v) + requires + std::is_copy_assignable_v && + std::is_copy_constructible_v && + std::is_copy_assignable_v && + std::is_copy_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (x.has_value_) + { + this->assign_val_impl(x.val_); + } + else + { + this->assign_unex_impl(x.unex_); + } + return *this; + } + + constexpr + Expected& + operator=(Expected&& x) + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && + std::is_nothrow_move_assignable_v) + requires + std::is_move_assignable_v && + std::is_move_constructible_v && + std::is_move_assignable_v && + std::is_move_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (x.has_value_) + { + assign_val_impl(std::move(x.val_)); + } + else + { + assign_unex_impl(std::move(x.unex_)); + } + return *this; + } + + template + requires + (!std::is_same_v>) && + (!detail::isUnexpected>) && + std::is_constructible_v && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr + Expected& + operator=(U&& v) + { + assign_val_impl(std::forward(v)); + return *this; + } + + template + requires + std::is_constructible_v && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr + Expected& + operator=(const Unexpected& e) + { + assign_unex_impl(e.error()); + return *this; + } + + template + requires + std::is_constructible_v && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr + Expected& + operator=(Unexpected&& e) + { + assign_unex_impl(std::move(e).error()); + return *this; + } + + template + requires std::is_nothrow_constructible_v + constexpr + T& + emplace(Args&&... args) noexcept + { + if (has_value_) + { + std::destroy_at(std::addressof(val_)); + } + else + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + std::construct_at( + std::addressof(val_), + std::forward(args)...); + return val_; + } + + template + requires + std::is_nothrow_constructible_v< + T, std::initializer_list&, Args...> + constexpr + T& + emplace(std::initializer_list il, Args&&... args) noexcept + { + if (has_value_) + { + std::destroy_at(std::addressof(val_)); + } + else + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + std::construct_at( + std::addressof(val_), il, + std::forward(args)...); + return val_; + } + + constexpr + void + swap(Expected& x) + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v && + std::is_nothrow_swappable_v) + requires + std::is_swappable_v && + std::is_swappable_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (has_value_) + { + if (x.has_value_) + { + using std::swap; + swap(val_, x.val_); + } + else + { + this->swap_val_unex_impl(x); + } + } + else + { + if (x.has_value_) + { + x.swap_val_unex_impl(*this); + } + else + { + using std::swap; + swap(unex_, x.unex_); + } + } + } + + // observers + + [[nodiscard]] + constexpr + T const* + operator->() const noexcept + { + MRDOCS_ASSERT(has_value_); + return std::addressof(val_); + } + + [[nodiscard]] + constexpr + T* + operator->() noexcept + { + MRDOCS_ASSERT(has_value_); + return std::addressof(val_); + } + + [[nodiscard]] + constexpr + T const& + operator*() const & noexcept + { + MRDOCS_ASSERT(has_value_); + return val_; + } + + [[nodiscard]] + constexpr + T& + operator*() & noexcept + { + MRDOCS_ASSERT(has_value_); + return val_; + } + + [[nodiscard]] + constexpr + T const&& + operator*() const && noexcept + { + MRDOCS_ASSERT(has_value_); + return std::move(val_); + } + + [[nodiscard]] + constexpr + T&& + operator*() && noexcept + { + MRDOCS_ASSERT(has_value_); + return std::move(val_); + } + + [[nodiscard]] + constexpr explicit + operator bool() const noexcept + { + return has_value_; + } + + [[nodiscard]] + constexpr bool has_value() const noexcept + { + return has_value_; + } + + constexpr T const& + value() const & + { + if (has_value_) [[likely]] + { + return val_; + } + throw BadExpectedAccess(unex_); + } + + constexpr T& + value() & + { + if (has_value_) [[likely]] + { + return val_; + } + auto const& unex = unex_; + throw BadExpectedAccess(unex); + } + + constexpr T const&& + value() const && + { + if (has_value_) [[likely]] + { + return std::move(val_); + } + throw BadExpectedAccess(std::move(unex_)); + } + + constexpr T&& + value() && + { + if (has_value_) [[likely]] + { + return std::move(val_); + } + throw BadExpectedAccess(std::move(unex_)); + } + + constexpr E const& + error() const & noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + constexpr E& + error() & noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + constexpr E const&& + error() const && noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + constexpr E&& + error() && noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + template + constexpr + T + value_or(U&& v) const & + noexcept( + std::is_nothrow_copy_constructible_v && + std::is_nothrow_convertible_v) + { + static_assert( std::is_copy_constructible_v ); + static_assert( std::is_convertible_v ); + if (has_value_) + { + return val_; + } + return static_cast(std::forward(v)); + } + + template + constexpr + T + value_or(U&& v) && + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_convertible_v) + { + static_assert( std::is_move_constructible_v ); + static_assert( std::is_convertible_v ); + if (has_value_) + { + return std::move(val_); + } + return static_cast(std::forward(v)); + } + + template + constexpr + E + error_or(G&& e) const& + { + static_assert(std::is_copy_constructible_v); + static_assert(std::is_convertible_v); + if (has_value_) + { + return std::forward(e); + } + return unex_; + } + + template + constexpr E + error_or(G&& e) && + { + static_assert(std::is_move_constructible_v); + static_assert(std::is_convertible_v); + if (has_value_) + { + return std::forward(e); + } + return std::move(unex_); + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) & + { + using U = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f), val_); + } + else + { + return U(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) const & + { + using U = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f), val_); + } + else + { + return U(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) && + { + using U = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f), std::move(val_)); + } + else + { + return U(unexpect, std::move(unex_)); + } + } + + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) const && + { + using U = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f), std::move(val_)); + } + else + { + return U(unexpect, std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + or_else(Fn&& f) & + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(std::in_place, val_); + } + else + { + return std::invoke(std::forward(f), unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + or_else(Fn&& f) const & + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(std::in_place, val_); + } + else + { + return std::invoke(std::forward(f), unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + or_else(Fn&& f) && + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(std::in_place, std::move(val_)); + } + else + { + return std::invoke(std::forward(f), std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + or_else(Fn&& f) const && + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(std::in_place, std::move(val_)); + } + else + { + return std::invoke(std::forward(f), std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) & + { + using U = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res( + in_place_inv{}, [&]() { + return std::invoke(std::forward(f), val_); + }); + } + else + { + return Res(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) const & + { + using U = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res( + in_place_inv{}, [&]() { + return std::invoke(std::forward(f), val_); + }); + } + else + { + return Res(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) && + { + using U = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res( + in_place_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(val_)); + }); + } + else + { + return Res(unexpect, std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) const && + { + using U = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res( + in_place_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(val_)); + }); + } + else + { + return Res(unexpect, std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform_error(Fn&& f) & + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(std::in_place, val_); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), unex_); + }); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform_error(Fn&& f) const & + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(std::in_place, val_); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), unex_); + }); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform_error(Fn&& f) && + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(std::in_place, std::move(val_)); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform_error(Fn&& f) const && + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(std::in_place, std::move(val_)); + } + else + { + return Res(unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + } + + template + requires (!std::is_void_v) + friend + constexpr + bool + operator==(Expected const& x, const Expected& y) + noexcept( + noexcept(bool(*x == *y)) && + noexcept(bool(x.error() == y.error()))) + { + if (x.has_value()) + { + return y.has_value() && bool(*x == *y); + } + else + { + return !y.has_value() && bool(x.error() == y.error()); + } + } + + template + friend + constexpr + bool + operator==(Expected const& x, U const& v) + noexcept(noexcept(bool(*x == v))) + { + return x.has_value() && bool(*x == v); + } + + template + friend + constexpr + bool + operator==(Expected const& x, const Unexpected& e) + noexcept(noexcept(bool(x.error() == e.error()))) + { + return !x.has_value() && bool(x.error() == e.error()); + } + + friend + constexpr + void + swap(Expected& x, Expected& y) + noexcept(noexcept(x.swap(y))) + requires requires {x.swap(y);} + { + x.swap(y); + } + +private: + template + constexpr + void + assign_val_impl(Vp&& v) + { + if (has_value_) + { + val_ = std::forward(v); + } + else + { + detail::ExpectedReinit( + std::addressof(val_), + std::addressof(unex_), + std::forward(v)); + has_value_ = true; + } + } + + template + constexpr + void + assign_unex_impl(Vp&& v) + { + if (has_value_) + { + detail::ExpectedReinit( + std::addressof(unex_), + std::addressof(val_), + std::forward(v)); + has_value_ = false; + } + else + { + unex_ = std::forward(v); + } + } + + constexpr + void + swap_val_unex_impl(Expected& rhs) + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + { + if constexpr (std::is_nothrow_move_constructible_v) + { + detail::ExpectedGuard guard(rhs.unex_); + std::construct_at( + std::addressof(rhs.val_), + std::move(val_)); + rhs.has_value_ = true; + std::destroy_at(std::addressof(val_)); + std::construct_at(std::addressof(unex_), guard.release()); + has_value_ = false; + } + else + { + detail::ExpectedGuard guard(val_); + std::construct_at( + std::addressof(unex_), + std::move(rhs.unex_)); + has_value_ = false; + std::destroy_at(std::addressof(rhs.unex_)); + std::construct_at(std::addressof(rhs.val_), guard.release()); + rhs.has_value_ = true; + } + } + + using in_place_inv = detail::in_place_inv; + using unexpect_inv = detail::unexpect_inv; + + template + explicit constexpr + Expected(in_place_inv, Fn&& fn) + : val_(std::forward(fn)()), has_value_(true) + { } + + template + explicit constexpr + Expected(unexpect_inv, Fn&& fn) + : unex_(std::forward(fn)()), has_value_(false) + { } +}; + +template +requires std::is_void_v +class Expected +{ + static_assert( detail::can_beUnexpected ); + + template > + static constexpr bool constructible_from_expected = + std::is_constructible_v&> || + std::is_constructible_v> || + std::is_constructible_v&> || + std::is_constructible_v>; + + template + static constexpr bool same_val + = std::is_same_v; + + template + static constexpr bool same_err + = std::is_same_v; + + template friend class Expected; + + union { + struct { } void_; + E unex_; + }; + + bool has_value_; + +public: + using value_type = T; + using error_type = E; + using unexpected_type = Unexpected; + + template + using rebind = Expected; + + constexpr + Expected() noexcept + : void_() + , has_value_(true) + { } + + Expected(Expected const&) = default; + + constexpr + Expected(Expected const& x) + noexcept(std::is_nothrow_copy_constructible_v) + requires + std::is_copy_constructible_v && + (!std::is_trivially_copy_constructible_v) + : void_() + , has_value_(x.has_value_) + { + if (!has_value_) + { + std::construct_at(std::addressof(unex_), x.unex_); + } + } + + Expected(Expected&&) = default; + + constexpr + Expected(Expected&& x) + noexcept(std::is_nothrow_move_constructible_v) + requires + std::is_move_constructible_v && + (!std::is_trivially_move_constructible_v) + : void_(), has_value_(x.has_value_) + { + if (!has_value_) + { + std::construct_at(std::addressof(unex_), std::move(x).unex_); + } + } + + template + requires + std::is_void_v && + std::is_constructible_v && + (!constructible_from_expected) + constexpr + explicit(!std::is_convertible_v) + Expected(const Expected& x) + noexcept(std::is_nothrow_constructible_v) + : void_() + , has_value_(x.has_value_) + { + if (!has_value_) + { + std::construct_at(std::addressof(unex_), x.unex_); + } + } + + template + requires + std::is_void_v && + std::is_constructible_v && + (!constructible_from_expected) + constexpr + explicit(!std::is_convertible_v) + Expected(Expected&& x) + noexcept(std::is_nothrow_constructible_v) + : void_() + , has_value_(x.has_value_) + { + if (!has_value_) + { + std::construct_at(std::addressof(unex_), std::move(x).unex_); + } + } + + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(const Unexpected& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(u.error()) + , has_value_(false) + { } + + template + requires std::is_constructible_v + constexpr + explicit(!std::is_convertible_v) + Expected(Unexpected&& u) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::move(u).error()), has_value_(false) + { } + + constexpr explicit + Expected(std::in_place_t) noexcept + : Expected() + { } + + template + requires std::is_constructible_v + constexpr explicit + Expected(unexpect_t, Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : unex_(std::forward(args)...) + , has_value_(false) + { } + + template + requires std::is_constructible_v&, Args...> + constexpr explicit + Expected(unexpect_t, std::initializer_list il, Args&&... args) + noexcept( + std::is_nothrow_constructible_v< + E, std::initializer_list&, Args...>) + : unex_(il, std::forward(args)...), has_value_(false) + { } + + constexpr ~Expected() = default; + + constexpr ~Expected() + requires (!std::is_trivially_destructible_v) + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + } + } + + Expected& operator=(Expected const&) = delete; + + constexpr + Expected& + operator=(Expected const& x) + noexcept( + std::is_nothrow_copy_constructible_v && + std::is_nothrow_copy_assignable_v) + requires + std::is_copy_constructible_v && + std::is_copy_assignable_v + { + if (x.has_value_) + { + emplace(); + } + else + { + assign_unex_impl(x.unex_); + } + return *this; + } + + constexpr + Expected& + operator=(Expected&& x) + noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v) + requires + std::is_move_constructible_v && + std::is_move_assignable_v + { + if (x.has_value_) + { + emplace(); + } + else + { + assign_unex_impl(std::move(x.unex_)); + } + return *this; + } + + template + requires + std::is_constructible_v && + std::is_assignable_v + constexpr + Expected& + operator=(const Unexpected& e) + { + assign_unex_impl(e.error()); + return *this; + } + + template + requires + std::is_constructible_v && + std::is_assignable_v + constexpr + Expected& + operator=(Unexpected&& e) + { + assign_unex_impl(std::move(e.error())); + return *this; + } + + constexpr + void + emplace() noexcept + { + if (!has_value_) + { + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + } + } + + constexpr + void + swap(Expected& x) + noexcept( + std::is_nothrow_swappable_v && + std::is_nothrow_move_constructible_v) + requires + std::is_swappable_v && + std::is_move_constructible_v + { + if (has_value_) + { + if (!x.has_value_) + { + std::construct_at( + std::addressof(unex_), + std::move(x.unex_)); + std::destroy_at(std::addressof(x.unex_)); + has_value_ = false; + x.has_value_ = true; + } + } + else + { + if (x.has_value_) + { + std::construct_at( + std::addressof(x.unex_), + std::move(unex_)); + std::destroy_at(std::addressof(unex_)); + has_value_ = true; + x.has_value_ = false; + } + else + { + using std::swap; + swap(unex_, x.unex_); + } + } + } + + [[nodiscard]] + constexpr + explicit + operator bool() const noexcept + { + return has_value_; + } + + [[nodiscard]] + constexpr + bool has_value() const noexcept + { + return has_value_; + } + + constexpr + void + operator*() const noexcept { + MRDOCS_ASSERT(has_value_); + } + + constexpr + void + value() const& + { + if (has_value_) [[likely]] + { + return; + } + throw BadExpectedAccess(unex_); + } + + constexpr + void + value() && + { + if (has_value_) [[likely]] + { + return; + } + throw BadExpectedAccess(std::move(unex_)); + } + + constexpr E const& + error() const & noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + constexpr E& + error() & noexcept + { + MRDOCS_ASSERT(!has_value_); + return unex_; + } + + constexpr E const&& + error() const && noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + constexpr E&& + error() && noexcept + { + MRDOCS_ASSERT(!has_value_); + return std::move(unex_); + } + + template + constexpr E + error_or(G&& e) const& + { + static_assert( std::is_copy_constructible_v ); + static_assert( std::is_convertible_v ); + + if (has_value_) + { + return std::forward(e); + } + return unex_; + } + + template + constexpr E + error_or(G&& e) && + { + static_assert( std::is_move_constructible_v ); + static_assert( std::is_convertible_v ); + + if (has_value_) + { + return std::forward(e); + } + return std::move(unex_); + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) & + { + using U = detail::result0; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f)); + } + else + { + return U(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) const & + { + using U = detail::result0; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f)); + } + else + { + return U(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) && + { + using U = detail::result0; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f)); + } + else + { + return U(unexpect, std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + and_then(Fn&& f) const && + { + using U = detail::result0; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return std::invoke(std::forward(f)); + } + else + { + return U(unexpect, std::move(unex_)); + } + } + + template + constexpr + auto + or_else(Fn&& f) & + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(); + } + else + { + return std::invoke(std::forward(f), unex_); + } + } + + template + constexpr + auto + or_else(Fn&& f) const & + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(); + } + else + { + return std::invoke(std::forward(f), unex_); + } + } + + template + constexpr + auto + or_else(Fn&& f) && + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(); + } + else + { + return std::invoke(std::forward(f), std::move(unex_)); + } + } + + template + constexpr + auto + or_else(Fn&& f) const&& + { + using G = detail::then_result; + static_assert(detail::isExpected); + static_assert(std::is_same_v); + + if (has_value()) + { + return G(); + } + else + { + return std::invoke(std::forward(f), std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) & + { + using U = detail::result0_xform; + using Res = Expected; + + if (has_value()) + { + return Res(in_place_inv{}, std::forward(f)); + } + else + { + return Res(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) const & + { + using U = detail::result0_xform; + using Res = Expected; + + if (has_value()) + { + return Res(in_place_inv{}, std::forward(f)); + } + else + { + return Res(unexpect, unex_); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) && + { + using U = detail::result0_xform; + using Res = Expected; + + if (has_value()) + { + return Res(in_place_inv{}, std::forward(f)); + } + else + { + return Res(unexpect, std::move(unex_)); + } + } + + template + requires std::is_constructible_v + constexpr + auto + transform(Fn&& f) const && + { + using U = detail::result0_xform; + using Res = Expected; + + if (has_value()) + { + return Res(in_place_inv{}, std::forward(f)); + } + else + { + return Res(unexpect, std::move(unex_)); + } + } + + template + constexpr + auto + transform_error(Fn&& f) & + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), unex_); + }); + } + } + + template + constexpr + auto + transform_error(Fn&& f) const & + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), unex_); + }); + } + } + + template + constexpr + auto + transform_error(Fn&& f) && + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + } + + template + constexpr + auto + transform_error(Fn&& f) const && + { + using G = detail::result_transform; + using Res = Expected; + + if (has_value()) + { + return Res(); + } + else + { + return Res( + unexpect_inv{}, [&]() { + return std::invoke(std::forward(f), std::move(unex_)); + }); + } + } + + template + requires std::is_void_v + friend + constexpr + bool + operator==(Expected const& x, const Expected& y) + noexcept(noexcept(bool(x.error() == y.error()))) + { + if (x.has_value()) + { + return y.has_value(); + } + else + { + return !y.has_value() && bool(x.error() == y.error()); + } + } + + template + friend + constexpr + bool + operator==(Expected const& x, const Unexpected& e) + noexcept(noexcept(bool(x.error() == e.error()))) + { + return !x.has_value() && bool(x.error() == e.error()); + } + + friend + constexpr + void + swap(Expected& x, Expected& y) + noexcept(noexcept(x.swap(y))) + requires requires { x.swap(y); } + { + x.swap(y); + } + +private: + template + constexpr + void + assign_unex_impl(Vp&& v) + { + if (has_value_) + { + std::construct_at( + std::addressof(unex_), + std::forward(v)); + has_value_ = false; + } + else + { + unex_ = std::forward(v); + } + } + + using in_place_inv = detail::in_place_inv; + using unexpect_inv = detail::unexpect_inv; + + template + explicit constexpr + Expected(in_place_inv, Fn&& fn) + : void_() + , has_value_(true) + { + std::forward(fn)(); + } + + template + explicit constexpr + Expected(unexpect_inv, Fn&& fn) + : unex_(std::forward(fn)()) + , has_value_(false) + { } +}; + +} // clang::mrdocs + +#endif diff --git a/include/mrdocs/Support/Glob.hpp b/include/mrdocs/Support/Glob.hpp index a65d127fd7..dc222f33ef 100644 --- a/include/mrdocs/Support/Glob.hpp +++ b/include/mrdocs/Support/Glob.hpp @@ -11,7 +11,7 @@ #ifndef MRDOCS_API_SUPPORT_GLOBPATTERN_HPP #define MRDOCS_API_SUPPORT_GLOBPATTERN_HPP -#include +#include #include #include #include diff --git a/include/mrdocs/Support/Path.hpp b/include/mrdocs/Support/Path.hpp index a4cf2447c7..6bcbeea8d6 100644 --- a/include/mrdocs/Support/Path.hpp +++ b/include/mrdocs/Support/Path.hpp @@ -12,7 +12,7 @@ #define MRDOCS_API_SUPPORT_PATH_HPP #include -#include +#include #include #include diff --git a/include/mrdocs/Support/Report.hpp b/include/mrdocs/Support/Report.hpp new file mode 100644 index 0000000000..db893db16c --- /dev/null +++ b/include/mrdocs/Support/Report.hpp @@ -0,0 +1,284 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_SUPPORT_REPORT_HPP +#define MRDOCS_API_SUPPORT_REPORT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clang::mrdocs::report { + +/** Severity levels attached to reported messags. +*/ +enum class Level +{ + trace = 0, + debug, + info, + warn, + error, + fatal +}; + +/** Provides statistics on the number of reported messages. +*/ +struct Results +{ + std::size_t traceCount; + std::size_t debugCount; + std::size_t infoCount; + std::size_t warnCount; + std::size_t errorCount; + std::size_t fatalCount; +}; + +/** Holds current statistics on reported messages. +*/ +extern +MRDOCS_DECL +Results +results; + +/** Set the minimum threshold level for reporting. + + Messages below this level will not be printed. + A value of 5 will suppress all messages. Note + that messages will still be counted towards + result totals even if they are not displayed. +*/ +MRDOCS_DECL +void +setMinimumLevel(Level level) noexcept; + +MRDOCS_DECL +Level +getMinimumLevel() noexcept; + +/** If true, source location information will be + printed with warnings, errors, and fatal messages. +*/ +MRDOCS_DECL +void +setSourceLocationWarnings(bool b) noexcept; + +/** Report a message to the console. + + @param text The message to print. A + trailing newline will be added to the + message automatically. +*/ +MRDOCS_DECL +void +print( + std::string const& text); + +/** Report a message to the console. + + @param level 0 to 4 The severity of the + report. 0 is debug and 4 is fatal. + + @param text The message to print. A + trailing newline will be added to the + message automatically. + + @param loc The source location of the report. + If this value is null, no location is printed. +*/ +MRDOCS_DECL +void +print( + Level level, + std::string const& text, + source_location const* loc = nullptr, + Error const* e = nullptr); + +/** Parameter type that adds a source location to a value. +*/ +template +struct Located +{ + T value; + source_location where; + + template + requires std::is_constructible_v + Located( + Arg&& arg, + source_location const& loc = + source_location::current()) + : value(std::forward(arg)) + , where(loc) + { + } +}; + +namespace detail { +template +requires (!std::same_as, Error>) +void +log_impl( + Level level, + Located fs, + Arg0&& arg0, + Args&&... args) +{ + std::string str = fmt::vformat( + fs.value, + fmt::make_format_args(arg0, args...)); + return print( + level, + str, + &fs.where); +} + +template +void +log_impl( + Level level, + Located fs, + Error const& e, + Args&&... args) +{ + // When the message is an error, we send split + // the information relevant to the user from + // the information relevant for bug tracking + // so that users can understand the message. + std::string str = fmt::vformat( + fs.value, + fmt::make_format_args(e.reason(), args...)); + return print( + level, + str, + &fs.where, + &e); +} + +inline +void +log_impl( + Level level, + Located fs) +{ + std::string str = fmt::vformat( + fs.value, fmt::make_format_args()); + return print( + level, + str, + &fs.where); +} +} + +/** Format a message to the console. + + @param level 0 to 4 The severity of the + report. 0 is debug and 4 is fatal. + + @param fs The format string. + + @param args... Optional additional arguments + used to format a message to print. A trailing + newline will be added to the message + automatically. +*/ +template +void +log( + Level level, + Located fs, + Args&&... args) +{ + return detail::log_impl( + level, + fs, + std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +trace( + Located format, + Args&&... args) +{ + return log(Level::trace, format, std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +debug( + Located format, + Args&&... args) +{ + return log(Level::debug, format, std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +info( + Located format, + Args&&... args) +{ + return log(Level::info, format, std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +warn( + Located format, + Args&&... args) +{ + return log(Level::warn, format, std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +error( + Located format, + Args&&... args) +{ + return log(Level::error, format, std::forward(args)...); +} + +/** Report a message to the console. +*/ +template +void +fatal( + Located format, + Args&&... args) +{ + return log(Level::fatal, format, std::forward(args)...); +} + +} // clang::mrdocs + +#endif diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 0c57b38062..3f3d995b4b 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -12,6 +12,7 @@ #include "lib/AST/ClangHelpers.hpp" #include +#include #include #include #include diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp index 4297132b82..637b4fb4aa 100644 --- a/src/lib/AST/ParseJavadoc.cpp +++ b/src/lib/AST/ParseJavadoc.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #endif #include #include +#include #include #ifdef NDEBUG @@ -166,6 +168,9 @@ class JavadocVisitor doc::Block* block_ = nullptr; doc::Text* last_child_ = nullptr; std::size_t htmlTagNesting_ = 0; + + // Used to visit children of a comment + Comment const* parent_ = nullptr; Comment::child_iterator it_{}; Comment::child_iterator end_{}; @@ -313,6 +318,28 @@ class JavadocVisitor void visitInlineCommandComment(InlineCommandComment const* C); + /** Fix a reference string + + The clang parser considers all chars up to the first + whitespace as part of the reference. + + In some cases, this causes the reference to be incomplete, + especially when handling a function signature. In this + case, we have to extract the text from the next comments + to complete the reference. + + In other cases, the reference may contain characters that + are not valid in identifiers. For instance, when the + identifier is followed by punctuation. In this case, we + have to truncate the reference at the last valid identifier + character. + + @param ref The reference string to fix + @return Any leftover text that removed from the reference + */ + std::string + fixReference(std::string& ref); + /** Visit a single paragraph that contains inline content. */ void @@ -559,6 +586,7 @@ visitChildren( MRDOCS_COMMENT_TRACE(C, ctx_); ScopeExitRestore s1(it_, C->child_begin()); ScopeExitRestore s2(end_, C->child_end()); + ScopeExitRestore s3(parent_, C); while(it_ != end_) { MRDOCS_COMMENT_TRACE(*it_, ctx_); @@ -915,94 +943,6 @@ convertDirection(ParamCommandPassDirection kind) } } -/** Parse first chars of string that represent an identifier - */ -std::string_view -parseIdentifier(std::string_view str) -{ - static constexpr auto idChars = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "_"; - static constexpr auto operatorChars = - "~!%^&*()-+=|[]{};:,.<>?/"; - if (str.empty()) - { - return {}; - } - - std::size_t p = str.find_first_not_of(idChars); - if (p == std::string_view::npos) - { - return str; - } - - if (str.substr(0, p) == "operator") - { - p = str.find_first_not_of(operatorChars, p); - if (p == std::string_view::npos) - { - return str; - } - } - - return str.substr(0, p); -} - -/** Parse first chars of string that represent an identifier - */ -std::string_view -parseQualifiedIdentifier(std::string_view str) -{ - auto str0 = str; - std::size_t off = 0; - if (str.starts_with("::")) - { - off += 2; - str.remove_prefix(2); - } - - bool atIdentifier = true; - while (!str.empty()) - { - if (atIdentifier) - { - auto idStr = parseIdentifier(str); - if (!idStr.empty()) - { - off += idStr.size(); - str = str.substr(idStr.size()); - atIdentifier = false; - } - else - { - break; - } - } - else - { - // At delimiter - if (str.starts_with("::")) - { - off += 2; - str = str.substr(2); - atIdentifier = true; - } - else - { - break; - } - } - } - std::string_view result = str0.substr(0, off); - if (result.ends_with("::")) - { - result = result.substr(0, result.size() - 2); - } - return result; -} - void JavadocVisitor:: visitInlineCommandComment( @@ -1032,8 +972,7 @@ visitInlineCommandComment( case CommandTraits::KCI_e: case CommandTraits::KCI_em: { - if(! goodArgCount(1, *C)) - return; + MRDOCS_CHECK_OR(goodArgCount(1, *C)); auto style = doc::Style::italic; emplaceText( C->hasTrailingNewline(), @@ -1047,78 +986,37 @@ visitInlineCommandComment( case CommandTraits::KCI_copydetails: case CommandTraits::KCI_copydoc: { - if (!goodArgCount(1, *C)) - { - return; - } - - // the referenced symbol will be resolved during - // the finalization step once all symbols are extracted - std::string const &s = C->getArgText(0).str(); - bool const copyingFunctionDoc = s.find('(') != std::string::npos; - std::string ref = s; - if (copyingFunctionDoc) - { - // Clang parses the copydoc command breaking - // before the complete overload information. For instance, - // `@copydoc operator()(unsigned char) const` will create - // a node with the text `operator()(unsigned` and another - // with `char) const`. We need to merge these nodes. - std::size_t open = std::ranges::count(s, '('); - std::size_t close = std::ranges::count(s, ')'); - while (open != close) - { - ++it_; - if (it_ == end_) - { - break; - } - Comment const* c = *it_; - if (c->getCommentKind() == CommentKind::TextComment) - { - ref += static_cast(c)->getText(); - } - else - { - break; - } - open = std::ranges::count(ref, '('); - close = std::ranges::count(ref, ')'); - } - } + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string originalRef = ref; + std::string leftOver = fixReference(ref); + bool const hasExtra = !leftOver.empty(); emplaceText( - C->hasTrailingNewline(), + C->hasTrailingNewline() && !hasExtra, ref, convertCopydoc(ID)); + if (hasExtra) + { + emplaceText( + C->hasTrailingNewline(), + leftOver); + } return; } case CommandTraits::KCI_ref: { - if(! goodArgCount(1, *C)) - return; - // The parsed reference often includes characters - // that are not valid in identifiers, so we need to - // clean it up. - // Find the first character that is not a valid C++ - // identifier character, and truncate the string there. - // This potentially creates two text nodes. - auto const s = C->getArgText(0).str(); - std::string_view ref = parseQualifiedIdentifier(s); - bool const hasExtraText = ref.size() != s.size(); - if (!ref.empty()) - { - // the referenced symbol will be resolved during - // the finalization step once all symbols are extracted - emplaceText( - C->hasTrailingNewline() && !hasExtraText, - std::string(ref)); - } - // Emplace the rest of the string as doc::Text - if(hasExtraText) + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string leftOver = fixReference(ref); + bool const hasExtra = !leftOver.empty(); + emplaceText( + C->hasTrailingNewline() && !hasExtra, + ref); + if (hasExtra) { emplaceText( C->hasTrailingNewline(), - s.substr(ref.size())); + leftOver); } return; } @@ -1126,31 +1024,18 @@ visitInlineCommandComment( case CommandTraits::KCI_related: case CommandTraits::KCI_relates: { - if(! goodArgCount(1, *C)) - return; - // The parsed reference often includes characters - // that are not valid in identifiers, so we need to - // clean it up. - // Find the first character that is not a valid C++ - // identifier character, and truncate the string there. - // This potentially creates two text nodes. - auto const s = C->getArgText(0).str(); - std::string_view ref = parseQualifiedIdentifier(s); - bool const hasExtraText = ref.size() != s.size(); - if (!ref.empty()) - { - // the referenced symbol will be resolved during - // the finalization step once all symbols are extracted - emplaceText( - C->hasTrailingNewline() && !hasExtraText, - std::string(ref)); - } - // Emplace the rest of the string as doc::Text - if(hasExtraText) + MRDOCS_CHECK_OR(goodArgCount(1, *C)); + std::string ref = C->getArgText(0).str(); + std::string leftOver = fixReference(ref); + bool const hasExtra = !leftOver.empty(); + emplaceText( + C->hasTrailingNewline() && !hasExtra, + ref); + if (hasExtra) { emplaceText( - C->hasTrailingNewline(), - s.substr(ref.size())); + C->hasTrailingNewline(), + leftOver); } return; } @@ -1184,6 +1069,156 @@ visitInlineCommandComment( } } +std::string +JavadocVisitor:: +fixReference(std::string& ref) +{ + // If the ref is only "operator", the next text comment + // might contain a simple operator name/type, or a + // full operator overload. + // In this case, we need to include the next text comments + // until we find this operator identifier/type or until + // we find an unbalanced '('. + // Simply including the next text comment is enough + // for the next step. + std::string_view trimmed = trim(ref); + bool const isNoSuffixOperator = + trimmed == "operator" || + trimmed.ends_with("::operator"); + if (isNoSuffixOperator) + { + ++it_; + if (it_ == end_) + { + return ref; + } + Comment const* c = *it_; + if (c->getCommentKind() == CommentKind::TextComment) + { + ref += static_cast(c)->getText(); + } + else + { + return ref; + } + } + static constexpr std::string_view idChars = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_:"; + bool const isNoFunctionOperator = + isNoSuffixOperator || + [trimmed]{ + if (contains_n(trimmed, '(', 1)) + { + return false; + } + std::size_t pos = trimmed.rfind("::"); + std::string_view last = trimmed; + if (pos != std::string::npos) { + last = trimmed.substr(pos + 2); + } + if (!last.starts_with("operator")) + { + return false; + } + last.remove_prefix(8); + if (last.empty()) + { + return true; + } + return !contains(idChars, last.front()); + }(); + + // Clang parses the copydoc command breaking + // before the complete overload information. For instance, + // `@copydoc operator()(unsigned char) const` will create + // a node with the text `operator()(unsigned` and another + // with `char) const`. We need to merge these nodes. + // If the ref contains an unbalanced '(', then it's + // a function, and we need to merge the next text comments + // until we find a balanced ')'. + bool const isFunction = contains(ref, '('); + while (std::ranges::count(ref, '(') != std::ranges::count(ref, ')')) + { + ++it_; + if (it_ == end_) + { + break; + } + Comment const* c = *it_; + if (c->getCommentKind() == CommentKind::TextComment) + { + ref += static_cast(c)->getText(); + } + else + { + break; + } + } + + // Clang refs can also contain invalid characters + // at the end, especially punctuation. We need to + // truncate the ref at the last valid identifier + // character. + // The last identifier character depends on the type + // of ref. + // - If it's an operator but not a function, then + // we also consider operator chars as valid. + // - If it's a function, then we also consider ')' + // as valid. + // - In all cases, we consider the identifier chars + // as valid. + static constexpr std::string_view operatorChars = + "~!%^&*()-+=|[]{};:,.<>?/"; + static constexpr std::string_view parenChars = + "()"; + std::string leftover; + bool const isRegularIdentifier = !isFunction && !isNoFunctionOperator; + if (isRegularIdentifier) + { + auto const lastIdChar = ref.find_last_of(idChars); + auto const firstLeftoverChar = lastIdChar + 1; + if (firstLeftoverChar < ref.size()) + { + leftover = std::string_view(ref).substr(lastIdChar + 1); + ref = ref.substr(0, lastIdChar + 1); + } + } + else if (isFunction) + { + auto reservedCharsets = {idChars, parenChars}; + auto reservedChars = std::views::join(reservedCharsets); + auto const lastIdOrParen = find_last_of(ref, reservedChars); + auto const firstLeftoverChar = + lastIdOrParen == ref.end() ? + ref.end() : + std::next(lastIdOrParen); + if (firstLeftoverChar != ref.end()) + { + leftover = std::string_view(firstLeftoverChar, ref.end()); + ref = ref.substr(0, std::distance(ref.begin(), firstLeftoverChar)); + } + } + else /* if (isNoFunctionOperator) */ + { + auto reservedCharsets = {idChars, operatorChars}; + auto reservedChars = std::views::join(reservedCharsets); + auto const lastIdOrOperator = find_last_of(ref, reservedChars); + auto const firstLeftoverChar = + lastIdOrOperator == ref.end() ? + ref.end() : + std::next(lastIdOrOperator); + if (firstLeftoverChar != ref.end()) + { + leftover = std::string_view(firstLeftoverChar, ref.end()); + ref = ref.substr(0, std::distance(ref.begin(), firstLeftoverChar)); + } + } + return leftover; +} + //------------------------------------------------ // // block Content diff --git a/src/lib/AST/ParseRef.cpp b/src/lib/AST/ParseRef.cpp index b1dffb8a71..a9dc91d07b 100644 --- a/src/lib/AST/ParseRef.cpp +++ b/src/lib/AST/ParseRef.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include namespace clang::mrdocs { diff --git a/src/lib/Gen/hbs/Builder.cpp b/src/lib/Gen/hbs/Builder.cpp index 7f041d7735..0e1c11a8bb 100644 --- a/src/lib/Gen/hbs/Builder.cpp +++ b/src/lib/Gen/hbs/Builder.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.cpp b/src/lib/Gen/hbs/HandlebarsGenerator.cpp index 22c841947e..e74f2cf38a 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.cpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/src/lib/Gen/hbs/MultiPageVisitor.cpp b/src/lib/Gen/hbs/MultiPageVisitor.cpp index 519a0f2b80..59034dab3c 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.cpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.cpp @@ -13,6 +13,7 @@ #include "VisitorHelpers.hpp" #include #include +#include namespace clang::mrdocs::hbs { diff --git a/src/lib/Lib/CMakeExecution.hpp b/src/lib/Lib/CMakeExecution.hpp index c96334ba39..8182a2bc1c 100644 --- a/src/lib/Lib/CMakeExecution.hpp +++ b/src/lib/Lib/CMakeExecution.hpp @@ -14,7 +14,7 @@ #include #include -#include +#include namespace clang { namespace mrdocs { diff --git a/src/lib/Lib/Config.cpp b/src/lib/Lib/Config.cpp index fd7f230f2f..48f24ce4cb 100644 --- a/src/lib/Lib/Config.cpp +++ b/src/lib/Lib/Config.cpp @@ -10,7 +10,7 @@ // #include "mrdocs/Config.hpp" -#include +#include #include #include #include diff --git a/src/lib/Lib/ConfigImpl.cpp b/src/lib/Lib/ConfigImpl.cpp index 9672096ff0..8261b69d60 100644 --- a/src/lib/Lib/ConfigImpl.cpp +++ b/src/lib/Lib/ConfigImpl.cpp @@ -12,7 +12,7 @@ #include "lib/Lib/ConfigImpl.hpp" #include "lib/Support/Debug.hpp" -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include "lib/Support/Path.hpp" #include "lib/Support/Yaml.hpp" #include "lib/Support/Glob.hpp" diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index c5c7dda4a5..95f95f9251 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -18,7 +18,7 @@ #include "lib/Metadata/Finalizers/SortMembersFinalizer.hpp" #include "lib/Metadata/Finalizers/JavadocFinalizer.hpp" #include "lib/Metadata/Finalizers/NamespacesFinalizer.hpp" -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include "lib/Support/Chrono.hpp" #include #include @@ -222,7 +222,7 @@ lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) { return Unexpected(formatError("Failed to parse '{}'\n {}", name, expRef.error().reason())); } - ParsedRef const& ref = *expRef; + ParsedRef const& ref = *std::move(expRef); Info const* res = lookupImpl(self, contextId, ref, name, false); if (!res) { @@ -375,12 +375,19 @@ lookupImpl( auto matchRes = MatchLevel::None; // Name match - if constexpr (requires { { M.OverloadedOperator } -> std::same_as; }) + if constexpr ( + std::same_as || + std::same_as) { - if (component.Operator != OperatorKind::None) + if (component.isOperator()) { MRDOCS_CHECK_OR(M.OverloadedOperator == component.Operator, matchRes); } + else if (component.isConversion()) + { + MRDOCS_CHECK_OR(M.Class == FunctionClass::Conversion, matchRes); + MRDOCS_CHECK_OR(component.ConversionType == M.ReturnType, matchRes); + } else { MRDOCS_CHECK_OR(member.Name == component.Name, matchRes); diff --git a/src/lib/Lib/Diagnostics.hpp b/src/lib/Lib/Diagnostics.hpp index 50d099c688..18e8434c5d 100644 --- a/src/lib/Lib/Diagnostics.hpp +++ b/src/lib/Lib/Diagnostics.hpp @@ -11,7 +11,7 @@ #ifndef MRDOCS_LIB_TOOL_DIAGNOSTICS_HPP #define MRDOCS_LIB_TOOL_DIAGNOSTICS_HPP -#include +#include #include #include #include diff --git a/src/lib/Lib/MrDocsCompilationDatabase.cpp b/src/lib/Lib/MrDocsCompilationDatabase.cpp index 4d1a801403..dbf64caf46 100644 --- a/src/lib/Lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/Lib/MrDocsCompilationDatabase.cpp @@ -13,6 +13,7 @@ #include "lib/Support/Path.hpp" #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/MrDocsCompilationDatabase.hpp" +#include #include #include #include diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp index 778f3d51f8..cc31cfc439 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -9,6 +9,7 @@ // #include "BaseMembersFinalizer.hpp" +#include namespace clang::mrdocs { diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp index 2ce05a3585..aca181b59e 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.cpp @@ -1146,9 +1146,10 @@ emitWarnings() "{}:{}\n", loc.FullPath, loc.LineNumber); + int i = 1; for (auto const& msg : msgs) { - locWarning += fmt::format(" {}\n", msg); + locWarning += fmt::format(" {}) {}\n", i++, msg); } report::log(level, locWarning); } diff --git a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp index 09683c8349..9356f52ba0 100644 --- a/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/JavadocFinalizer.hpp @@ -14,7 +14,8 @@ #include "lib/Lib/CorpusImpl.hpp" #include "lib/Lib/Info.hpp" -#include "mrdocs/Support/ScopeExit.hpp" +#include +#include #include #include @@ -212,7 +213,7 @@ class JavadocFinalizer void warn( Location const& loc, - Located const format, + report::Located const format, Args&&... args) { MRDOCS_CHECK_OR(corpus_.config->warnings); @@ -223,7 +224,7 @@ class JavadocFinalizer template void warn( - Located const format, + report::Located const format, Args&&... args) { MRDOCS_CHECK_OR(corpus_.config->warnings); diff --git a/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp b/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp index 6ff0fee45f..be862fe226 100644 --- a/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/NamespacesFinalizer.cpp @@ -9,6 +9,7 @@ // #include "NamespacesFinalizer.hpp" +#include namespace clang::mrdocs { diff --git a/src/lib/Metadata/Info/Overloads.cpp b/src/lib/Metadata/Info/Overloads.cpp index 06d787efa0..b91078ff01 100644 --- a/src/lib/Metadata/Info/Overloads.cpp +++ b/src/lib/Metadata/Info/Overloads.cpp @@ -51,10 +51,15 @@ addMember(OverloadsInfo& I, FunctionInfo const& Member) I.Extraction = Member.Extraction; I.Class = Member.Class; I.OverloadedOperator = Member.OverloadedOperator; + I.ReturnType = Member.ReturnType; } else { I.Extraction = leastSpecific(I.Extraction, Member.Extraction); + if (I.ReturnType != Member.ReturnType) + { + I.ReturnType = {}; + } } merge(dynamic_cast(I), dynamic_cast(Member)); I.Members.push_back(Member.id); diff --git a/src/lib/Metadata/Name.cpp b/src/lib/Metadata/Name.cpp index 87f5cfa4c4..e9e0e97ee1 100644 --- a/src/lib/Metadata/Name.cpp +++ b/src/lib/Metadata/Name.cpp @@ -41,7 +41,12 @@ writeTo( std::strong_ordering NameInfo:: -operator<=>(NameInfo const& other) const = default; +operator<=>(NameInfo const& other) const +{ + return + std::tie(Kind, Name, Prefix) <=> + std::tie(other.Kind, other.Name, other.Prefix); +} std::strong_ordering operator<=>(Polymorphic const& lhs, Polymorphic const& rhs) diff --git a/src/lib/Support/GeneratorsImpl.cpp b/src/lib/Support/GeneratorsImpl.cpp index e1ac134e60..b0cfdbbfb9 100644 --- a/src/lib/Support/GeneratorsImpl.cpp +++ b/src/lib/Support/GeneratorsImpl.cpp @@ -9,6 +9,7 @@ // #include "GeneratorsImpl.hpp" +#include #include namespace clang { diff --git a/src/lib/Support/Glob.cpp b/src/lib/Support/Glob.cpp index e67e49121d..6b445a2e70 100644 --- a/src/lib/Support/Glob.cpp +++ b/src/lib/Support/Glob.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace clang::mrdocs { diff --git a/src/lib/Support/JavaScript.cpp b/src/lib/Support/JavaScript.cpp index baa80c0b20..abbf3165d2 100644 --- a/src/lib/Support/JavaScript.cpp +++ b/src/lib/Support/JavaScript.cpp @@ -9,7 +9,7 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include #include #include diff --git a/src/lib/Support/Lua.cpp b/src/lib/Support/Lua.cpp index 8598609f01..eee6bde0ca 100644 --- a/src/lib/Support/Lua.cpp +++ b/src/lib/Support/Lua.cpp @@ -9,7 +9,7 @@ // #include -#include +#include #include #include "../../../third-party/lua/src/lua.hpp" #include diff --git a/src/lib/Support/Path.cpp b/src/lib/Support/Path.cpp index 5c240f6452..617bb2e35e 100644 --- a/src/lib/Support/Path.cpp +++ b/src/lib/Support/Path.cpp @@ -10,6 +10,7 @@ // #include "Path.hpp" +#include #include #include #include diff --git a/src/lib/Support/Error.cpp b/src/lib/Support/Report.cpp similarity index 97% rename from src/lib/Support/Error.cpp rename to src/lib/Support/Report.cpp index 17e9c91be4..20642e4d5c 100644 --- a/src/lib/Support/Error.cpp +++ b/src/lib/Support/Report.cpp @@ -8,7 +8,7 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include #include #include @@ -170,21 +170,6 @@ static bool sourceLocationWarnings_ = true; constinit Results results{}; -static -void -print_impl( - std::string const& s) -{ - llvm::errs() << s << '\n'; -#ifdef _MSC_VER - if(::IsDebuggerPresent() != 0) - { - ::OutputDebugStringA(s.c_str()); - ::OutputDebugStringA("\n"); - } -#endif -} - void setMinimumLevel(Level const level) noexcept { @@ -205,9 +190,16 @@ setSourceLocationWarnings(bool b) noexcept void print( - std::string const& text) + std::string const& s) { - print_impl(text); + llvm::outs() << s << '\n'; +#ifdef _MSC_VER + if(::IsDebuggerPresent() != 0) + { + ::OutputDebugStringA(s.c_str()); + ::OutputDebugStringA("\n"); + } +#endif } void @@ -253,7 +245,7 @@ getLevelColor(Level level) case Level::info: return llvm::raw_ostream::Colors::WHITE; case Level::warn: - return llvm::raw_ostream::Colors::MAGENTA; + return llvm::raw_ostream::Colors::BRIGHT_YELLOW; case Level::error: return llvm::raw_ostream::Colors::RED; case Level::fatal: diff --git a/src/lib/Support/Error.hpp b/src/lib/Support/Report.hpp similarity index 97% rename from src/lib/Support/Error.hpp rename to src/lib/Support/Report.hpp index 8975582363..837972db3d 100644 --- a/src/lib/Support/Error.hpp +++ b/src/lib/Support/Report.hpp @@ -11,10 +11,9 @@ #ifndef MRDOCS_LIB_SUPPORT_ERROR_HPP #define MRDOCS_LIB_SUPPORT_ERROR_HPP -#include +#include #include #include -#include #include #include #include diff --git a/src/lib/Support/Yaml.cpp b/src/lib/Support/Yaml.cpp index 315db6df1f..34aa6aaf7d 100644 --- a/src/lib/Support/Yaml.cpp +++ b/src/lib/Support/Yaml.cpp @@ -9,7 +9,7 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include "lib/Support/Yaml.hpp" namespace clang { diff --git a/src/test/TestMain.cpp b/src/test/TestMain.cpp index 34bc8d1e00..4b7e52f66a 100644 --- a/src/test/TestMain.cpp +++ b/src/test/TestMain.cpp @@ -11,7 +11,7 @@ #include "TestArgs.hpp" #include "TestRunner.hpp" #include "lib/Support/Debug.hpp" -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include #include #include @@ -70,10 +70,14 @@ void DoTestAction(char const** argv) os << report::numberOf(results.numberOfDirs.load(), "directory", "directories") << " visited"; - if(auto n = results.expectedDocsMatching.load()) + if (auto n = results.expectedDocsMatching.load()) + { os << ", " << report::numberOf(n, "file", "files") << " matched"; - if(auto n = results.expectedDocsWritten.load()) + } + if (auto n = results.expectedDocsWritten.load()) + { os << ", " << report::numberOf(n, "file", "files") << " written"; + } os << ".\n"; report::print(os.str()); } diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index 3f8485d09e..189f8d2c84 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -10,7 +10,7 @@ #include "TestRunner.hpp" #include "TestArgs.hpp" -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include "lib/Support/Path.hpp" #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/CorpusImpl.hpp" diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index f540e3dac6..3f3ff42a14 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -19,7 +19,7 @@ #include "lib/Support/Path.hpp" #include "llvm/Support/Program.h" #include -#include +#include #include #include #include diff --git a/src/tool/ToolMain.cpp b/src/tool/ToolMain.cpp index b923174065..4bde149076 100644 --- a/src/tool/ToolMain.cpp +++ b/src/tool/ToolMain.cpp @@ -11,7 +11,7 @@ #include "ToolArgs.hpp" #include "lib/Support/Debug.hpp" -#include "lib/Support/Error.hpp" +#include "lib/Support/Report.hpp" #include #include #include diff --git a/test-files/golden-tests/javadoc/copydoc/conversion.adoc b/test-files/golden-tests/javadoc/copydoc/conversion.adoc new file mode 100644 index 0000000000..84cf377132 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/conversion.adoc @@ -0,0 +1,249 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +=== Types + +[cols=1] +|=== +| Name + +| <> + +| <> + +| <> + +|=== + +[#A] +== A + + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct A; +---- + +=== Member Functions + +[cols=2] +|=== +| Name +| Description + +| <> +| Convert a string to A + +| <> +| Convert A to a string + +| <> +| Convert A to a string + +|=== + + + +[#A-operator_assign-0d] +== <>::operator= + + +Convert a string to A + +=== Synopses + + +Declared in `<conversion.cpp>` + +Convert a string to A + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +<>& +<>(<> const& other); +---- + +[.small]#<># + +Convert a string to A + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +<>& +<>(<> const& other); +---- + +[.small]#<># + +=== Return Value + + +A representation of the string + +=== Parameters + + +|=== +| Name | Description + +| *other* +| The string to convert + +|=== + +[#A-operator_assign-08] +== <>::operator= + + +Convert a string to A + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +<>& +operator=(<> const& other); +---- + +=== Return Value + + +A representation of the string + +=== Parameters + + +|=== +| Name | Description + +| *other* +| The string to convert + +|=== + +[#A-operator_assign-00] +== <>::operator= + + +Convert a string to A + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +<>& +operator=(<> const& other); +---- + +=== Return Value + + +A representation of the string + +=== Parameters + + +|=== +| Name | Description + +| *other* +| The string to convert + +|=== + +[#A-2conversion-02] +== <>::operator <> + + +Convert A to a string + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +operator <>() const; +---- + +=== Return Value + + +A string representation of A + +[#A-2conversion-00] +== <>::operator <> + + +Convert A to a string + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +operator <>() const; +---- + +=== Return Value + + +A string representation of A + +[#string_type] +== string_type + + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class string_type; +---- + + + + +[#string_view_type] +== string_view_type + + +=== Synopsis + + +Declared in `<conversion.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class string_view_type; +---- + + + + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/javadoc/copydoc/conversion.cpp b/test-files/golden-tests/javadoc/copydoc/conversion.cpp new file mode 100644 index 0000000000..655a014b57 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/conversion.cpp @@ -0,0 +1,26 @@ +class string_type; +class string_view_type; + +struct A { + /** Convert A to a string + + @return A string representation of A + */ + operator string_type() const; + + /** @copydoc operator string_type() + */ + operator string_view_type() const; + + /** Convert a string to A + + @param other The string to convert + @return A representation of the string + */ + A& + operator=(string_type const& other); + + /// @copydoc operator=(string_type const&) + A& + operator=(string_view_type const& other); +}; \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/copydoc/conversion.html b/test-files/golden-tests/javadoc/copydoc/conversion.html new file mode 100644 index 0000000000..7f79dc5f32 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/conversion.html @@ -0,0 +1,311 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + + + + + + +
Name
A
string_type
string_view_type
+
+
+
+

A

+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+struct A;
+
+
+
+

Member Functions

+ + + + + + + + + + + + + + + + +
NameDescription
operator= Convert a string to A +
operator string_type Convert A to a string +
operator string_view_type Convert A to a string +
+ + +
+
+
+

A::operator=

+
+Convert a string to A + + +
+
+
+

Synopses

+
+Declared in <conversion.cpp>
+

+ Convert a string to A + +

+ + +
+
+A&
+operator=(string_type const& other);
+
+
» more... + +

+ Convert a string to A + +

+ + +
+
+A&
+operator=(string_view_type const& other);
+
+
» more... + + +
+
+

Return Value

+

A representation of the string

+ +
+
+

Parameters

+ + + + + + + + + + + + + +
NameDescription
other

The string to convert

+
+
+
+
+
+

A::operator=

+
+Convert a string to A + + +
+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+A&
+operator=(string_type const& other);
+
+
+
+
+

Return Value

+

A representation of the string

+ +
+
+

Parameters

+ + + + + + + + + + + + + +
NameDescription
other

The string to convert

+
+
+
+
+
+

A::operator=

+
+Convert a string to A + + +
+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+A&
+operator=(string_view_type const& other);
+
+
+
+
+

Return Value

+

A representation of the string

+ +
+
+

Parameters

+ + + + + + + + + + + + + +
NameDescription
other

The string to convert

+
+
+
+
+
+

A::operator string_type

+
+Convert A to a string + + +
+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+operator string_type() const;
+
+
+
+
+

Return Value

+

A string representation of A

+ +
+
+
+
+

A::operator string_view_type

+
+Convert A to a string + + +
+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+operator string_view_type() const;
+
+
+
+
+

Return Value

+

A string representation of A

+ +
+
+
+
+

string_type

+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+class string_type;
+
+
+
+ + +
+
+
+

string_view_type

+
+
+

Synopsis

+
+Declared in <conversion.cpp>
+
+
+class string_view_type;
+
+
+
+ + +
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/copydoc/conversion.xml b/test-files/golden-tests/javadoc/copydoc/conversion.xml new file mode 100644 index 0000000000..765b31c724 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/conversion.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + Convert a string to A + + + A representation of the string + + + The string to convert + + + + + + + + + + + + + + + + + + + Convert a string to A + + + A representation of the string + + + The string to convert + + + + + + + + + + + + Convert A to a string + + + A string representation of A + + + + + + + + + + + + Convert A to a string + + + A string representation of A + + + + + + + + + + + + diff --git a/util/generate-config-info.py b/util/generate-config-info.py index db1e15fb98..2eba3c8590 100644 --- a/util/generate-config-info.py +++ b/util/generate-config-info.py @@ -928,6 +928,7 @@ def generate_public_toolargs_cpp(config): '"PublicToolArgs.hpp"', # '"Addons.hpp"', '', + '', '', '', '',