From 48e0a59222f2e1200193fd83a7c55ea3879dd8dc Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 9 Jun 2022 17:59:27 -0700 Subject: [PATCH] Implement compile-time checks for dynamic width/precision type --- include/fmt/core.h | 24 +++++++++++++++++++++--- test/core-test.cc | 1 + test/format-test.cc | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 1e8ecad1..c6ebef20 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -710,8 +710,8 @@ class basic_format_parse_context : private ErrorHandler { next_arg_id_ = -1; do_check_arg_id(id); } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); FMT_CONSTEXPR void on_error(const char* message) { ErrorHandler::on_error(message); @@ -738,7 +738,8 @@ class compile_parse_context ErrorHandler eh = {}, int next_arg_id = 0) : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {} - constexpr int num_args() const { return num_args_; } + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } FMT_CONSTEXPR auto next_arg_id() -> int { int id = base::next_arg_id(); @@ -751,6 +752,11 @@ class compile_parse_context if (id >= num_args_) this->on_error("argument not found"); } using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + this->on_error("width/precision is not integer"); + } }; FMT_END_DETAIL_NAMESPACE @@ -766,6 +772,15 @@ basic_format_parse_context::do_check_arg_id(int id) { } } +template +FMT_CONSTEXPR void +basic_format_parse_context::check_dynamic_spec(int arg_id) { + if (detail::is_constant_evaluated()) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} + template class basic_format_arg; template class basic_format_args; template class dynamic_format_arg_store; @@ -2243,11 +2258,14 @@ class dynamic_specs_handler FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { context_.check_arg_id(arg_id); + context_.check_dynamic_spec(arg_id); return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { - return arg_ref_type(context_.next_arg_id()); + int arg_id = context_.next_arg_id(); + context_.check_dynamic_spec(arg_id); + return arg_ref_type(arg_id); } FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) diff --git a/test/core-test.cc b/test/core-test.cc index ce0816c6..96cde1ed 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -584,6 +584,7 @@ struct test_parse_context { constexpr int next_arg_id() { return 11; } template FMT_CONSTEXPR void check_arg_id(Id) {} + FMT_CONSTEXPR void check_dynamic_spec(int) {} constexpr const char* begin() { return nullptr; } constexpr const char* end() { return nullptr; } diff --git a/test/format-test.cc b/test/format-test.cc index 4ec7c838..5d8f19f3 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2177,6 +2177,7 @@ TEST(format_test, format_string_errors) { EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{:{}}", "argument not found", int); EXPECT_ERROR("{:.{}}", "argument not found", double); + EXPECT_ERROR("{:{}}", "width/precision is not integer", int, double); EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int); EXPECT_ERROR("{:s}", "invalid type specifier", int); EXPECT_ERROR("{:s}", "invalid type specifier", char);