From fe0d910a7de68710171bfcc994802fe8a840c547 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 15 Jan 2024 06:48:10 -0800 Subject: [PATCH] Replace multiple error reporting mechanisms with report_error --- include/fmt/base.h | 59 ++++++++++++++++++++-------------------- include/fmt/format-inl.h | 2 +- include/fmt/format.h | 32 ++++++++++------------ include/fmt/printf.h | 20 ++++++-------- include/fmt/ranges.h | 7 ++--- test/format-test.cc | 2 +- test/scan-test.cc | 2 +- test/scan.h | 20 +++++++------- 8 files changed, 70 insertions(+), 74 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index 4d0fb703..432a6f0a 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -683,8 +683,17 @@ enum { }; } // namespace detail -/** Throws ``format_error`` with a given message. */ -FMT_NORETURN FMT_API void throw_format_error(const char* message); +/** + Reports a format error at compile time or, via a ``format_error`` exception, + at runtime. + */ +// This function is intentionally not constexpr to give a compile-time error. +FMT_NORETURN FMT_API void report_error(const char* message); + +FMT_DEPRECATED FMT_NORETURN inline void throw_format_error( + const char* message) { + report_error(message); +} /** String's character (code unit) type. */ template class basic_format_parse_context { */ FMT_CONSTEXPR auto next_arg_id() -> int { if (next_arg_id_ < 0) { - throw_format_error( - "cannot switch from manual to automatic argument indexing"); + report_error("cannot switch from manual to automatic argument indexing"); return 0; } int id = next_arg_id_++; @@ -753,8 +761,7 @@ template class basic_format_parse_context { */ FMT_CONSTEXPR void check_arg_id(int id) { if (next_arg_id_ > 0) { - throw_format_error( - "cannot switch from automatic to manual argument indexing"); + report_error("cannot switch from automatic to manual argument indexing"); return; } next_arg_id_ = -1; @@ -787,20 +794,20 @@ class compile_parse_context : public basic_format_parse_context { FMT_CONSTEXPR auto next_arg_id() -> int { int id = base::next_arg_id(); - if (id >= num_args_) throw_format_error("argument not found"); + if (id >= num_args_) report_error("argument not found"); return id; } FMT_CONSTEXPR void check_arg_id(int id) { base::check_arg_id(id); - if (id >= num_args_) throw_format_error("argument not found"); + if (id >= num_args_) report_error("argument not found"); } using base::check_arg_id; FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { detail::ignore_unused(arg_id); if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) - throw_format_error("width/precision is not integer"); + report_error("width/precision is not integer"); } }; @@ -1081,7 +1088,7 @@ FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { using context = detail::compile_parse_context; if (id >= static_cast(this)->num_args()) - throw_format_error("argument not found"); + report_error("argument not found"); } } @@ -1916,9 +1923,6 @@ class context { } auto args() const -> const basic_format_args& { return args_; } - // This function is intentionally not constexpr to give a compile-time error. - void on_error(const char* message) { throw_format_error(message); } - // Returns an iterator to the beginning of the output range. FMT_CONSTEXPR auto out() -> iterator { return out_; } @@ -2215,13 +2219,13 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) - throw_format_error("invalid format string"); + report_error("invalid format string"); else handler.on_index(index); return begin; } if (!is_name_start(c)) { - throw_format_error("invalid format string"); + report_error("invalid format string"); return begin; } auto it = begin; @@ -2274,13 +2278,13 @@ FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, if (val != -1) value = val; else - throw_format_error("number is too big"); + report_error("number is too big"); } else if (*begin == '{') { ++begin; auto handler = dynamic_spec_id_handler{ctx, ref}; if (begin != end) begin = parse_arg_id(begin, end, handler); if (begin != end && *begin == '}') return ++begin; - throw_format_error("invalid format string"); + report_error("invalid format string"); } return begin; } @@ -2292,7 +2296,7 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, -> const Char* { ++begin; if (begin == end || *begin == '}') { - throw_format_error("invalid precision"); + report_error("invalid precision"); return begin; } return parse_dynamic_spec(begin, end, value, ref, ctx); @@ -2319,7 +2323,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, state current_state = state::start; FMT_CONSTEXPR void operator()(state s, bool valid = true) { if (current_state >= s || !valid) - throw_format_error("invalid format specifier"); + report_error("invalid format specifier"); current_state = s; } } enter_state; @@ -2334,7 +2338,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { if (!in(arg_type, set)) { if (arg_type == type::none_type) return begin; - throw_format_error("invalid format specifier"); + report_error("invalid format specifier"); } specs.type = pres_type; return begin + 1; @@ -2378,7 +2382,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, enter_state(state::zero); if (!is_arithmetic_type(arg_type)) { if (arg_type == type::none_type) return begin; - throw_format_error("format specifier requires numeric argument"); + report_error("format specifier requires numeric argument"); } if (specs.align == align::none) { // Ignore 0 if align is specified for compatibility with std::format. @@ -2448,8 +2452,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, case 'a': return parse_presentation_type(pres::hexfloat, float_set); case 'c': - if (arg_type == type::bool_type) - throw_format_error("invalid format specifier"); + if (arg_type == type::bool_type) report_error("invalid format specifier"); return parse_presentation_type(pres::chr, integral_set); case 's': return parse_presentation_type(pres::string, @@ -2466,11 +2469,11 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, // Parse fill and alignment. auto fill_end = begin + code_point_length(begin); if (end - fill_end <= 0) { - throw_format_error("invalid format specifier"); + report_error("invalid format specifier"); return begin; } if (*begin == '{') { - throw_format_error("invalid fill character '{'"); + report_error("invalid fill character '{'"); return begin; } auto align = parse_align(to_ascii(*fill_end)); @@ -2610,7 +2613,7 @@ FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { return false; } if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - throw_format_error("invalid format specifier for char"); + report_error("invalid format specifier for char"); return true; } @@ -2687,9 +2690,7 @@ template class format_string_checker { return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; } - FMT_CONSTEXPR void on_error(const char* message) { - throw_format_error(message); - } + FMT_CONSTEXPR void on_error(const char* message) { report_error(message); } }; // A base class for compile-time strings. diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 15649194..05ea41ac 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -123,7 +123,7 @@ FMT_FUNC auto write_loc(appender out, loc_value value, } } // namespace detail -FMT_FUNC void throw_format_error(const char* message) { +FMT_FUNC void report_error(const char* message) { FMT_THROW(format_error(message)); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 558b6dd1..12f5ba09 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1033,8 +1033,6 @@ template class generic_context { return args_; } - void on_error(const char* message) { throw_format_error(message); } - FMT_CONSTEXPR auto out() -> iterator { return out_; } void advance_to(iterator it) { @@ -2333,7 +2331,7 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s, -> OutputIt { if (specs.type == presentation_type::pointer) return write_ptr(out, bit_cast(s), &specs); - if (!s) throw_format_error("string pointer is null"); + if (!s) report_error("string pointer is null"); return write(out, basic_string_view(s), specs, {}); } @@ -2384,7 +2382,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, auto c = *begin; if (c == '}') return begin; if (c == '{') { - throw_format_error("invalid fill character '{'"); + report_error("invalid fill character '{'"); return begin; } specs.fill = {begin, to_unsigned(p - begin)}; @@ -3600,7 +3598,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, : 6; if (fspecs.format == float_format::exp) { if (precision == max_value()) - throw_format_error("number is too big"); + report_error("number is too big"); else ++precision; } else if (fspecs.format != float_format::fixed && precision == 0) { @@ -3707,7 +3705,7 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { template FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); - throw_format_error("string pointer is null"); + report_error("string pointer is null"); return out; } @@ -3785,13 +3783,13 @@ template struct arg_formatter { struct width_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative width"); + if (is_negative(value)) report_error("negative width"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("width is not integer"); + report_error("width is not integer"); return 0; } }; @@ -3799,13 +3797,13 @@ struct width_checker { struct precision_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative precision"); + if (is_negative(value)) report_error("negative precision"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("precision is not integer"); + report_error("precision is not integer"); return 0; } }; @@ -3813,15 +3811,14 @@ struct precision_checker { template FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int { unsigned long long value = arg.visit(Handler()); - if (value > to_unsigned(max_value())) - throw_format_error("number is too big"); + if (value > to_unsigned(max_value())) report_error("number is too big"); return static_cast(value); } template FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) { auto arg = ctx.arg(id); - if (!arg) ctx.on_error("argument not found"); + if (!arg) report_error("argument not found"); return arg; } @@ -3893,6 +3890,7 @@ using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void format_error_code(buffer& out, int error_code, string_view message) noexcept; +using fmt::report_error; FMT_API void report_error(format_func func, int error_code, const char* message) noexcept; } // namespace detail @@ -4253,7 +4251,7 @@ void vformat_to(buffer& buf, basic_string_view fmt, auto out = basic_appender(buf); if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { auto arg = args.get(0); - if (!arg) throw_format_error("argument not found"); + if (!arg) report_error("argument not found"); arg.visit(default_arg_formatter{out, args, loc}); return; } @@ -4280,7 +4278,7 @@ void vformat_to(buffer& buf, basic_string_view fmt, } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { int arg_id = context.arg_id(id); - if (arg_id < 0) throw_format_error("argument not found"); + if (arg_id < 0) report_error("argument not found"); return arg_id; } @@ -4303,13 +4301,13 @@ void vformat_to(buffer& buf, basic_string_view fmt, detail::handle_dynamic_spec( specs.precision, specs.precision_ref, context); if (begin == end || *begin != '}') - throw_format_error("missing '}' in format string"); + report_error("missing '}' in format string"); context.advance_to(arg.visit( arg_formatter{context.out(), specs, context.locale()})); return begin; } - void on_error(const char* message) { throw_format_error(message); } + void on_error(const char* message) { report_error(message); } }; detail::parse_format_string(fmt, format_handler(out, fmt, args, loc)); } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 13b4df6c..30f41404 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -52,8 +52,6 @@ template class basic_printf_context { auto arg(int id) const -> basic_format_arg { return args_.get(id); } - - void on_error(const char* message) { throw_format_error(message); } }; namespace detail { @@ -80,13 +78,13 @@ struct printf_precision_handler { template ::value)> auto operator()(T value) -> int { if (!int_checker::is_signed>::fits_in_int(value)) - throw_format_error("number is too big"); + report_error("number is too big"); return (std::max)(static_cast(value), 0); } template ::value)> auto operator()(T) -> int { - throw_format_error("precision is not integer"); + report_error("precision is not integer"); return 0; } }; @@ -208,13 +206,13 @@ template class printf_width_handler { width = 0 - width; } unsigned int_max = max_value(); - if (width > int_max) throw_format_error("number is too big"); + if (width > int_max) report_error("number is too big"); return static_cast(width); } template ::value)> auto operator()(T) -> unsigned { - throw_format_error("width is not integer"); + report_error("width is not integer"); return 0; } }; @@ -352,7 +350,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs, if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. - if (value == -1) throw_format_error("number is too big"); + if (value == -1) report_error("number is too big"); specs.width = value; return arg_index; } @@ -363,7 +361,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs, if (it != end) { if (*it >= '0' && *it <= '9') { specs.width = parse_nonnegative_int(it, end, -1); - if (specs.width == -1) throw_format_error("number is too big"); + if (specs.width == -1) report_error("number is too big"); } else if (*it == '*') { ++it; specs.width = static_cast( @@ -457,7 +455,7 @@ void vprintf(buffer& buf, basic_string_view format, // Parse argument index, flags and width. int arg_index = parse_header(it, end, specs, get_arg); - if (arg_index == 0) throw_format_error("argument not found"); + if (arg_index == 0) report_error("argument not found"); // Parse precision. if (it != end && *it == '.') { @@ -539,7 +537,7 @@ void vprintf(buffer& buf, basic_string_view format, } // Parse type. - if (it == end) throw_format_error("invalid format string"); + if (it == end) report_error("invalid format string"); char type = static_cast(*it++); if (arg.is_integral()) { // Normalize type. @@ -556,7 +554,7 @@ void vprintf(buffer& buf, basic_string_view format, bool upper = false; specs.type = parse_printf_presentation_type(type, arg.type(), upper); if (specs.type == presentation_type::none) - throw_format_error("invalid format specifier"); + report_error("invalid format specifier"); specs.upper = upper; start = it; diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 07d8d38b..9499fa14 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -310,8 +310,7 @@ struct formatter FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); - if (it != ctx.end() && *it != '}') - throw_format_error("invalid format specifier"); + if (it != ctx.end() && *it != '}') report_error("invalid format specifier"); detail::for_each(formatters_, detail::parse_empty_specs{ctx}); return it; } @@ -417,7 +416,7 @@ struct range_formatter< } if (it != end && *it != '}') { - if (*it != ':') throw_format_error("invalid format specifier"); + if (*it != ':') report_error("invalid format specifier"); ++it; } else { detail::maybe_set_debug_format(underlying_, true); @@ -650,7 +649,7 @@ struct formatter, Char> { if (N > 1) { auto end1 = do_parse(ctx, std::integral_constant()); if (end != end1) - throw_format_error("incompatible format specs for tuple elements"); + report_error("incompatible format specs for tuple elements"); } #endif return end; diff --git a/test/format-test.cc b/test/format-test.cc index e78fc6b1..05575643 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -447,7 +447,7 @@ TEST(memory_buffer_test, max_size_allocator_overflow) { } TEST(format_test, exception_from_lib) { - EXPECT_THROW_MSG(fmt::throw_format_error("test"), format_error, "test"); + EXPECT_THROW_MSG(fmt::report_error("test"), format_error, "test"); } TEST(format_test, escape) { diff --git a/test/scan-test.cc b/test/scan-test.cc index 76d75f16..e9ffbe79 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -87,7 +87,7 @@ template <> struct scanner { hex = true; ++it; } - if (it != end && *it != '}') throw_format_error("invalid format"); + if (it != end && *it != '}') report_error("invalid format"); return it; } diff --git a/test/scan.h b/test/scan.h index 5fd15baf..67b5d5ba 100644 --- a/test/scan.h +++ b/test/scan.h @@ -453,7 +453,7 @@ template ::value)> auto read(scan_iterator it, T& value) -> scan_iterator { if (it == scan_sentinel()) return it; char c = *it; - if (c < '0' || c > '9') throw_format_error("invalid input"); + if (c < '0' || c > '9') report_error("invalid input"); int num_digits = 0; T n = 0, prev = 0; @@ -477,7 +477,7 @@ auto read(scan_iterator it, T& value) -> scan_iterator { prev * 10ull + unsigned(prev_digit - '0') <= max) { value = n; } else { - throw_format_error("number is too big"); + report_error("number is too big"); } return it; } @@ -486,7 +486,7 @@ template ::value)> auto read_hex(scan_iterator it, T& value) -> scan_iterator { if (it == scan_sentinel()) return it; int digit = to_hex_digit(*it); - if (digit < 0) throw_format_error("invalid input"); + if (digit < 0) report_error("invalid input"); int num_digits = 0; T n = 0; @@ -501,7 +501,7 @@ auto read_hex(scan_iterator it, T& value) -> scan_iterator { if (num_digits <= (std::numeric_limits::digits >> 2)) value = n; else - throw_format_error("number is too big"); + report_error("number is too big"); return it; } @@ -518,7 +518,7 @@ auto read(scan_iterator it, T& value, const format_specs<>& specs = {}) bool negative = it != scan_sentinel() && *it == '-'; if (negative) { ++it; - if (it == scan_sentinel()) throw_format_error("invalid input"); + if (it == scan_sentinel()) report_error("invalid input"); } using unsigned_type = typename std::make_unsigned::type; unsigned_type abs_value = 0; @@ -538,7 +538,7 @@ auto read(scan_iterator it, string_view& value, const format_specs<>& = {}) -> scan_iterator { auto range = to_contiguous(it); // This could also be checked at compile time in scan. - if (!range) throw_format_error("string_view requires contiguous input"); + if (!range) report_error("string_view requires contiguous input"); auto p = range.begin; while (p != range.end && *p != ' ') ++p; size_t size = to_unsigned(p - range.begin); @@ -624,7 +624,7 @@ struct scan_handler { return begin; } - void on_error(const char* message) { throw_format_error(message); } + void on_error(const char* message) { report_error(message); } }; } // namespace detail @@ -640,7 +640,7 @@ template class scan_value { public: scan_value(T value) : value_(std::move(value)) {} - const T& value() const { return value_; } + auto value() const -> const T& { return value_; } }; // A rudimentary version of std::expected for testing the API shape. @@ -651,7 +651,7 @@ template class expected { public: expected(T value) : value_(std::move(value)) {} - const T* operator->() const { return &value_; } + auto operator->() const -> const T* { return &value_; } }; template using scan_result = expected>; @@ -688,7 +688,7 @@ auto scan_to(InputRange&& input, string_view fmt, T&... args) } template -bool scan_to(FILE* f, string_view fmt, T&... args) { +auto scan_to(FILE* f, string_view fmt, T&... args) -> bool { auto&& buf = detail::file_scan_buffer(f); vscan(buf, fmt, make_scan_args(args...)); return buf.begin() != buf.end();