forked from fmtlib/fmt
Check dynamic width/precision id at compile time (#1614)
This commit is contained in:
@ -568,7 +568,7 @@ class basic_format_parse_context : private ErrorHandler {
|
|||||||
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, ErrorHandler eh = {})
|
||||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2626,25 +2626,51 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
|
|||||||
return f.parse(ctx);
|
return f.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A parse context with extra argument id checks. It is only used at compile
|
||||||
|
// time because adding checks at runtime would introduce substantial overhead
|
||||||
|
// and would be redundant since argument ids are checked when arguments are
|
||||||
|
// retrieved anyway.
|
||||||
|
template <typename Char, typename ErrorHandler = error_handler>
|
||||||
|
class compile_parse_context
|
||||||
|
: public basic_format_parse_context<Char, ErrorHandler> {
|
||||||
|
private:
|
||||||
|
int num_args_;
|
||||||
|
using base = basic_format_parse_context<Char, ErrorHandler>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FMT_CONSTEXPR compile_parse_context(
|
||||||
|
basic_string_view<Char> format_str, int num_args = max_value<int>(),
|
||||||
|
ErrorHandler eh = {})
|
||||||
|
: base(format_str, eh), num_args_(num_args) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR int next_arg_id() {
|
||||||
|
int id = base::next_arg_id();
|
||||||
|
if (id >= num_args_) this->on_error("argument not found");
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void check_arg_id(int id) {
|
||||||
|
base::check_arg_id(id);
|
||||||
|
if (id >= num_args_) this->on_error("argument not found");
|
||||||
|
}
|
||||||
|
using base::check_arg_id;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char, typename ErrorHandler, typename... Args>
|
template <typename Char, typename ErrorHandler, typename... Args>
|
||||||
class format_string_checker {
|
class format_string_checker {
|
||||||
public:
|
public:
|
||||||
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");
|
||||||
@ -2662,13 +2688,9 @@ class format_string_checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
|
using parse_context_type = compile_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&);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Reference in New Issue
Block a user