mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-04 15:40:07 +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 {
|
namespace internal {
|
||||||
|
|
||||||
struct error_handler {
|
struct error_handler {
|
||||||
|
// This function is intentionally not constexpr to give a compile-time error.
|
||||||
void on_error(const char *message) {
|
void on_error(const char *message) {
|
||||||
FMT_THROW(format_error(message));
|
FMT_THROW(format_error(message));
|
||||||
}
|
}
|
||||||
@ -2586,10 +2587,11 @@ constexpr const Char *parse_format_specs(parse_context<Char> &ctx) {
|
|||||||
return f.parse(ctx);
|
return f.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename ErrorHandler, typename... Args>
|
||||||
struct format_string_checker {
|
class format_string_checker : public ErrorHandler {
|
||||||
public:
|
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 *) {}
|
constexpr void on_text(const Char *, const Char *) {}
|
||||||
|
|
||||||
@ -2610,13 +2612,10 @@ struct format_string_checker {
|
|||||||
return parse_funcs_[arg_index_](ctx);
|
return parse_funcs_[arg_index_](ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is intentionally not constexpr to give a compile-time error.
|
|
||||||
void on_error(const char *);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr void check_arg_index() {
|
constexpr void check_arg_index() {
|
||||||
if (arg_index_ < 0 || arg_index_ >= sizeof...(Args))
|
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.
|
// Format specifier parsing function.
|
||||||
@ -2629,10 +2628,12 @@ struct format_string_checker {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename ErrorHandler, typename... Args>
|
||||||
constexpr bool check_format_string(basic_string_view<Char> s) {
|
constexpr bool check_format_string(
|
||||||
format_string_checker<Char, Args...> checker(s.end());
|
basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
|
||||||
internal::parse_format_string(s.begin(), checker);
|
format_string_checker<Char, ErrorHandler, Args...>
|
||||||
|
checker(std::move(eh), s.end());
|
||||||
|
parse_format_string(s.begin(), checker);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3538,7 +3539,8 @@ template <typename String, typename... Args>
|
|||||||
inline typename std::enable_if<
|
inline typename std::enable_if<
|
||||||
std::is_base_of<internal::format_string, String>::value, std::string>::type
|
std::is_base_of<internal::format_string, String>::value, std::string>::type
|
||||||
format(String format_str, const Args & ... args) {
|
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()));
|
string_view(format_str.value(), format_str.size()));
|
||||||
return vformat(format_str.value(), make_args(args...));
|
return vformat(format_str.value(), make_args(args...));
|
||||||
}
|
}
|
||||||
@ -3919,7 +3921,8 @@ class udl_formatter {
|
|||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
std::basic_string<Char> operator()(const Args &... args) const {
|
std::basic_string<Char> operator()(const Args &... args) const {
|
||||||
constexpr Char s[] = {CHARS..., '\0'};
|
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)));
|
basic_string_view<Char>(s, sizeof...(CHARS)));
|
||||||
return format(s, args...);
|
return format(s, args...);
|
||||||
}
|
}
|
||||||
|
@ -1817,3 +1817,45 @@ TEST(FormatTest, UdlTemplate) {
|
|||||||
EXPECT_EQ(" 42", "{0:10}"_format(42));
|
EXPECT_EQ(" 42", "{0:10}"_format(42));
|
||||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 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…
x
Reference in New Issue
Block a user