Diagnose invalid precision

This commit is contained in:
Victor Zverovich 2024-07-27 09:54:40 -07:00
parent 707d7d923a
commit a80d668a52
4 changed files with 36 additions and 28 deletions

View File

@ -2323,7 +2323,7 @@ template <typename Char> struct dynamic_spec_id_handler {
} }
}; };
// Parses [integer | "{" [arg_id] "}"]. // Parses integer | "{" [arg_id] "}".
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
int& value, arg_ref<Char>& ref, int& value, arg_ref<Char>& ref,
@ -2332,24 +2332,24 @@ FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
if ('0' <= *begin && *begin <= '9') { if ('0' <= *begin && *begin <= '9') {
int val = parse_nonnegative_int(begin, end, -1); int val = parse_nonnegative_int(begin, end, -1);
if (val != -1) if (val == -1) report_error("number is too big");
value = val; value = val;
else } else {
report_error("number is too big"); if (*begin == '{') {
} else if (*begin == '{') { ++begin;
++begin; if (begin != end) {
if (begin != end) { Char c = *begin;
Char c = *begin; if (c == '}' || c == ':') {
if (c == '}' || c == ':') { int id = ctx.next_arg_id();
int id = ctx.next_arg_id(); ref = arg_ref<Char>(id);
ref = arg_ref<Char>(id); ctx.check_dynamic_spec(id);
ctx.check_dynamic_spec(id); } else {
} else { begin =
begin = parse_arg_id(begin, end, dynamic_spec_id_handler<Char>{ctx, ref});
parse_arg_id(begin, end, dynamic_spec_id_handler<Char>{ctx, ref}); }
} }
if (begin != end && *begin == '}') return ++begin;
} }
if (begin != end && *begin == '}') return ++begin;
report_error("invalid format string"); report_error("invalid format string");
} }
return begin; return begin;
@ -2361,11 +2361,9 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
basic_format_parse_context<Char>& ctx) basic_format_parse_context<Char>& ctx)
-> const Char* { -> const Char* {
++begin; ++begin;
if (begin == end || *begin == '}') { if (begin != end) begin = parse_dynamic_spec(begin, end, value, ref, ctx);
report_error("invalid precision"); else report_error("invalid precision");
return begin; return begin;
}
return parse_dynamic_spec(begin, end, value, ref, ctx);
} }
enum class state { start, align, sign, hash, zero, width, precision, locale }; enum class state { start, align, sign, hash, zero, width, precision, locale };

View File

@ -2251,8 +2251,11 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
it = detail::parse_align(it, end, specs_); it = detail::parse_align(it, end, specs_);
if (it == end) return it; if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); Char c = *it;
if (it == end) return it; if ((c >= '0' && c <= '9') || c == '{') {
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
}
auto checker = detail::chrono_format_checker(); auto checker = detail::chrono_format_checker();
if (*it == '.') { if (*it == '.') {
@ -2410,8 +2413,11 @@ template <typename Char> struct formatter<std::tm, Char> {
it = detail::parse_align(it, end, specs_); it = detail::parse_align(it, end, specs_);
if (it == end) return it; if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); Char c = *it;
if (it == end) return it; if ((c >= '0' && c <= '9') || c == '{') {
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
}
end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
// Replace the default format_str only if the new spec is not empty. // Replace the default format_str only if the new spec is not empty.

View File

@ -129,7 +129,9 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
it = detail::parse_align(it, end, specs_); it = detail::parse_align(it, end, specs_);
if (it == end) return it; if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); Char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') { if (it != end && *it == '?') {
debug_ = true; debug_ = true;
++it; ++it;

View File

@ -945,7 +945,7 @@ TEST(format_test, precision) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0.0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0.0), format_error,
"invalid precision"); "invalid precision");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0.0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0.0), format_error,
"invalid precision"); "invalid format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error,
"invalid format specifier"); "invalid format specifier");
@ -1066,6 +1066,8 @@ TEST(format_test, precision) {
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304), (void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.f}"), 42.0), format_error,
"invalid format string");
EXPECT_EQ(fmt::format("{0:.2}", "str"), "st"); EXPECT_EQ(fmt::format("{0:.2}", "str"), "st");
EXPECT_EQ(fmt::format("{0:.5}", "вожыкі"), "вожык"); EXPECT_EQ(fmt::format("{0:.5}", "вожыкі"), "вожык");