diff --git a/include/fmt/std.h b/include/fmt/std.h index 207c9d35..18b12abc 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -637,7 +637,12 @@ struct formatter< template auto format(const std::exception& ex, Context& ctx) const -> decltype(ctx.out()) { - auto out = ctx.out(); + return write(ctx.out(), ex); + } + + private: + template + auto write(OutputIt out, const std::exception& ex) const -> OutputIt { #if FMT_USE_RTTI if (with_typename_) { out = detail::write_demangled_name(out, typeid(ex)); @@ -645,7 +650,24 @@ struct formatter< *out++ = ' '; } #endif - return detail::write_bytes(out, string_view(ex.what())); + out = detail::write_bytes(out, string_view(ex.what())); +#if FMT_USE_RTTI + // If the exception carries a nested exception (e.g. via + // std::throw_with_nested), format the whole chain. + if (auto* nested = dynamic_cast(&ex)) { + if (auto ep = nested->nested_ptr()) { + out = detail::write(out, string_view(": ")); + try { + std::rethrow_exception(ep); + } catch (const std::exception& nested_ex) { + out = write(out, nested_ex); + } catch (...) { + out = detail::write(out, string_view("unknown exception")); + } + } + } +#endif + return out; } }; diff --git a/test/std-test.cc b/test/std-test.cc index d64ef956..8fb92f79 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -402,6 +402,34 @@ TEST(std_test, exception) { StartsWith("std::filesystem::filesystem_error: ")); } #endif + +#if FMT_USE_RTTI + // Nested exceptions (e.g. from std::throw_with_nested) are unwound. + try { + try { + throw std::runtime_error("inner"); + } catch (...) { + std::throw_with_nested(std::runtime_error("outer")); + } + } catch (const std::exception& ex) { + EXPECT_EQ("outer: inner", fmt::format("{}", ex)); + } + + // Multiple levels of nesting. + try { + try { + try { + throw std::runtime_error("level 3"); + } catch (...) { + std::throw_with_nested(std::runtime_error("level 2")); + } + } catch (...) { + std::throw_with_nested(std::runtime_error("level 1")); + } + } catch (const std::exception& ex) { + EXPECT_EQ("level 1: level 2: level 3", fmt::format("{}", ex)); + } +#endif } TEST(std_test, exception_ptr) { @@ -421,6 +449,19 @@ TEST(std_test, exception_ptr) { #if FMT_USE_RTTI EXPECT_EQ(fmt::format("{:t}", p2), "my_ns1::my_ns2::my_exception: My Exception"); + + // Nested exceptions are unwound through an exception_ptr too. + std::exception_ptr p3; + try { + try { + throw std::runtime_error("inner"); + } catch (...) { + std::throw_with_nested(std::runtime_error("outer")); + } + } catch (...) { + p3 = std::current_exception(); + } + EXPECT_EQ(fmt::format("{}", p3), "outer: inner"); #endif }