diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 8617ef85..51f555a1 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -82,6 +82,8 @@ template 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. diff --git a/include/fmt/core.h b/include/fmt/core.h index 0d7d15b5..e891d1e6 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -489,6 +489,9 @@ class basic_parse_context : private ErrorHandler { FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + 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); } diff --git a/include/fmt/format.h b/include/fmt/format.h index e198233b..7c12f782 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2477,8 +2477,10 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view 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(begin, end, '{', p)) + if (*begin != '{' && !find(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 format_str, } begin = p + 1; } + handler.on_end_of_string(); } template @@ -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 parse_context; Context context; basic_format_arg arg; diff --git a/test/format-test.cc b/test/format-test.cc index f03a2bd6..5591ff14 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -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) { diff --git a/test/scan.h b/test/scan.h index ace84f77..1062e45a 100644 --- a/test/scan.h +++ b/test/scan.h @@ -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