Test compile-time errors

This commit is contained in:
Victor Zverovich 2017-11-11 10:28:05 -08:00
parent 246bdafc74
commit 5b95b5d77c
2 changed files with 58 additions and 13 deletions

View File

@ -1708,6 +1708,7 @@ typedef basic_format_specs<char> format_specs;
namespace internal {
struct error_handler {
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char *message) {
FMT_THROW(format_error(message));
}
@ -2586,10 +2587,11 @@ constexpr const Char *parse_format_specs(parse_context<Char> &ctx) {
return f.parse(ctx);
}
template <typename Char, typename... Args>
struct format_string_checker {
template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker : public ErrorHandler {
public:
explicit constexpr format_string_checker(const Char *end) : end_(end) {}
explicit constexpr format_string_checker(ErrorHandler eh, const Char *end)
: ErrorHandler(std::move(eh)), end_(end) {}
constexpr void on_text(const Char *, const Char *) {}
@ -2610,13 +2612,10 @@ struct format_string_checker {
return parse_funcs_[arg_index_](ctx);
}
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char *);
private:
constexpr void check_arg_index() {
if (arg_index_ < 0 || arg_index_ >= sizeof...(Args))
on_error("argument index out of range");
this->on_error("argument index out of range");
}
// Format specifier parsing function.
@ -2629,10 +2628,12 @@ struct format_string_checker {
};
};
template <typename Char, typename... Args>
constexpr bool check_format_string(basic_string_view<Char> s) {
format_string_checker<Char, Args...> checker(s.end());
internal::parse_format_string(s.begin(), checker);
template <typename Char, typename ErrorHandler, typename... Args>
constexpr bool check_format_string(
basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...>
checker(std::move(eh), s.end());
parse_format_string(s.begin(), checker);
return true;
}
@ -3538,7 +3539,8 @@ template <typename String, typename... Args>
inline typename std::enable_if<
std::is_base_of<internal::format_string, String>::value, std::string>::type
format(String format_str, const Args & ... args) {
constexpr bool invalid_format = internal::check_format_string<char, Args...>(
constexpr bool invalid_format =
internal::check_format_string<char, internal::error_handler, Args...>(
string_view(format_str.value(), format_str.size()));
return vformat(format_str.value(), make_args(args...));
}
@ -3919,7 +3921,8 @@ class udl_formatter {
template <typename... Args>
std::basic_string<Char> operator()(const Args &... args) const {
constexpr Char s[] = {CHARS..., '\0'};
constexpr bool invalid_format = check_format_string<Char, Args...>(
constexpr bool invalid_format =
check_format_string<Char, error_handler, Args...>(
basic_string_view<Char>(s, sizeof...(CHARS)));
return format(s, args...);
}

View File

@ -1817,3 +1817,45 @@ TEST(FormatTest, UdlTemplate) {
EXPECT_EQ(" 42", "{0:10}"_format(42));
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
}
struct test_error_handler {
const char *&error;
constexpr void on_error(const char *message) { error = message; }
};
constexpr size_t len(const char *s) {
size_t len = 0;
while (*s++)
++len;
return len;
}
constexpr bool eq(const char *s1, const char *s2) {
if (!s1 && !s2)
return true;
while (*s1 && *s1 == *s2) {
++s1;
++s2;
}
return *s1 == *s2;
}
template <typename... Args>
constexpr bool test_error(const char *fmt, const char *expected_error) {
const char *actual_error = nullptr;
test_error_handler eh{actual_error};
using ref = std::reference_wrapper<test_error_handler>;
fmt::internal::check_format_string<char, test_error_handler, Args...>(
string_view(fmt, len(fmt)), eh);
return eq(actual_error, expected_error);
}
#define EXPECT_ERROR(fmt, error, ...) \
static_assert(test_error<__VA_ARGS__>(fmt, error), "")
TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("foo", nullptr);
EXPECT_ERROR("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
}