Implement compile-time checks by default

This commit is contained in:
Victor Zverovich 2020-11-15 09:03:20 -08:00
parent befd7d4a2f
commit bcc20b29df
12 changed files with 258 additions and 223 deletions

View File

@ -17,6 +17,9 @@ jobs:
std: 14
- cxx: g++-10
std: 17
- cxx: g++-10
std: 20
cxxflags: -DFMT_COMPILE_TIME_CHECKS=1
- cxx: clang++-9
std: 11
- cxx: clang++-9
@ -36,6 +39,7 @@ jobs:
working-directory: ${{runner.workspace}}/build
env:
CXX: ${{matrix.cxx}}
CXXFLAGS: ${{matrix.cxxflags}}
run: |
cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{matrix.fuzz}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} -DFMT_DOC=OFF \

View File

@ -404,20 +404,20 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
};
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
specs = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> tm_format;
tm_format.append(specs.begin(), specs.end());
tm_format.push_back('\0');
basic_memory_buffer<Char> buf;
size_t start = buf.size();
for (;;) {
@ -440,7 +440,7 @@ template <typename Char> struct formatter<std::tm, Char> {
return std::copy(buf.begin(), buf.end(), ctx.out());
}
basic_memory_buffer<Char> tm_format;
basic_string_view<Char> specs;
};
namespace detail {
@ -638,28 +638,29 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
struct chrono_format_checker {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
template <typename Char> void on_text(const Char*, const Char*) {}
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
FMT_NORETURN void on_full_weekday() { report_no_date(); }
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_abbr_month() { report_no_date(); }
FMT_NORETURN void on_full_month() { report_no_date(); }
void on_24_hour(numeric_system) {}
void on_12_hour(numeric_system) {}
void on_minute(numeric_system) {}
void on_second(numeric_system) {}
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
FMT_CONSTEXPR void on_minute(numeric_system) {}
FMT_CONSTEXPR void on_second(numeric_system) {}
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
FMT_NORETURN void on_us_date() { report_no_date(); }
FMT_NORETURN void on_iso_date() { report_no_date(); }
void on_12_hour_time() {}
void on_24_hour_time() {}
void on_iso_time() {}
void on_am_pm() {}
void on_duration_value() {}
void on_duration_unit() {}
FMT_CONSTEXPR void on_12_hour_time() {}
FMT_CONSTEXPR void on_24_hour_time() {}
FMT_CONSTEXPR void on_iso_time() {}
FMT_CONSTEXPR void on_am_pm() {}
FMT_CONSTEXPR void on_duration_value() {}
FMT_CONSTEXPR void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
};
@ -1033,11 +1034,11 @@ template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
int precision = -1;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
struct spec_handler {
@ -1060,17 +1061,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
void end_precision() {}
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
f.specs.fill = fill;
}
FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
FMT_CONSTEXPR void on_precision(int _precision) {
f.precision = _precision;
}
FMT_CONSTEXPR void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id);
}
template <typename Id> void on_dynamic_precision(Id arg_id) {
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
f.precision_ref = make_arg_ref(arg_id);
}
};
@ -1100,8 +1105,6 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);

View File

@ -249,6 +249,10 @@
# pragma execution_character_set("utf-8")
#endif
#ifndef FMT_COMPILE_TIME_CHECKS
# define FMT_COMPILE_TIME_CHECKS 0
#endif
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for older systems.
@ -1864,7 +1868,9 @@ FMT_INLINE std::basic_string<Char> vformat(
*/
// 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>>
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS ||
!std::is_same<Char, char>::value)>
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::vformat(to_string_view(format_str), vargs);

View File

