Compile-time error on too many arguments provided

Adds a compile time error if the number of arguments provided to the
format function is larger than the number of braces in the format
string.
Only works in automatic argument indexing mode.

This check deliberately only works for compile-time format string
checking, not at runtime. This is because we don't want existing code to
have unexpected runtime errors after upgrades. There are also scenarios
imaginable where either the arguments or the format string is generated
at runtime, and the ability to have less braces in the format string can
actually be a feature. At the same time, compile-time format calls are
guaranteed to be constant. In other words, having too many arguments will
always mean there's a bug in the code.

The new feature works by adding a on_end_of_string() function to
formatting handlers. This function is called after the whole format
string has been parsed, and needs to be implemented by all parsing
handlers.
This commit is contained in:
Mart Slot 2019-09-24 08:27:15 +00:00
parent ccc8f5db02
commit 0aa4721472
5 changed files with 24 additions and 1 deletions

View File

@ -82,6 +82,8 @@ template <typename Char> struct part_counter {
return begin;
}
FMT_CONSTEXPR void on_end_of_string() {}
FMT_CONSTEXPR void on_error(const char*) {}
};
@ -148,6 +150,8 @@ class format_string_compiler : public error_handler {
handler_(part);
return it;
}
FMT_CONSTEXPR void on_end_of_string() {}
};
// Compiles a format string and invokes handler(part) for each parsed part.

View File

@ -489,6 +489,9 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
FMT_CONSTEXPR bool is_auto_arg_indexing() { return next_arg_id_ >= 0; }
FMT_CONSTEXPR int num_auto_args() { return next_arg_id_; }
FMT_CONSTEXPR void on_error(const char* message) {
ErrorHandler::on_error(message);
}

View File

@ -2477,8 +2477,10 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p))
if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p)) {
handler.on_end_of_string();
return write(begin, end);
}
write(begin, p);
++p;
if (p == end) return handler.on_error("invalid format string");
@ -2502,6 +2504,7 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
}
begin = p + 1;
}
handler.on_end_of_string();
}
template <typename T, typename ParseContext>
@ -2551,6 +2554,11 @@ class format_string_checker {
return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
}
FMT_CONSTEXPR void on_end_of_string() {
if (context_.is_auto_arg_indexing() && context_.num_auto_args() < num_args)
context_.on_error("number of arguments in format string is less than number of arguments provided");
}
FMT_CONSTEXPR void on_error(const char* message) {
context_.on_error(message);
}
@ -3212,6 +3220,8 @@ struct format_handler : internal::error_handler {
return begin;
}
void on_end_of_string() {}
basic_parse_context<Char> parse_context;
Context context;
basic_format_arg<Context> arg;

View File

@ -2377,6 +2377,8 @@ struct test_format_string_handler {
return begin;
}
FMT_CONSTEXPR void on_end_of_string() {}
FMT_CONSTEXPR void on_error(const char*) { error = true; }
bool error = false;
@ -2496,6 +2498,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{}{1}",
"cannot switch from automatic to manual argument indexing", int,
int);
EXPECT_ERROR("", "number of arguments in format string is less than number of arguments provided", int);
EXPECT_ERROR("{}", "number of arguments in format string is less than number of arguments provided", int, int);
}
TEST(FormatTest, VFormatTo) {

View File

@ -212,6 +212,8 @@ struct scan_handler : error_handler {
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
return parse_ctx_.begin();
}
void on_end_of_string() {}
};
} // namespace internal