Check dynamic widht/precision id at compile time (#1614)

This commit is contained in:
Victor Zverovich 2020-04-22 14:18:45 -07:00
parent 2b75bd7ce6
commit 7d748a6f82
3 changed files with 23 additions and 14 deletions

View File

@ -8,6 +8,7 @@
#ifndef FMT_CORE_H_ #ifndef FMT_CORE_H_
#define FMT_CORE_H_ #define FMT_CORE_H_
#include <climits>
#include <cstdio> // std::FILE #include <cstdio> // std::FILE
#include <cstring> #include <cstring>
#include <functional> #include <functional>
@ -562,14 +563,24 @@ class basic_format_parse_context : private ErrorHandler {
private: private:
basic_string_view<Char> format_str_; basic_string_view<Char> format_str_;
int next_arg_id_; int next_arg_id_;
int num_args_;
FMT_CONSTEXPR int do_check_arg_id(int id) {
if (id >= num_args_) on_error("argument not found");
return id;
}
public: public:
using char_type = Char; using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator; using iterator = typename basic_string_view<Char>::iterator;
explicit FMT_CONSTEXPR basic_format_parse_context( explicit FMT_CONSTEXPR basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler()) basic_string_view<Char> format_str, int num_args = INT_MAX,
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} ErrorHandler eh = {})
: ErrorHandler(eh),
format_str_(format_str),
next_arg_id_(0),
num_args_(num_args) {}
/** /**
Returns an iterator to the beginning of the format string range being Returns an iterator to the beginning of the format string range being
@ -594,7 +605,7 @@ class basic_format_parse_context : private ErrorHandler {
the next argument index and switches to the automatic indexing. the next argument index and switches to the automatic indexing.
*/ */
FMT_CONSTEXPR int next_arg_id() { FMT_CONSTEXPR int next_arg_id() {
if (next_arg_id_ >= 0) return next_arg_id_++; if (next_arg_id_ >= 0) return do_check_arg_id(next_arg_id_++);
on_error("cannot switch from manual to automatic argument indexing"); on_error("cannot switch from manual to automatic argument indexing");
return 0; return 0;
} }
@ -603,10 +614,12 @@ class basic_format_parse_context : private ErrorHandler {
Reports an error if using the automatic argument indexing; otherwise Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing. switches to the manual indexing.
*/ */
FMT_CONSTEXPR void check_arg_id(int) { FMT_CONSTEXPR void check_arg_id(int id) {
if (next_arg_id_ > 0) if (next_arg_id_ > 0) {
on_error("cannot switch from automatic to manual argument indexing"); on_error("cannot switch from automatic to manual argument indexing");
else return;
}
do_check_arg_id(id);
next_arg_id_ = -1; next_arg_id_ = -1;
} }

View File

@ -2632,19 +2632,17 @@ class format_string_checker {
explicit FMT_CONSTEXPR format_string_checker( explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh) basic_string_view<Char> format_str, ErrorHandler eh)
: arg_id_(-1), : arg_id_(-1),
context_(format_str, eh), context_(format_str, num_args, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {} parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
FMT_CONSTEXPR void on_text(const Char*, const Char*) {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_CONSTEXPR void on_arg_id() { FMT_CONSTEXPR void on_arg_id() {
arg_id_ = context_.next_arg_id(); arg_id_ = context_.next_arg_id();
check_arg_id();
} }
FMT_CONSTEXPR void on_arg_id(int id) { FMT_CONSTEXPR void on_arg_id(int id) {
arg_id_ = id; arg_id_ = id;
context_.check_arg_id(id); context_.check_arg_id(id);
check_arg_id();
} }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
on_error("compile-time checks don't support named arguments"); on_error("compile-time checks don't support named arguments");
@ -2665,10 +2663,6 @@ class format_string_checker {
using parse_context_type = basic_format_parse_context<Char, ErrorHandler>; using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
enum { num_args = sizeof...(Args) }; enum { num_args = sizeof...(Args) };
FMT_CONSTEXPR void check_arg_id() {
if (arg_id_ >= num_args) context_.on_error("argument not found");
}
// Format specifier parsing function. // Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&); using parse_func = const Char* (*)(parse_context_type&);

View File

@ -2493,6 +2493,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:{}}", "argument not found", int);
EXPECT_ERROR("{:.{}}", "argument not found", double);
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int); EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
EXPECT_ERROR("{:s}", "invalid type specifier", int); EXPECT_ERROR("{:s}", "invalid type specifier", int);
EXPECT_ERROR("{:s}", "invalid type specifier", bool); EXPECT_ERROR("{:s}", "invalid type specifier", bool);