diff --git a/include/fmt/format.h b/include/fmt/format.h index 6dbd12d6..5fe22333 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2737,6 +2737,17 @@ FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) { return begin; } +// Converts a character to the underlying integral type. +template ::value)> +constexpr Char to_integral(Char value) { + return value; +} + +template ::value)> +constexpr typename std::underlying_type::type to_integral(Char value) { + return value; +} + // Parses fill and alignment. template FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, @@ -2746,7 +2757,7 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, auto p = next_code_point(begin, end); if (p == end) p = begin; for (;;) { - switch (static_cast(*p)) { + switch (to_integral(*p)) { case '<': align = align::left; break; @@ -2831,7 +2842,7 @@ FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end, if (begin == end) return begin; // Parse sign. - switch (static_cast(*begin)) { + switch (to_integral(*begin)) { case '+': handler.on_plus(); ++begin; @@ -2908,7 +2919,7 @@ FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin, Handler&& handler) { ++begin; if (begin == end) return handler.on_error("invalid format string"), end; - if (static_cast(*begin) == '}') { + if (*begin == '}') { handler.on_replacement_field(handler.on_arg_id(), begin); } else if (*begin == '{') { handler.on_text(begin, begin + 1); diff --git a/test/format-test.cc b/test/format-test.cc index a3c925b0..027c50a0 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -719,6 +719,17 @@ TEST(FormatterTest, SpaceSign) { "format specifier requires numeric argument"); } +TEST(FormatterTest, SignNotTruncated) { + wchar_t format_str[] = { + L'{', + L':', + '+' | (1 << fmt::detail::num_bits()), + L'}', + 0 + }; + EXPECT_THROW(format(format_str, 42), format_error); +} + TEST(FormatterTest, HashFlag) { EXPECT_EQ("42", format("{0:#}", 42)); EXPECT_EQ("-42", format("{0:#}", -42)); @@ -2462,24 +2473,26 @@ TEST(FormatTest, CharTraitsIsNotAmbiguous) { #endif } -struct mychar { +struct custom_char { int value; - mychar() = default; + custom_char() = default; - template mychar(T val) : value(static_cast(val)) {} + template custom_char(T val) : value(static_cast(val)) {} operator int() const { return value; } }; +int to_integral(custom_char c) { return c; } + FMT_BEGIN_NAMESPACE -template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; FMT_END_NAMESPACE TEST(FormatTest, FormatCustomChar) { - const mychar format[] = {'{', '}', 0}; - auto result = fmt::format(format, mychar('x')); + const custom_char format[] = {'{', '}', 0}; + auto result = fmt::format(format, custom_char('x')); EXPECT_EQ(result.size(), 1); - EXPECT_EQ(result[0], mychar('x')); + EXPECT_EQ(result[0], custom_char('x')); } // Convert a char8_t string to std::string. Otherwise GTest will insist on