More tests

This commit is contained in:
Victor Zverovich 2017-11-19 06:35:23 -08:00
parent 093e2a4780
commit 5a32e64b05
5 changed files with 44 additions and 43 deletions

View File

@ -407,7 +407,7 @@ class basic_string_view {
constexpr iterator begin() const { return data_; } constexpr iterator begin() const { return data_; }
constexpr iterator end() const { return data_ + size_; } constexpr iterator end() const { return data_ + size_; }
void remove_prefix(size_t n) { constexpr void remove_prefix(size_t n) {
data_ += n; data_ += n;
size_ -= n; size_ -= n;
} }
@ -460,7 +460,7 @@ namespace internal {
// Casts nonnegative integer to unsigned. // Casts nonnegative integer to unsigned.
template <typename Int> template <typename Int>
inline typename std::make_unsigned<Int>::type to_unsigned(Int value) { constexpr typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value"); FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value); return static_cast<typename std::make_unsigned<Int>::type>(value);
} }
@ -1956,9 +1956,9 @@ class parse_context : public ErrorHandler {
int next_arg_index_; int next_arg_index_;
protected: protected:
bool check_no_auto_index(const char *&error) { constexpr bool check_no_auto_index() {
if (next_arg_index_ > 0) { if (next_arg_index_ > 0) {
error = "cannot switch from automatic to manual argument indexing"; on_error("cannot switch from automatic to manual argument indexing");
return false; return false;
} }
next_arg_index_ = -1; next_arg_index_ = -1;
@ -1981,26 +1981,25 @@ class parse_context : public ErrorHandler {
constexpr iterator end() const { return format_str_.end(); } constexpr iterator end() const { return format_str_.end(); }
// Advances the begin iterator to ``it``. // Advances the begin iterator to ``it``.
void advance_to(iterator it) { constexpr void advance_to(iterator it) {
format_str_.remove_prefix(it - begin()); format_str_.remove_prefix(it - begin());
} }
// Returns the next argument index. // Returns the next argument index.
unsigned next_arg_index(const char *&error) { constexpr unsigned next_arg_index() {
if (next_arg_index_ >= 0) if (next_arg_index_ >= 0)
return internal::to_unsigned(next_arg_index_++); return internal::to_unsigned(next_arg_index_++);
error = "cannot switch from manual to automatic argument indexing"; on_error("cannot switch from manual to automatic argument indexing");
return 0; return 0;
} }
void check_arg_id(unsigned) { constexpr void check_arg_id(unsigned) { check_no_auto_index(); }
const char *error = 0;
if (!check_no_auto_index(error))
FMT_THROW(format_error(error));
}
void check_arg_id(basic_string_view<Char>) {} void check_arg_id(basic_string_view<Char>) {}
constexpr void on_error(const char *message) {
ErrorHandler::on_error(message);
}
constexpr ErrorHandler error_handler() const { return *this; } constexpr ErrorHandler error_handler() const { return *this; }
}; };
@ -2029,7 +2028,7 @@ class context_base : public parse_context<Char>{
// Checks if manual indexing is used and returns the argument with // Checks if manual indexing is used and returns the argument with
// specified index. // specified index.
format_arg get_arg(unsigned arg_index, const char *&error) { format_arg get_arg(unsigned arg_index, const char *&error) {
return this->check_no_auto_index(error) ? return this->check_no_auto_index() ?
this->do_get_arg(arg_index, error) : format_arg(); this->do_get_arg(arg_index, error) : format_arg();
} }
@ -2365,11 +2364,7 @@ class dynamic_specs_handler :
} }
constexpr arg_ref_type make_arg_ref(auto_id) { constexpr arg_ref_type make_arg_ref(auto_id) {
const char *error = 0; return arg_ref_type(context_.next_arg_index());
auto index = context_.next_arg_index(error);
if (error)
FMT_THROW(format_error(error));
return arg_ref_type(index);
} }
dynamic_format_specs<char_type> &specs_; dynamic_format_specs<char_type> &specs_;
@ -2607,19 +2602,21 @@ constexpr const typename ParseContext::char_type *
} }
template <typename Char, typename ErrorHandler, typename... Args> template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker : public ErrorHandler { class format_string_checker {
public: public:
explicit constexpr format_string_checker(ErrorHandler eh, const Char *end) explicit constexpr format_string_checker(
: ErrorHandler(eh), end_(end) {} basic_string_view<Char> format_str, ErrorHandler eh)
: context_(format_str, eh) {}
constexpr void on_text(const Char *, const Char *) {} constexpr void on_text(const Char *, const Char *) {}
constexpr void on_arg_id() { constexpr void on_arg_id() {
++arg_index_; arg_index_ = context_.next_arg_index();
check_arg_index(); check_arg_index();
} }
constexpr void on_arg_id(unsigned index) { constexpr void on_arg_id(unsigned index) {
arg_index_ = index; arg_index_ = index;
context_.check_arg_id(index);
check_arg_index(); check_arg_index();
} }
constexpr void on_arg_id(basic_string_view<Char>) {} constexpr void on_arg_id(basic_string_view<Char>) {}
@ -2627,8 +2624,13 @@ class format_string_checker : public ErrorHandler {
constexpr void on_replacement_field(const Char *) {} constexpr void on_replacement_field(const Char *) {}
constexpr const Char *on_format_specs(const Char *s) { constexpr const Char *on_format_specs(const Char *s) {
parse_context_type ctx(basic_string_view<Char>(s, end_ - s), *this); context_.advance_to(s);
return arg_index_ < NUM_ARGS ? parse_funcs_[arg_index_](ctx) : s; return to_unsigned(arg_index_) < NUM_ARGS ?
parse_funcs_[arg_index_](context_) : s;
}
constexpr void on_error(const char *message) {
context_.on_error(message);
} }
private: private:
@ -2636,16 +2638,15 @@ class format_string_checker : public ErrorHandler {
constexpr static size_t NUM_ARGS = sizeof...(Args); constexpr static size_t NUM_ARGS = sizeof...(Args);
constexpr void check_arg_index() { constexpr void check_arg_index() {
unsigned unsigned_index = arg_index_; if (internal::to_unsigned(arg_index_) >= NUM_ARGS)
if (arg_index_ < 0 || unsigned_index >= NUM_ARGS) context_.on_error("argument index out of range");
this->on_error("argument index out of range");
} }
// Format specifier parsing function. // Format specifier parsing function.
using parse_func = const Char *(*)(parse_context_type &); using parse_func = const Char *(*)(parse_context_type &);
const Char *end_;
int arg_index_ = -1; int arg_index_ = -1;
parse_context_type context_;
parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1] = { parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1] = {
&parse_format_specs<Args, parse_context_type>... &parse_format_specs<Args, parse_context_type>...
}; };
@ -2654,7 +2655,7 @@ class format_string_checker : public ErrorHandler {
template <typename Char, typename ErrorHandler, typename... Args> template <typename Char, typename ErrorHandler, typename... Args>
constexpr bool check_format_string( constexpr bool check_format_string(
basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) { basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...> checker(eh, s.end()); format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
parse_format_string(s.begin(), checker); parse_format_string(s.begin(), checker);
return true; return true;
} }
@ -2751,7 +2752,7 @@ class basic_context :
format_arg next_arg() { format_arg next_arg() {
const char *error = 0; const char *error = 0;
format_arg arg = this->do_get_arg(this->next_arg_index(error), error); format_arg arg = this->do_get_arg(this->next_arg_index(), error);
if (error) if (error)
FMT_THROW(format_error(error)); FMT_THROW(format_error(error));
return arg; return arg;
@ -3861,15 +3862,12 @@ struct dynamic_formatter {
template <typename Char> template <typename Char>
inline typename basic_context<Char>::format_arg inline typename basic_context<Char>::format_arg
basic_context<Char>::get_arg(basic_string_view<Char> name) { basic_context<Char>::get_arg(basic_string_view<Char> name) {
const char *error = 0; if (this->check_no_auto_index()) {
if (this->check_no_auto_index(error)) {
map_.init(this->args()); map_.init(this->args());
if (const format_arg *arg = map_.find(name)) if (const format_arg *arg = map_.find(name))
return *arg; return *arg;
error = "argument not found"; this->on_error("argument not found");
} }
if (error)
FMT_THROW(format_error(error));
return format_arg(); return format_arg();
} }
@ -3881,8 +3879,8 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
struct handler : internal::error_handler { struct handler : internal::error_handler {
handler(basic_buffer<Char> &b, basic_string_view<Char> str, handler(basic_buffer<Char> &b, basic_string_view<Char> str,
basic_args<Context> args) basic_args<Context> format_args)
: buffer(b), context(str, args) {} : buffer(b), context(str, format_args) {}
void on_text(iterator begin, iterator end) { void on_text(iterator begin, iterator end) {
buffer.append(pointer_from(begin), pointer_from(end)); buffer.append(pointer_from(begin), pointer_from(end));

View File

@ -370,7 +370,7 @@ typename printf_context<Char, AF>::format_arg printf_context<Char, AF>::get_arg(
const char *error = 0; const char *error = 0;
format_arg arg; format_arg arg;
if (arg_index == std::numeric_limits<unsigned>::max()) { if (arg_index == std::numeric_limits<unsigned>::max()) {
arg_index = this->next_arg_index(error); arg_index = this->next_arg_index();
if (!error) if (!error)
arg = this->do_get_arg(arg_index, error); arg = this->do_get_arg(arg_index, error);
} else { } else {

View File

@ -32,7 +32,7 @@ struct formatter<std::tm> {
return pointer_from(end); return pointer_from(end);
} }
void format(buffer &buf, const std::tm &tm, context &ctx) { void format(buffer &buf, const std::tm &tm, context &) {
std::size_t start = buf.size(); std::size_t start = buf.size();
for (;;) { for (;;) {
std::size_t size = buf.capacity() - start; std::size_t size = buf.capacity() - start;

View File

@ -1702,7 +1702,7 @@ struct test_context {
template <typename Id> template <typename Id>
constexpr void check_arg_id(Id) {} constexpr void check_arg_id(Id) {}
constexpr unsigned next_arg_index(const char *&) { return 33; } constexpr unsigned next_arg_index() { return 33; }
void on_error(const char *) {} void on_error(const char *) {}
@ -1903,4 +1903,7 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:.x}", "missing precision specifier", int); EXPECT_ERROR("{:.x}", "missing precision specifier", int);
EXPECT_ERROR("{}", "argument index out of range"); EXPECT_ERROR("{}", "argument index out of range");
EXPECT_ERROR("{1}", "argument index out of range", int); EXPECT_ERROR("{1}", "argument index out of range", int);
EXPECT_ERROR("{1}{}",
"cannot switch from manual to automatic argument indexing",
int, int);
} }

View File

@ -87,14 +87,14 @@ TEST(PrintfTest, NumberIsTooBigInArgIndex) {
TEST(PrintfTest, SwitchArgIndexing) { TEST(PrintfTest, SwitchArgIndexing) {
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2), EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
format_error, "invalid format string"); format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2), EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
format_error, "cannot switch from manual to automatic argument indexing"); format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2), EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
format_error, "invalid format string"); format_error, "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2), EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),