Fix handling of weird character types when parsing sign (#1932)

This commit is contained in:
Victor Zverovich 2020-10-17 09:19:01 -07:00
parent 08370c39ff
commit 20d4f2e836
2 changed files with 34 additions and 10 deletions

View File

@ -2737,6 +2737,17 @@ FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
return begin;
}
// Converts a character to the underlying integral type.
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
constexpr Char to_integral(Char value) {
return value;
}
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
constexpr typename std::underlying_type<Char>::type to_integral(Char value) {
return value;
}
// Parses fill and alignment.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
@ -2746,7 +2757,7 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
auto p = next_code_point(begin, end);
if (p == end) p = begin;
for (;;) {
switch (static_cast<int>(*p)) {
switch (to_integral(*p)) {
case '<':
align = align::left;
break;
@ -2831,7 +2842,7 @@ FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
if (begin == end) return begin;
// Parse sign.
switch (static_cast<char>(*begin)) {
switch (to_integral(*begin)) {
case '+':
handler.on_plus();
++begin;
@ -2908,7 +2919,7 @@ FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
Handler&& handler) {
++begin;
if (begin == end) return handler.on_error("invalid format string"), end;
if (static_cast<char>(*begin) == '}') {
if (*begin == '}') {
handler.on_replacement_field(handler.on_arg_id(), begin);
} else if (*begin == '{') {
handler.on_text(begin, begin + 1);

View File

@ -719,6 +719,17 @@ TEST(FormatterTest, SpaceSign) {
"format specifier requires numeric argument");
}
TEST(FormatterTest, SignNotTruncated) {
wchar_t format_str[] = {
L'{',
L':',
'+' | (1 << fmt::detail::num_bits<char>()),
L'}',
0
};
EXPECT_THROW(format(format_str, 42), format_error);
}
TEST(FormatterTest, HashFlag) {
EXPECT_EQ("42", format("{0:#}", 42));
EXPECT_EQ("-42", format("{0:#}", -42));
@ -2462,24 +2473,26 @@ TEST(FormatTest, CharTraitsIsNotAmbiguous) {
#endif
}
struct mychar {
struct custom_char {
int value;
mychar() = default;
custom_char() = default;
template <typename T> mychar(T val) : value(static_cast<int>(val)) {}
template <typename T> custom_char(T val) : value(static_cast<int>(val)) {}
operator int() const { return value; }
};
int to_integral(custom_char c) { return c; }
FMT_BEGIN_NAMESPACE
template <> struct is_char<mychar> : std::true_type {};
template <> struct is_char<custom_char> : std::true_type {};
FMT_END_NAMESPACE
TEST(FormatTest, FormatCustomChar) {
const mychar format[] = {'{', '}', 0};
auto result = fmt::format(format, mychar('x'));
const custom_char format[] = {'{', '}', 0};
auto result = fmt::format(format, custom_char('x'));
EXPECT_EQ(result.size(), 1);
EXPECT_EQ(result[0], mychar('x'));
EXPECT_EQ(result[0], custom_char('x'));
}
// Convert a char8_t string to std::string. Otherwise GTest will insist on