diff --git a/stl/inc/format b/stl/inc/format index 00c0aa1e2f4..95df0a427d8 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3537,6 +3537,7 @@ struct _Default_arg_formatter { _OutputIt _Out; basic_format_args<_Context> _Args; _Lazy_locale _Loc; + basic_format_parse_context<_CharT>& _Parse_ctx; template _OutputIt operator()(_Ty _Val) && { @@ -3544,7 +3545,6 @@ struct _Default_arg_formatter { } _OutputIt operator()(basic_format_arg<_Context>::handle _Handle) && { - basic_format_parse_context<_CharT> _Parse_ctx({}); auto _Format_ctx = _Context::_Make_from(_STD move(_Out), _Args, _Loc); _Handle.format(_Parse_ctx, _Format_ctx); return _Format_ctx.out(); @@ -3606,9 +3606,9 @@ struct _Format_checker { : _Parse_context(_Fmt, _Num_args, _Arg_type), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _ParseContext>...} {} constexpr void _On_text(const _CharT*, const _CharT*) const noexcept {} - constexpr void _On_replacement_field(const size_t _Id, const _CharT*) const { - _ParseContext _Parse_ctx({}); - (void) _Parse_funcs[_Id](_Parse_ctx); + constexpr void _On_replacement_field(const size_t _Id, const _CharT* _Last) { + _Parse_context.advance_to(_Parse_context.begin() + (_Last - _Parse_context.begin()._Unwrapped())); + (void) _Parse_funcs[_Id](_Parse_context); } constexpr const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT*) { _Parse_context.advance_to(_Parse_context.begin() + (_First - _Parse_context.begin()._Unwrapped())); @@ -3641,14 +3641,18 @@ struct _Format_handler { _Ctx.advance_to(_RANGES _Copy_unchecked(_First, _Last, _Ctx.out()).out); } - void _On_replacement_field(const size_t _Id, const _CharT*) { + void _On_replacement_field(const size_t _Id, const _CharT* _Last) { auto _Arg = _Get_arg(_Ctx, _Id); - _Ctx.advance_to(_STD visit_format_arg( - _Default_arg_formatter<_OutputIt, _CharT>{_Ctx.out(), _Ctx._Get_args(), _Ctx._Get_lazy_locale()}, _Arg)); + if (_Arg._Active_state == _Basic_format_arg_type::_Custom_type) { + _Parse_context.advance_to(_Parse_context.begin() + (_Last - _Parse_context.begin()._Unwrapped())); + } + _Ctx.advance_to(_STD visit_format_arg(_Default_arg_formatter<_OutputIt, _CharT>{_Ctx.out(), _Ctx._Get_args(), + _Ctx._Get_lazy_locale(), _Parse_context}, + _Arg)); } const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT* _Last) { - _Parse_context.advance_to(_Parse_context.begin() + (_First - &*_Parse_context.begin())); + _Parse_context.advance_to(_Parse_context.begin() + (_First - _Parse_context.begin()._Unwrapped())); auto _Arg = _Get_arg(_Ctx, _Id); if (_Arg._Active_state == _Basic_format_arg_type::_Custom_type) { _Arg._Custom_state.format(_Parse_context, _Ctx); diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp index 999ab832d81..6a9db58b132 100644 --- a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -58,10 +59,10 @@ struct not_const_formattable_type { template <> struct std::formatter { constexpr basic_format_parse_context::iterator parse(basic_format_parse_context& parse_ctx) { - if (parse_ctx.begin() != parse_ctx.end()) { + if (parse_ctx.begin() != parse_ctx.end() && *parse_ctx.begin() != '}') { throw format_error{"only empty specs please"}; } - return parse_ctx.end(); + return parse_ctx.begin(); } format_context::iterator format(const basic_custom_formattable_type& val, format_context& ctx) const { ctx.advance_to(copy(val.string_content.begin(), val.string_content.end(), ctx.out())); @@ -72,10 +73,10 @@ struct std::formatter { template <> struct std::formatter { constexpr basic_format_parse_context::iterator parse(basic_format_parse_context& parse_ctx) { - if (parse_ctx.begin() != parse_ctx.end()) { + if (parse_ctx.begin() != parse_ctx.end() && *parse_ctx.begin() != '}') { throw format_error{"only empty specs please"}; } - return parse_ctx.end(); + return parse_ctx.begin(); } format_context::iterator format(not_const_formattable_type& val, format_context& ctx) const { ctx.advance_to(copy(val.string_content.begin(), val.string_content.end(), ctx.out())); @@ -276,6 +277,69 @@ void test_basic_format_context_construction() { static_assert(!is_constructible_with_trailing_empty_brace_impl&>); } +// Test GH-4636 ": Call to next_arg_id may result in unexpected error (regression)" + +struct FormatNextArg {}; + +template +struct std::formatter { +public: + template + constexpr auto parse(ParseContext& ctx) { + auto it = ctx.begin(); + if (it != ctx.end() && *it != '}') { + throw format_error{"Expected empty spec"}; + } + + arg_id = ctx.next_arg_id(); + return it; + } + + template + auto format(FormatNextArg, FormatContext& ctx) const { + return format_to(ctx.out(), TYPED_LITERAL(CharT, "arg-id: {}"), arg_id); + } + +private: + size_t arg_id; +}; + +template +void test_parsing_with_next_id() { + assert(format(TYPED_LITERAL(CharT, "{}, {}"), FormatNextArg{}, 0, FormatNextArg{}, TYPED_LITERAL(CharT, "1")) + == TYPED_LITERAL(CharT, "arg-id: 1, arg-id: 3")); + assert(format(TYPED_LITERAL(CharT, "{:}, {:}"), FormatNextArg{}, 2, FormatNextArg{}, TYPED_LITERAL(CharT, "3")) + == TYPED_LITERAL(CharT, "arg-id: 1, arg-id: 3")); +} + +struct NeedMagicWord {}; + +template +struct std::formatter { + constexpr auto parse(const basic_format_parse_context& ctx) { + constexpr basic_string_view magic_word{TYPED_LITERAL(CharT, "narf")}; + auto [i, j] = ranges::mismatch(ctx, magic_word); + if (j != magic_word.end()) { + throw format_error{"you didn't say the magic word!"}; + } + + if (i != ctx.end() && *i != '}') { + throw format_error{"the whole spec must be the magic word!"}; + } + return i; + } + + template + auto format(NeedMagicWord, basic_format_context& ctx) const { + return ctx.out(); + } +}; + +template +void test_parsing_needing_magic_word() { + assert(format(TYPED_LITERAL(CharT, "{:narf}"), NeedMagicWord{}).empty()); +} + int main() { test_format_family_overloads(); test_format_family_overloads(); @@ -297,5 +361,9 @@ int main() { test_basic_format_context_construction(); test_basic_format_context_construction(); test_basic_format_context_construction, wchar_t>(); - return 0; + + test_parsing_with_next_id(); + test_parsing_with_next_id(); + test_parsing_needing_magic_word(); + test_parsing_needing_magic_word(); }