diff --git a/include/fmt/core.h b/include/fmt/core.h index 92e1a6fe..f70722ce 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -592,6 +592,10 @@ enum class type { custom_type }; +constexpr auto has_sign(type t) -> bool { + return ((0xe2a >> static_cast(t)) & 1) != 0; +} + // Maps core type T to the corresponding type enum constant. template struct type_constant : std::integral_constant {}; @@ -2434,7 +2438,7 @@ FMT_CONSTEXPR inline auto parse_presentation_type(char type) case '?': return presentation_type::debug; default: - throw_format_error("invalid type specifier"); + throw_format_error("invalid format specifier"); return presentation_type::none; } } @@ -2465,31 +2469,25 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( begin = align.end; if (begin == end) return begin; - // Parse sign. - switch (to_ascii(*begin)) { - case '+': - specs.sign = sign::plus; - ++begin; - break; - case '-': - specs.sign = sign::minus; - ++begin; - break; - case ' ': - specs.sign = sign::space; - ++begin; - break; - default: - break; - } - if (specs.sign != sign::none) { - require_numeric_argument(arg_type); - if (is_integral_type(arg_type) && arg_type != type::int_type && - arg_type != type::long_long_type && arg_type != type::int128_type) { - throw_format_error("format specifier requires signed argument"); + if (has_sign(arg_type)) { // Parse sign. + switch (to_ascii(*begin)) { + case '+': + specs.sign = sign::plus; + ++begin; + break; + case '-': + specs.sign = sign::minus; + ++begin; + break; + case ' ': + specs.sign = sign::space; + ++begin; + break; + default: + break; } + if (begin == end) return begin; } - if (begin == end) return begin; if (*begin == '#') { require_numeric_argument(arg_type); @@ -2649,7 +2647,7 @@ template FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler&& eh) { if (type > presentation_type::bin_upper && type != presentation_type::chr) - eh.on_error("invalid type specifier"); + eh.on_error("invalid format specifier"); } // Checks char specs and returns true if the type spec is char (and not int). @@ -2723,7 +2721,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, result.format = float_format::hex; break; default: - eh.on_error("invalid type specifier"); + eh.on_error("invalid format specifier"); break; } return result; @@ -2735,7 +2733,8 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, if (type == presentation_type::none || type == presentation_type::string || type == presentation_type::debug) return true; - if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); + if (type != presentation_type::pointer) + eh.on_error("invalid format specifier"); return false; } @@ -2744,14 +2743,14 @@ FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler&& eh = {}) { if (type != presentation_type::none && type != presentation_type::string && type != presentation_type::debug) - eh.on_error("invalid type specifier"); + eh.on_error("invalid format specifier"); } template FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, ErrorHandler&& eh) { if (type != presentation_type::none && type != presentation_type::pointer) - eh.on_error("invalid type specifier"); + eh.on_error("invalid format specifier"); } constexpr int invalid_arg_index = -1; diff --git a/include/fmt/format.h b/include/fmt/format.h index 6700b139..63365992 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2150,7 +2150,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); default: - throw_format_error("invalid type specifier"); + throw_format_error("invalid format specifier"); } return out; } diff --git a/test/core-test.cc b/test/core-test.cc index f66cc8ae..434a2449 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -483,6 +483,19 @@ TEST(arg_test, visit_invalid_arg) { fmt::visit_format_arg(visitor, arg); } +TEST(core_test, has_sign) { + using fmt::detail::type; + type types_with_sign[] = {type::int_type, type::long_long_type, + type::int128_type, type::float_type, + type::double_type, type::long_double_type}; + for (auto t : types_with_sign) EXPECT_TRUE(fmt::detail::has_sign(t)); + type types_without_sign[] = {type::uint_type, type::ulong_long_type, + type::uint128_type, type::bool_type, + type::char_type, type::string_type, + type::cstring_type, type::custom_type}; + for (auto t : types_without_sign) EXPECT_FALSE(fmt::detail::has_sign(t)); +} + #if FMT_USE_CONSTEXPR enum class arg_id_result { none, empty, index, name }; @@ -511,7 +524,7 @@ constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) { return h; } -TEST(format_test, constexpr_parse_arg_id) { +TEST(core_test, constexpr_parse_arg_id) { static_assert(parse_arg_id(":").res == arg_id_result::empty, ""); static_assert(parse_arg_id("}").res == arg_id_result::empty, ""); static_assert(parse_arg_id("42:").res == arg_id_result::index, ""); diff --git a/test/format-test.cc b/test/format-test.cc index b5fb9b47..05e4bc5b 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -646,7 +646,7 @@ TEST(format_test, fill) { fmt::format(string_view("{:\0>4}", 6), '*')); EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42)); EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0), - format_error, "invalid type specifier"); + format_error, "invalid format specifier"); } TEST(format_test, plus_sign) { @@ -654,25 +654,25 @@ TEST(format_test, plus_sign) { EXPECT_EQ("-42", fmt::format("{0:+}", -42)); EXPECT_EQ("+42", fmt::format("{0:+}", 42)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("+42", fmt::format("{0:+}", 42l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); #if FMT_USE_INT128 EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42))); #endif EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error, - "format specifier requires numeric argument"); + "invalid format specifier"); EXPECT_THROW_MSG( (void)fmt::format(runtime("{0:+}"), reinterpret_cast(0x42)), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); } TEST(format_test, minus_sign) { @@ -680,22 +680,22 @@ TEST(format_test, minus_sign) { EXPECT_EQ("-42", fmt::format("{0:-}", -42)); EXPECT_EQ("42", fmt::format("{0:-}", 42)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("42", fmt::format("{0:-}", 42l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("42", fmt::format("{0:-}", 42ll)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error, - "format specifier requires numeric argument"); + "invalid format specifier"); EXPECT_THROW_MSG( (void)fmt::format(runtime("{0:-}"), reinterpret_cast(0x42)), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); } TEST(format_test, space_sign) { @@ -703,22 +703,22 @@ TEST(format_test, space_sign) { EXPECT_EQ("-42", fmt::format("{0: }", -42)); EXPECT_EQ(" 42", fmt::format("{0: }", 42)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ(" 42", fmt::format("{0: }", 42l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ(" 42", fmt::format("{0: }", 42ll)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error, - "format specifier requires signed argument"); + "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error, - "format specifier requires numeric argument"); + "invalid format specifier"); EXPECT_THROW_MSG( (void)fmt::format(runtime("{0: }"), reinterpret_cast(0x42)), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); } TEST(format_test, hash_flag) { @@ -1172,7 +1172,7 @@ void check_unknown_types(const T& value, const char* types, const char*) { char c = static_cast(i); if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; safe_sprintf(format_str, "{0:10%c}", c); - const char* message = "invalid type specifier"; + const char* message = "invalid format specifier"; EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), value), format_error, message) << format_str << " " << message; @@ -1181,7 +1181,7 @@ void check_unknown_types(const T& value, const char* types, const char*) { TEST(format_test, format_int) { EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error, - "invalid type specifier"); + "invalid format specifier"); check_unknown_types(42, "bBdoxXnLc", "integer"); EXPECT_EQ("x", fmt::format("{:c}", static_cast('x'))); } @@ -1779,7 +1779,7 @@ TEST(format_test, format_examples) { EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42)); EXPECT_THROW_MSG( (void)fmt::format(runtime("The answer is {:d}"), "forty-two"), - format_error, "invalid type specifier"); + format_error, "invalid format specifier"); EXPECT_WRITE( stdout, fmt::print("{}", std::numeric_limits::infinity()), "inf"); diff --git a/test/ostream-test.cc b/test/ostream-test.cc index a3a51d53..eb5a33bc 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -86,11 +86,11 @@ TEST(ostream_test, format_specs) { EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()), - format_error, "format specifier requires numeric argument"); + format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()), format_error, "format specifier requires numeric argument"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()), diff --git a/test/printf-test.cc b/test/printf-test.cc index be2cc3ba..b7e7ca42 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -237,7 +237,7 @@ TEST(printf_test, width) { // Width cannot be specified twice. EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error, - "invalid type specifier"); + "invalid format specifier"); EXPECT_THROW_MSG(test_sprintf(format("%{}d", big_num), 42), format_error, "number is too big");