mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-07 17:41:11 +00:00
Test compile-time errors
This commit is contained in:
parent
246bdafc74
commit
5b95b5d77c
@ -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...);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user