#2390: consteval format checks for wchar_t and char8_t
This commit is contained in:
parent
e5c46e13e8
commit
cd2b1c09fb
@ -29,8 +29,11 @@ using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround broken conversion on older gcc.
|
||||
template <typename Char, typename... Args> using xformat_string = basic_string_view<Char>;
|
||||
template <typename... Args> using wformat_string = wstring_view;
|
||||
#else
|
||||
template <typename Char, typename... Args>
|
||||
using xformat_string = basic_format_string<Char, type_identity_t<Args>...>;
|
||||
template <typename... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||
#endif
|
||||
@ -40,6 +43,12 @@ template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||
template <> struct is_char<char16_t> : std::true_type {};
|
||||
template <> struct is_char<char32_t> : std::true_type {};
|
||||
|
||||
template <typename Char, typename... Args>
|
||||
constexpr format_arg_store<buffer_context<Char>, Args...> make_xformat_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
|
||||
const Args&... args) {
|
||||
@ -87,10 +96,26 @@ auto vformat(basic_string_view<Char> format_str,
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||
return vformat<wchar_t>(fmt, make_xformat_args<wchar_t>(args...));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
auto format(xformat_string<detail::char8_type, T...> fmt, T&&... args)
|
||||
-> std::basic_string<detail::char8_type> {
|
||||
return vformat<detail::char8_type>(
|
||||
fmt, make_xformat_args<detail::char8_type>(args...));
|
||||
}
|
||||
|
||||
// Pass char_t as a default template parameter instead of using
|
||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
typename Str = xformat_string<Char, Args...>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
|
||||
(!std::is_convertible<S, Str>::value ||
|
||||
(!std::is_same<Char, wchar_t>::value &&
|
||||
!std::is_same<Char, detail::char8_type>::value)))>
|
||||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
return vformat(to_string_view(format_str), vargs);
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
// clang-format off
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/xchar.h"
|
||||
// clang-format on
|
||||
|
||||
#include <stdint.h> // uint32_t
|
||||
@ -33,6 +34,7 @@ using fmt::memory_buffer;
|
||||
using fmt::runtime;
|
||||
using fmt::string_view;
|
||||
using fmt::detail::max_value;
|
||||
using fmt::char_t;
|
||||
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
@ -1949,7 +1951,8 @@ struct test_error_handler {
|
||||
}
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR size_t len(const char* s) {
|
||||
template<typename Char>
|
||||
FMT_CONSTEXPR size_t len(const Char* s) {
|
||||
size_t len = 0;
|
||||
while (*s++) ++len;
|
||||
return len;
|
||||
@ -1964,12 +1967,12 @@ FMT_CONSTEXPR bool equal(const char* s1, const char* s2) {
|
||||
return *s1 == *s2;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
|
||||
template <typename Char, typename... Args>
|
||||
FMT_CONSTEXPR bool test_error(const Char* fmt, const char* expected_error) {
|
||||
const char* actual_error = nullptr;
|
||||
auto s = string_view(fmt, len(fmt));
|
||||
auto s = fmt::basic_string_view<Char>(fmt, len(fmt));
|
||||
auto checker =
|
||||
fmt::detail::format_string_checker<char, test_error_handler, Args...>(
|
||||
fmt::detail::format_string_checker<Char, test_error_handler, Args...>(
|
||||
s, test_error_handler(actual_error));
|
||||
fmt::detail::parse_format_string<true>(s, checker);
|
||||
return equal(actual_error, expected_error);
|
||||
@ -1978,7 +1981,7 @@ FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
|
||||
# define EXPECT_ERROR_NOARGS(fmt, error) \
|
||||
static_assert(test_error(fmt, error), "")
|
||||
# define EXPECT_ERROR(fmt, error, ...) \
|
||||
static_assert(test_error<__VA_ARGS__>(fmt, error), "")
|
||||
static_assert(test_error<char_t<decltype(fmt)>, __VA_ARGS__>(fmt, error), "")
|
||||
|
||||
TEST(format_test, format_string_errors) {
|
||||
EXPECT_ERROR_NOARGS("foo", nullptr);
|
||||
@ -2041,6 +2044,9 @@ TEST(format_test, format_string_errors) {
|
||||
EXPECT_ERROR("{}{1}",
|
||||
"cannot switch from automatic to manual argument indexing", int,
|
||||
int);
|
||||
|
||||
EXPECT_ERROR(L"{:d}", "invalid type specifier", decltype(L""));
|
||||
EXPECT_ERROR(u8"{:d}", "invalid type specifier", decltype(u8""));
|
||||
}
|
||||
|
||||
TEST(format_test, vformat_to) {
|
||||
|
||||
@ -78,7 +78,7 @@ TEST(xchar_test, format) {
|
||||
EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
|
||||
EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
|
||||
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
|
||||
EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error);
|
||||
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
||||
EXPECT_EQ(L"true", fmt::format(L"{}", true));
|
||||
EXPECT_EQ(L"a", fmt::format(L"{0}", 'a'));
|
||||
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
|
||||
@ -130,7 +130,7 @@ TEST(xchar_test, format_utf8_precision) {
|
||||
str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
|
||||
auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>(
|
||||
u8"caf\u00e9s")); // cafés
|
||||
auto result = fmt::format(format, str);
|
||||
auto result = fmt::format(fmt::runtime(format), str);
|
||||
EXPECT_EQ(fmt::detail::compute_width(result), 4);
|
||||
EXPECT_EQ(result.size(), 5);
|
||||
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
|
||||
@ -221,7 +221,7 @@ TEST(xchar_test, sign_not_truncated) {
|
||||
wchar_t format_str[] = {
|
||||
L'{', L':',
|
||||
'+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0};
|
||||
EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error);
|
||||
EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error);
|
||||
}
|
||||
|
||||
namespace fake_qt {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user