From 4ad90578f73fe7859d7ae1ee76b1098aa2f580b3 Mon Sep 17 00:00:00 2001 From: timsong-cpp Date: Fri, 18 Mar 2022 00:04:29 -0500 Subject: [PATCH] Fix #2818: diagnose unformattable arguments in unpacked case --- include/fmt/core.h | 37 +++++++++++++++----------- test/compile-error-test/CMakeLists.txt | 26 ++++++++++++++++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 2ea48559..43bca2fb 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1260,7 +1260,7 @@ template class value { }; template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1513,7 +1513,7 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) -> basic_format_arg; template @@ -1674,19 +1674,7 @@ constexpr auto encode_types() -> unsigned long long { } template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { const auto& arg = arg_mapper().map(std::forward(val)); constexpr bool formattable_char = @@ -1715,9 +1703,26 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { return {arg}; } +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); +} + template -inline auto make_arg(const T& value) -> basic_format_arg { +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { return make_arg(value); } FMT_END_DETAIL_NAMESPACE diff --git a/test/compile-error-test/CMakeLists.txt b/test/compile-error-test/CMakeLists.txt index db7a9429..3847a5d0 100644 --- a/test/compile-error-test/CMakeLists.txt +++ b/test/compile-error-test/CMakeLists.txt @@ -6,6 +6,8 @@ project(compile-error-test CXX) set(fmt_headers " #include #include + #include + #include ") set(error_test_names "") @@ -154,6 +156,16 @@ expect_compile(format-function-error " fmt::format(\"{}\", f); " ERROR) +# Formatting an unformattable argument should always be a compile time error +expect_compile(format-lots-of-arguments-with-unformattable " + struct E {}; + fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E()); +" ERROR) +expect_compile(format-lots-of-arguments-with-function " + void (*f)(); + fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f); +" ERROR) + # Make sure that compiler features detected in the header # match the features detected in CMake. if (SUPPORTS_USER_DEFINED_LITERALS) @@ -181,6 +193,20 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20) #error #endif " ERROR) + expect_compile(print-string-number-spec-error " + #ifdef FMT_HAS_CONSTEVAL + fmt::print(\"{:d}\", \"I am not a number\"); + #else + #error + #endif + " ERROR) + expect_compile(print-stream-string-number-spec-error " + #ifdef FMT_HAS_CONSTEVAL + fmt::print(std::cout, \"{:d}\", \"I am not a number\"); + #else + #error + #endif + " ERROR) # Compile-time argument name check expect_compile(format-string-name "