Replace multiple error reporting mechanisms with report_error

This commit is contained in:
Victor Zverovich 2024-01-15 06:48:10 -08:00
parent f9294f0e60
commit fe0d910a7d
8 changed files with 70 additions and 74 deletions

View File

@ -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 <typename S,
@ -738,8 +747,7 @@ template <typename Char> 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 <typename Char> 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<Char> {
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<Char>::do_check_arg_id(int id) {
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
if (id >= static_cast<context*>(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<context>& { 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<Char>{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<Char>& 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 <typename Char, typename... Args> 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.

View File

@ -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));
}

View File

@ -1033,8 +1033,6 @@ template <typename OutputIt, typename Char> 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<Char>(out, bit_cast<uintptr_t>(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<Char>(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<int>())
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 <typename Char, typename OutputIt>
FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt {
if (value) return write(out, basic_string_view<Char>(value));
throw_format_error("string pointer is null");
report_error("string pointer is null");
return out;
}
@ -3785,13 +3783,13 @@ template <typename Char> struct arg_formatter {
struct width_checker {
template <typename T, FMT_ENABLE_IF(is_integer<T>::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<unsigned long long>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::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 <typename T, FMT_ENABLE_IF(is_integer<T>::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<unsigned long long>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::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 <typename Handler, typename FormatArg>
FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int {
unsigned long long value = arg.visit(Handler());
if (value > to_unsigned(max_value<int>()))
throw_format_error("number is too big");
if (value > to_unsigned(max_value<int>())) report_error("number is too big");
return static_cast<int>(value);
}
template <typename Context, typename ID>
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<char>&, int, const char*);
FMT_API void format_error_code(buffer<char>& 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<Char>& buf, basic_string_view<Char> fmt,
auto out = basic_appender<Char>(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<Char>{out, args, loc});
return;
}
@ -4280,7 +4278,7 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> 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<Char>& buf, basic_string_view<Char> fmt,
detail::handle_dynamic_spec<detail::precision_checker>(
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<Char>{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<false>(fmt, format_handler(out, fmt, args, loc));
}

View File

@ -52,8 +52,6 @@ template <typename Char> class basic_printf_context {
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
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 <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big");
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::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 <typename Char> class printf_width_handler {
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) throw_format_error("number is too big");
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::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<Char>& 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<Char>& 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<int>(
@ -457,7 +455,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> 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<Char>& buf, basic_string_view<Char> format,
}
// Parse type.
if (it == end) throw_format_error("invalid format string");
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
@ -556,7 +554,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> 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;

View File

@ -310,8 +310,7 @@ struct formatter<Tuple, Char,
template <typename ParseContext>
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<ParseContext>{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<tuple_join_view<Char, T...>, Char> {
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
throw_format_error("incompatible format specs for tuple elements");
report_error("incompatible format specs for tuple elements");
}
#endif
return end;

View File

@ -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) {

View File

@ -87,7 +87,7 @@ template <> struct scanner<num> {
hex = true;
++it;
}
if (it != end && *it != '}') throw_format_error("invalid format");
if (it != end && *it != '}') report_error("invalid format");
return it;
}

View File

@ -453,7 +453,7 @@ template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::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 <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::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<T>::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<T>::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 <typename T> 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 <typename T> class expected {
public:
expected(T value) : value_(std::move(value)) {}
const T* operator->() const { return &value_; }
auto operator->() const -> const T* { return &value_; }
};
template <typename T> using scan_result = expected<scan_value<T>>;
@ -688,7 +688,7 @@ auto scan_to(InputRange&& input, string_view fmt, T&... args)
}
template <typename... T>
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();