@ -2652,7 +2652,8 @@ struct stringifier {
} // namespace detail
template <> struct formatter<detail::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) {
FMT_CONSTEXPR format_parse_context::iterator parse(
format_parse_context& ctx) {
return ctx.begin();
}

View File

@ -3065,7 +3065,7 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
using mapped_type =
conditional_t<detail::mapped_type_constant<T, context>::value !=
type::custom_type,
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
detail::fallback_formatter<T, char_type>>();
@ -3561,7 +3561,7 @@ template <typename Char = char> class dynamic_formatter {
public:
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
// Checks are deferred to formatting time when the argument type is known.
detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
@ -3864,6 +3864,30 @@ make_format_to_n_args(const Args&... args) {
return format_arg_store<buffer_context<Char>, Args...>(args...);
}
#if FMT_COMPILE_TIME_CHECKS
template <typename... Args> struct format_string {
string_view str;
template <size_t N> consteval format_string(const char (&s)[N]) : str(s) {
if constexpr (detail::count_named_args<Args...>() == 0) {
using checker = detail::format_string_checker<char, detail::error_handler,
remove_cvref_t<Args>...>;
detail::parse_format_string<true>(string_view(s, N), checker(s, {}));
}
}
template <typename T,
FMT_ENABLE_IF(std::is_constructible_v<string_view, const T&>)>
format_string(const T& s) : str(s) {}
};
template <typename... Args>
FMT_INLINE std::string format(
format_string<std::type_identity_t<Args>...> format_str, Args&&... args) {
return detail::vformat(format_str.str, make_format_args(args...));
}
#endif
template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>>
std::basic_string<Char> detail::vformat(
basic_string_view<Char> format_str,

View File

@ -36,20 +36,18 @@ struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
Char prefix = '{';
Char delimiter = ',';
Char postfix = '}';
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
Char prefix = '(';
Char delimiter = ',';
Char postfix = ')';
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};

View File

@ -148,7 +148,7 @@ endif ()
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
if (FMT_PEDANTIC)
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
# MSVC fails to compile GMock when C++17 is enabled.
if (FMT_HAS_VARIANT AND NOT MSVC)
add_fmt_test(std-format-test)

View File

@ -96,12 +96,14 @@ TEST(TimeTest, GMTime) {
}
TEST(TimeTest, TimePoint) {
std::chrono::system_clock::time_point point = std::chrono::system_clock::now();
std::chrono::system_clock::time_point point =
std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(point);
std::tm tm = *std::localtime(&t);
char strftime_output[256];
std::strftime(strftime_output, sizeof(strftime_output), "It is %Y-%m-%d %H:%M:%S", &tm);
std::strftime(strftime_output, sizeof(strftime_output),
"It is %Y-%m-%d %H:%M:%S", &tm);
EXPECT_EQ(strftime_output, fmt::format("It is {:%Y-%m-%d %H:%M:%S}", point));
}
@ -246,25 +248,25 @@ TEST(ChronoTest, FormatSpecs) {
TEST(ChronoTest, InvalidSpecs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format("{:%a}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%A}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%c}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%x}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ex}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%X}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%EX}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%D}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%F}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ec}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%w}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%u}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%b}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
EXPECT_THROW_MSG(fmt::format(+"{:%a}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%A}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%c}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%x}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%Ex}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%X}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%EX}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%D}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%F}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%Ec}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%w}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%u}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%b}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%B}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%Z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format(+"{:%Eq}", sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
EXPECT_THROW_MSG(fmt::format(+"{:%Oq}", sec), fmt::format_error,
"invalid format");
}
@ -307,7 +309,7 @@ TEST(ChronoTest, FormatDefaultFP) {
}
TEST(ChronoTest, FormatPrecision) {
EXPECT_THROW_MSG(fmt::format("{:.2}", std::chrono::seconds(42)),
EXPECT_THROW_MSG(fmt::format(+"{:.2}", std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
@ -334,7 +336,7 @@ TEST(ChronoTest, FormatSimpleQq) {
}
TEST(ChronoTest, FormatPrecisionQq) {
EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)),
EXPECT_THROW_MSG(fmt::format(+"{:.2%Q %q}", std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
@ -351,12 +353,12 @@ TEST(ChronoTest, FormatFullSpecsQq) {
}
TEST(ChronoTest, InvalidWidthId) {
EXPECT_THROW(fmt::format("{:{o}", std::chrono::seconds(0)),
EXPECT_THROW(fmt::format(+"{:{o}", std::chrono::seconds(0)),
fmt::format_error);
}
TEST(ChronoTest, InvalidColons) {
EXPECT_THROW(fmt::format("{0}=:{0::", std::chrono::seconds(0)),
EXPECT_THROW(fmt::format(+"{0}=:{0::", std::chrono::seconds(0)),
fmt::format_error);
}

View File

@ -24,6 +24,9 @@
#endif
#include "fmt/args.h"
#if defined(FMT_COMPILE_TIME_CHECKS) && FMT_COMPILE_TIME_CHECKS
# include "fmt/format.h"
#endif
#undef min
#undef max
@ -633,7 +636,7 @@ template <> struct formatter<convertible_to_int> {
};
template <> struct formatter<convertible_to_c_string> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_c_string, format_context& ctx)
@ -696,18 +699,10 @@ TYPED_TEST(IsStringTest, IsString) {
}
TEST(CoreTest, Format) {
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
EXPECT_EQ(fmt::format("{}", 42), "42");
}
TEST(CoreTest, FormatTo) {
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42);
EXPECT_EQ(s, "42");

View File

@ -521,9 +521,10 @@ TEST(FormatterTest, Escape) {
}
TEST(FormatterTest, UnmatchedBraces) {
EXPECT_THROW_MSG(format("{"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("}"), format_error, "unmatched '}' in format string");
EXPECT_THROW_MSG(format("{0{}"), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{"), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"}"), format_error,
"unmatched '}' in format string");
EXPECT_THROW_MSG(format(+"{0{}"), format_error, "invalid format string");
}
TEST(FormatterTest, NoArgs) { EXPECT_EQ("test", format("test")); }
@ -539,22 +540,22 @@ TEST(FormatterTest, ArgsInDifferentPositions) {
}
TEST(FormatterTest, ArgErrors) {
EXPECT_THROW_MSG(format("{"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0}"), format_error, "argument not found");
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{"), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{?}"), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{0"), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{0}"), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{00}", 42), format_error, "invalid format string");
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{%u", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+format_str), format_error, "invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error, "argument not found");
EXPECT_THROW_MSG(format(+format_str), format_error, "argument not found");
safe_sprintf(format_str, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str), format_error, "number is too big");
safe_sprintf(format_str, "{%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str), format_error, "number is too big");
}
template <int N> struct TestFormat {
@ -595,22 +596,22 @@ TEST(FormatterTest, NamedArg) {
fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0),
fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0),
fmt::arg("o", 0), fmt::arg("p", 0)));
EXPECT_THROW_MSG(format("{a}"), format_error, "argument not found");
EXPECT_THROW_MSG(format("{a}", 42), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{a}"), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{a}", 42), format_error, "argument not found");
}
TEST(FormatterTest, AutoArgIndex) {
EXPECT_EQ("abc", format("{}{}{}", 'a', 'b', 'c'));
EXPECT_THROW_MSG(format("{0}{}", 'a', 'b'), format_error,
EXPECT_THROW_MSG(format(+"{0}{}", 'a', 'b'), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{}{0}", 'a', 'b'), format_error,
EXPECT_THROW_MSG(format(+"{}{0}", 'a', 'b'), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_EQ("1.2", format("{:.{}}", 1.2345, 2));
EXPECT_THROW_MSG(format("{0}:.{}", 1.2345, 2), format_error,
EXPECT_THROW_MSG(format(+"{0}:.{}", 1.2345, 2), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error,
EXPECT_THROW_MSG(format(+"{:.{0}}", 1.2345, 2), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(format("{}"), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{}"), format_error, "argument not found");
}
TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); }
@ -671,9 +672,9 @@ TEST(FormatterTest, CenterAlign) {
}
TEST(FormatterTest, Fill) {
EXPECT_THROW_MSG(format("{0:{<5}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:{<5}", 'c'), format_error,
"invalid fill character '{'");
EXPECT_THROW_MSG(format("{0:{<5}}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:{<5}}", 'c'), format_error,
"invalid fill character '{'");
EXPECT_EQ("**42", format("{0:*>4}", 42));
EXPECT_EQ("**-42", format("{0:*>5}", -42));
@ -690,7 +691,7 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
EXPECT_EQ("жж42", format("{0:ж>4}", 42));
EXPECT_THROW_MSG(format("{:\x80\x80\x80\x80\x80>}", 0), format_error,
EXPECT_THROW_MSG(format(+"{:\x80\x80\x80\x80\x80>}", 0), format_error,
"missing '}' in format string");
}
@ -698,72 +699,72 @@ TEST(FormatterTest, PlusSign) {
EXPECT_EQ("+42", format("{0:+}", 42));
EXPECT_EQ("-42", format("{0:+}", -42));
EXPECT_EQ("+42", format("{0:+}", 42));
EXPECT_THROW_MSG(format("{0:+}", 42u), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", format("{0:+}", 42l));
EXPECT_THROW_MSG(format("{0:+}", 42ul), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", format("{0:+}", 42ll));
EXPECT_THROW_MSG(format("{0:+}", 42ull), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", format("{0:+}", 42.0));
EXPECT_EQ("+42", format("{0:+}", 42.0l));
EXPECT_THROW_MSG(format("{0:+", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:+", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:+}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:+}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", reinterpret_cast<void*>(0x42)), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format(+"{0:+}", reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(FormatterTest, MinusSign) {
EXPECT_EQ("42", format("{0:-}", 42));
EXPECT_EQ("-42", format("{0:-}", -42));
EXPECT_EQ("42", format("{0:-}", 42));
EXPECT_THROW_MSG(format("{0:-}", 42u), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", format("{0:-}", 42l));
EXPECT_THROW_MSG(format("{0:-}", 42ul), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", format("{0:-}", 42ll));
EXPECT_THROW_MSG(format("{0:-}", 42ull), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", format("{0:-}", 42.0));
EXPECT_EQ("42", format("{0:-}", 42.0l));
EXPECT_THROW_MSG(format("{0:-", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:-", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:-}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:-}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", reinterpret_cast<void*>(0x42)), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format(+"{0:-}", reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(FormatterTest, SpaceSign) {
EXPECT_EQ(" 42", format("{0: }", 42));
EXPECT_EQ("-42", format("{0: }", -42));
EXPECT_EQ(" 42", format("{0: }", 42));
EXPECT_THROW_MSG(format("{0: }", 42u), format_error,
EXPECT_THROW_MSG(format(+"{0: }", 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", format("{0: }", 42l));
EXPECT_THROW_MSG(format("{0: }", 42ul), format_error,
EXPECT_THROW_MSG(format(+"{0: }", 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", format("{0: }", 42ll));
EXPECT_THROW_MSG(format("{0: }", 42ull), format_error,
EXPECT_THROW_MSG(format(+"{0: }", 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", format("{0: }", 42.0));
EXPECT_EQ(" 42", format("{0: }", 42.0l));
EXPECT_THROW_MSG(format("{0: ", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0: ", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0: }", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0: }", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0: }", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", reinterpret_cast<void*>(0x42)), format_error,
EXPECT_THROW_MSG(format(+"{0: }", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format(+"{0: }", reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(FormatterTest, SignNotTruncated) {
@ -812,14 +813,14 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0.", format("{:#.0f}", 0.01));
EXPECT_EQ("0.50", format("{:#.2g}", 0.5));
EXPECT_EQ("0.", format("{:#.0f}", 0.5));
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:#", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:#}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:#}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", reinterpret_cast<void*>(0x42)), format_error,
EXPECT_THROW_MSG(format(+"{0:#}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format(+"{0:#}", reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(FormatterTest, ZeroFlag) {
@ -832,13 +833,13 @@ TEST(FormatterTest, ZeroFlag) {
EXPECT_EQ("00042", format("{0:05}", 42ull));
EXPECT_EQ("-000042", format("{0:07}", -42.0));
EXPECT_EQ("-000042", format("{0:07}", -42.0l));
EXPECT_THROW_MSG(format("{0:0", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:0", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:05}", 'c'), format_error,
EXPECT_THROW_MSG(format(+"{0:05}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:05}", "abc"), format_error,
EXPECT_THROW_MSG(format(+"{0:05}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", reinterpret_cast<void*>(0x42)),
EXPECT_THROW_MSG(format(+"{0:05}", reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
@ -846,16 +847,16 @@ TEST(FormatterTest, Width) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:%u", UINT_MAX);
increment(format_str + 3);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
safe_sprintf(format_str, "{0:%u", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
EXPECT_EQ(" -42", format("{0:4}", -42));
EXPECT_EQ(" 42", format("{0:5}", 42u));
EXPECT_EQ(" -42", format("{0:6}", -42l));
@ -876,39 +877,40 @@ TEST(FormatterTest, RuntimeWidth) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
format_str[size + 1] = '}';
format_str[size + 2] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format("{0:{", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0:{}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:{", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{0:{}", 0), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{0:{?}}", 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(format(+"{0:{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:{0:}}", 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0, -1), format_error, "negative width");
EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1u)), format_error,
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, -1), format_error, "negative width");
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, (INT_MAX + 1u)), format_error,
"number is too big");
EXPECT_THROW_MSG(format("{0:{1}}", 0, -1l), format_error, "negative width");
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, -1l), format_error, "negative width");
if (const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(format("{0:{1}}", 0, (value + 1)), format_error,
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, (value + 1)), format_error,
"number is too big");
}
EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1ul)), format_error,
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, (INT_MAX + 1ul)), format_error,
"number is too big");
EXPECT_THROW_MSG(format("{0:{1}}", 0, '0'), format_error,
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, '0'), format_error,
"width is not integer");
EXPECT_THROW_MSG(format("{0:{1}}", 0, 0.0), format_error,
EXPECT_THROW_MSG(format(+"{0:{1}}", 0, 0.0), format_error,
"width is not integer");
EXPECT_EQ(" -42", format("{0:{1}}", -42, 4));
@ -929,49 +931,49 @@ TEST(FormatterTest, Precision) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:.%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format("{0:.", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.", 0), format_error,
"missing precision specifier");
EXPECT_THROW_MSG(format("{0:.}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.}", 0), format_error,
"missing precision specifier");
EXPECT_THROW_MSG(format("{0:.2", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.2", 0), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42u), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42u), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42u), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42u), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42l), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42l), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42l), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42l), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42ul), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42ul), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ul), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42ul), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42ll), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42ll), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ll), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42ll), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2}", 42ull), format_error,
EXPECT_THROW_MSG(format(+"{0:.2}", 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ull), format_error,
EXPECT_THROW_MSG(format(+"{0:.2f}", 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:3.0}", 'x'), format_error,
EXPECT_THROW_MSG(format(+"{0:3.0}", 'x'), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", format("{0:.2}", 1.2345l));
@ -1008,13 +1010,13 @@ TEST(FormatterTest, Precision) {
EXPECT_EQ("1e+01", format("{:.0e}", 9.5));
EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
EXPECT_THROW_MSG(format(+"{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast<void*>(0xcafe)),
EXPECT_THROW_MSG(format(+"{0:.2f}", reinterpret_cast<void*>(0xcafe)),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::detail::max_value<int>()),
EXPECT_THROW_MSG(format(+"{:.{}e}", 42.0, fmt::detail::max_value<int>()),
format_error, "number is too big");
EXPECT_EQ("st", format("{0:.2}", "str"));
@ -1024,79 +1026,79 @@ TEST(FormatterTest, RuntimePrecision) {
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
increment(format_str + 5);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
format_str[size + 1] = '}';
format_str[size + 2] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format(+format_str, 0), format_error, "number is too big");
EXPECT_THROW_MSG(format("{0:.{", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0:.{}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.{", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format(+"{0:.{}", 0), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:.{?}}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.{?}}", 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}", 0, 0), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error,
EXPECT_THROW_MSG(format(+"{0:.{0:}}", 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, -1), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, -1), format_error,
"negative precision");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, (INT_MAX + 1u)), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, (INT_MAX + 1u)), format_error,
"number is too big");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, -1l), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, -1l), format_error,
"negative precision");
if (const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(format("{0:.{1}}", 0, (value + 1)), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, (value + 1)), format_error,
"number is too big");
}
EXPECT_THROW_MSG(format("{0:.{1}}", 0, (INT_MAX + 1ul)), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, (INT_MAX + 1ul)), format_error,
"number is too big");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, '0'), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, '0'), format_error,
"precision is not integer");
EXPECT_THROW_MSG(format("{0:.{1}}", 0, 0.0), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 0, 0.0), format_error,
"precision is not integer");
EXPECT_THROW_MSG(format("{0:.{1}}", 42, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 42u, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42u, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42u, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42u, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 42l, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42l, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42l, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42l, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 42ul, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42ul, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42ul, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42ul, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 42ll, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42ll, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42ll, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42ll, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 42ull, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}}", 42ull, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", 42ull, 2), format_error,
EXPECT_THROW_MSG(format(+"{0:.{1}f}", 42ull, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:3.{1}}", 'x', 0), format_error,
EXPECT_THROW_MSG(format(+"{0:3.{1}}", 'x', 0), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", format("{0:.{1}}", 1.2345, 2));
EXPECT_EQ("1.2", format("{1:.{0}}", 2, 1.2345l));
EXPECT_THROW_MSG(format("{0:.{1}}", reinterpret_cast<void*>(0xcafe), 2),
EXPECT_THROW_MSG(format(+"{0:.{1}}", reinterpret_cast<void*>(0xcafe), 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}f}", reinterpret_cast<void*>(0xcafe), 2),
EXPECT_THROW_MSG(format(+"{0:.{1}f}", reinterpret_cast<void*>(0xcafe), 2),
format_error,
"precision not allowed for this argument type");
@ -1112,7 +1114,7 @@ void check_unknown_types(const T& value, const char* types, const char*) {
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
safe_sprintf(format_str, "{0:10%c}", c);
const char* message = "invalid type specifier";
EXPECT_THROW_MSG(format(format_str, value), format_error, message)
EXPECT_THROW_MSG(format(+format_str, value), format_error, message)
<< format_str << " " << message;
}
}
@ -1133,7 +1135,7 @@ TEST(FormatterTest, FormatShort) {
}
TEST(FormatterTest, FormatInt) {
EXPECT_THROW_MSG(format("{0:v", 42), format_error,
EXPECT_THROW_MSG(format(+"{0:v", 42), format_error,
"missing '}' in format string");
check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ("x", format("{:c}", static_cast<int>('x')));
@ -1437,7 +1439,7 @@ TEST(FormatterTest, FormatCString) {
EXPECT_EQ("test", format("{0:s}", "test"));
char nonconst[] = "nonconst";
EXPECT_EQ("nonconst", format("{0}", nonconst));
EXPECT_THROW_MSG(format("{0}", static_cast<const char*>(nullptr)),
EXPECT_THROW_MSG(format(+"{0}", static_cast<const char*>(nullptr)),
format_error, "string pointer is null");
}
@ -1570,7 +1572,7 @@ FMT_END_NAMESPACE
TEST(FormatterTest, FormatCustom) {
Date date(2012, 12, 9);
EXPECT_THROW_MSG(fmt::format("{:s}", date), format_error,
EXPECT_THROW_MSG(fmt::format(+"{:s}", date), format_error,
"unknown format specifier");
}
@ -1669,7 +1671,7 @@ TEST(FormatterTest, Examples) {
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42));
EXPECT_EQ("The answer is 42", format("The answer is {}", 42));
EXPECT_THROW_MSG(format("The answer is {:d}", "forty-two"), format_error,
EXPECT_THROW_MSG(format(+"The answer is {:d}", "forty-two"), format_error,
"invalid type specifier");
EXPECT_EQ(L"Cyrillic letter \x42e", format(L"Cyrillic letter {}", L'\x42e'));
@ -1931,25 +1933,25 @@ TEST(FormatTest, DynamicFormatter) {
EXPECT_EQ("42", format("{:d}", num));
EXPECT_EQ("foo", format("{:s}", str));
EXPECT_EQ(" 42 foo ", format("{:{}} {:{}}", num, 3, str, 4));
EXPECT_THROW_MSG(format("{0:{}}", num), format_error,
EXPECT_THROW_MSG(format(+"{0:{}}", num), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:{0}}", num), format_error,
EXPECT_THROW_MSG(format(+"{:{0}}", num), format_error,
"cannot switch from automatic to manual argument indexing");
#if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{:=}", str), format_error,
EXPECT_THROW_MSG(format(+"{:=}", str), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_THROW_MSG(format("{:+}", str), format_error,
EXPECT_THROW_MSG(format(+"{:+}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:-}", str), format_error,
EXPECT_THROW_MSG(format(+"{:-}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{: }", str), format_error,
EXPECT_THROW_MSG(format(+"{: }", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:#}", str), format_error,
EXPECT_THROW_MSG(format(+"{:#}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:0}", str), format_error,
EXPECT_THROW_MSG(format(+"{:0}", str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{:.2}", num), format_error,
EXPECT_THROW_MSG(format(+"{:.2}", num), format_error,
"precision not allowed for this argument type");
}

View File

@ -115,7 +115,7 @@ template <class charT> struct formatter<std::complex<double>, charT> {
detail::dynamic_format_specs<char> specs_;
public:
typename basic_format_parse_context<charT>::iterator parse(
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
basic_format_parse_context<charT>& ctx) {
using handler_type =
detail::dynamic_specs_handler<basic_format_parse_context<charT>>;

View File

@ -97,20 +97,20 @@ TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
#if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
EXPECT_THROW_MSG(format(+"{0:=5}", TestString("def")), format_error,
"format specifier requires numeric argument");
#endif
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
EXPECT_THROW_MSG(format(+"{0:+}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
EXPECT_THROW_MSG(format(+"{0:-}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
EXPECT_THROW_MSG(format(+"{0: }", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
EXPECT_THROW_MSG(format(+"{0:#}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
EXPECT_THROW_MSG(format(+"{0:05}", TestString()), format_error,
"format specifier requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));