Skip to content

Commit 9b43aed

Browse files
committed
[libc++][format] Implements LWG3892.
This LWG issue is based on the discussion regarding P2733R3 Fix handling of empty specifiers in std::format This paper was disussed and changed a few times in LEWG during the Issaquah meeting. The paper was not voted in, instead LEWG asked for a DR against C++26. This LWG issue contains the direction voted by LEWG. This issue has not been voted in yet. However it fixes some of the defencies on the container based formatting. Without this fix the range-default-formatter for strings looks bad when used in containers. The changes of this issue match the intended changes of P27333. type fmt before after (if changed) --------------------------------------------------------------- char {} a char {:?} 'a' array<char, 1> {} ['a'] array<char, 1> {::} [a] array<char, 1> {::c} [a] array<char, 1> {::?} ['a'] map<char, char> {} {a: a} -> {'a': 'a'} map<char, char> {::} {'a': 'a'} set<char> {} {'a'} set<char> {::} {a} set<char> {::c} {a} set<char> {::?} {'a'} tuple<char> {} ('a') stack<char> {} ['a'] stack<char> {::} [a] stack<char> {::c} [a] stack<char> {::?} ['a'] array<array<char, 1>, 1> {} [[a]] -> {'a': 'a'} array<array<char, 1>, 1> {::} [['a']] array<array<char, 1>, 1> {:::} [[a]] array<array<char, 1>, 1> {:::c} [[a]] array<array<char, 1>, 1> {:::?} [['a']] array<tuple<char>, 1> {} [(a)] -> [('a')] tuple<tuple<char>> {} ((a)) -> (('a')) tuple<array<char, 1>> {} ([a]) -> (['a']) Note the optimization text as mentioned in the tuple formatter can't be done. The call to parse may affect the formatter so its state needs to be preserved. Reviewed By: ldionne, #libc, EricWF Differential Revision: https://reviews.llvm.org/D145847
1 parent c7a34d3 commit 9b43aed

File tree

17 files changed

+426
-144
lines changed

17 files changed

+426
-144
lines changed

libcxx/docs/Status/Cxx2bIssues.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,4 @@
308308
"`3881 <https://wg21.link/LWG3881>`__","Incorrect formatting of container adapters backed by ``std::string``","February 2023","|Complete|","17.0","|format|"
309309
"","","","","",""
310310
"`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""
311+
"`3892 <https://wg21.link/LWG3892>`__","Incorrect formatting of nested ranges and tuples","Not Yet Adopted","|Complete|","17.0",""

libcxx/include/__format/formatter_tuple.h

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -53,33 +53,38 @@ struct _LIBCPP_TEMPLATE_VIS __formatter_tuple {
5353
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
5454
auto __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_tuple);
5555

56-
// [format.tuple]/7
57-
// ... For each element e in underlying_, if e.set_debug_format()
58-
// is a valid expression, calls e.set_debug_format().
59-
// TODO FMT this can be removed when P2733 is accepted.
60-
std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> {
61-
std::__set_debug_format(std::get<_Index>(__underlying_));
62-
});
63-
6456
auto __end = __parse_ctx.end();
65-
if (__begin == __end)
66-
return __begin;
67-
68-
if (*__begin == _CharT('m')) {
69-
if constexpr (sizeof...(_Args) == 2) {
70-
set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
57+
if (__begin != __end) {
58+
if (*__begin == _CharT('m')) {
59+
if constexpr (sizeof...(_Args) == 2) {
60+
set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
61+
set_brackets({}, {});
62+
++__begin;
63+
} else
64+
std::__throw_format_error("The format specifier m requires a pair or a two-element tuple");
65+
} else if (*__begin == _CharT('n')) {
7166
set_brackets({}, {});
7267
++__begin;
73-
} else
74-
std::__throw_format_error("The format specifier m requires a pair or a two-element tuple");
75-
} else if (*__begin == _CharT('n')) {
76-
set_brackets({}, {});
77-
++__begin;
68+
}
7869
}
7970

8071
if (__begin != __end && *__begin != _CharT('}'))
8172
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
8273

74+
__parse_ctx.advance_to(__begin);
75+
76+
// [format.tuple]/7
77+
// ... For each element e in underlying_, if e.set_debug_format()
78+
// is a valid expression, calls e.set_debug_format().
79+
std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> {
80+
auto& __formatter = std::get<_Index>(__underlying_);
81+
__formatter.parse(__parse_ctx);
82+
// Unlike the range_formatter we don't guard against evil parsers. Since
83+
// this format-spec never has a format-spec for the underlying type
84+
// adding the test would give additional overhead.
85+
std::__set_debug_format(__formatter);
86+
});
87+
8388
return __begin;
8489
}
8590

@@ -120,35 +125,7 @@ struct _LIBCPP_TEMPLATE_VIS __formatter_tuple {
120125
std::__for_each_index_sequence(make_index_sequence<sizeof...(_Args)>(), [&]<size_t _Index> {
121126
if constexpr (_Index)
122127
__ctx.advance_to(std::ranges::copy(__separator_, __ctx.out()).out);
123-
124-
// During review Victor suggested to make the exposition only
125-
// __underlying_ member a local variable. Currently the Standard
126-
// requires nested debug-enabled formatter specializations not to
127-
// output escaped output. P2733 fixes that bug, once accepted the
128-
// code below can be used.
129-
// (Note when a paper allows parsing a tuple-underlying-spec the
130-
// exposition only member needs to be a class member. Earlier
131-
// revisions of P2286 proposed that, but this was not pursued,
132-
// due to time constrains and complexity of the matter.)
133-
// TODO FMT This can be updated after P2733 is accepted.
134-
# if 0
135-
// P2286 uses an exposition only member in the formatter
136-
// tuple<formatter<remove_cvref_t<_Args>, _CharT>...> __underlying_;
137-
// This was used in earlier versions of the paper since
138-
// __underlying_.parse(...) was called. This is no longer the case
139-
// so we can reduce the scope of the formatter.
140-
//
141-
// It does require the underlying's parse effect to be moved here too.
142-
using _Arg = tuple_element<_Index, decltype(__tuple)>;
143-
formatter<remove_cvref_t<_Args>, _CharT> __underlying;
144-
145-
// [format.tuple]/7
146-
// ... For each element e in underlying_, if e.set_debug_format()
147-
// is a valid expression, calls e.set_debug_format().
148-
std::__set_debug_format(__underlying);
149-
# else
150128
__ctx.advance_to(std::get<_Index>(__underlying_).format(std::get<_Index>(__tuple), __ctx));
151-
# endif
152129
});
153130

154131
return std::ranges::copy(__closing_bracket_, __ctx.out()).out;

libcxx/include/__format/range_formatter.h

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,57 @@ struct _LIBCPP_TEMPLATE_VIS range_formatter {
5757
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
5858
auto __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_range);
5959
auto __end = __parse_ctx.end();
60-
if (__begin == __end)
61-
return __begin;
60+
// Note the cases where __begin == __end in this code only happens when the
61+
// replacement-field has no terminating }, or when the parse is manually
62+
// called with a format-spec. The former is an error and the latter means
63+
// using a formatter without the format functions or print.
64+
if (__begin == __end) [[unlikely]]
65+
return __parse_empty_range_underlying_spec(__parse_ctx, __begin);
6266

6367
// The n field overrides a possible m type, therefore delay applying the
6468
// effect of n until the type has been procesed.
6569
bool __clear_brackets = (*__begin == _CharT('n'));
6670
if (__clear_brackets) {
6771
++__begin;
68-
if (__begin == __end) {
72+
if (__begin == __end) [[unlikely]] {
6973
// Since there is no more data, clear the brackets before returning.
7074
set_brackets({}, {});
71-
return __begin;
75+
return __parse_empty_range_underlying_spec(__parse_ctx, __begin);
7276
}
7377
}
7478

7579
__parse_type(__begin, __end);
7680
if (__clear_brackets)
7781
set_brackets({}, {});
78-
if (__begin == __end)
79-
return __begin;
82+
if (__begin == __end) [[unlikely]]
83+
return __parse_empty_range_underlying_spec(__parse_ctx, __begin);
8084

8185
bool __has_range_underlying_spec = *__begin == _CharT(':');
86+
if (__has_range_underlying_spec) {
87+
// range-underlying-spec:
88+
// : format-spec
89+
++__begin;
90+
} else if (__begin != __end && *__begin != _CharT('}'))
91+
// When there is no underlaying range the current parse should have
92+
// consumed the format-spec. If not, the not consumed input will be
93+
// processed by the underlying. For example {:-} for a range in invalid,
94+
// the sign field is not present. Without this check the underlying_ will
95+
// get -} as input which my be valid.
96+
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
97+
98+
__parse_ctx.advance_to(__begin);
99+
__begin = __underlying_.parse(__parse_ctx);
100+
101+
// This test should not be required if __has_range_underlying_spec is false.
102+
// However this test makes sure the underlying formatter left the parser in
103+
// a valid state. (Note this is not a full protection against evil parsers.
104+
// For example
105+
// } this is test for the next argument {}
106+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
107+
// could consume more than it should.
108+
if (__begin != __end && *__begin != _CharT('}'))
109+
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
110+
82111
if (__parser_.__type_ != __format_spec::__type::__default) {
83112
// [format.range.formatter]/6
84113
// If the range-type is s or ?s, then there shall be no n option and no
@@ -96,20 +125,6 @@ struct _LIBCPP_TEMPLATE_VIS range_formatter {
96125
} else if (!__has_range_underlying_spec)
97126
std::__set_debug_format(__underlying_);
98127

99-
if (__has_range_underlying_spec) {
100-
// range-underlying-spec:
101-
// : format-spec
102-
++__begin;
103-
if (__begin == __end)
104-
return __begin;
105-
106-
__parse_ctx.advance_to(__begin);
107-
__begin = __underlying_.parse(__parse_ctx);
108-
}
109-
110-
if (__begin != __end && *__begin != _CharT('}'))
111-
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
112-
113128
return __begin;
114129
}
115130

@@ -243,6 +258,16 @@ struct _LIBCPP_TEMPLATE_VIS range_formatter {
243258
}
244259
}
245260

261+
template <class _ParseContext>
262+
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator
263+
__parse_empty_range_underlying_spec(_ParseContext& __parse_ctx, typename _ParseContext::iterator __begin) {
264+
__parse_ctx.advance_to(__begin);
265+
[[maybe_unused]] typename _ParseContext::iterator __result = __underlying_.parse(__parse_ctx);
266+
_LIBCPP_ASSERT(__result == __begin,
267+
"the underlying's parse function should not advance the input beyond the end of the input");
268+
return __begin;
269+
}
270+
246271
formatter<_Tp, _CharT> __underlying_;
247272
basic_string_view<_CharT> __separator_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ", ");
248273
basic_string_view<_CharT> __opening_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "[");

libcxx/test/std/utilities/format/format.range/format.range.fmtdef/format.pass.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <format>
2727
#include <iterator>
2828

29+
#include "assert_macros.h"
30+
#include "format.functions.common.h"
2931
#include "test_format_context.h"
3032
#include "test_macros.h"
3133
#include "make_string.h"
@@ -48,10 +50,51 @@ void test_format(StringViewT expected, std::array<int, 2> arg) {
4850
assert(result == expected);
4951
}
5052

53+
template <class CharT>
54+
void test_assure_parse_is_called(std::basic_string_view<CharT> fmt) {
55+
using String = std::basic_string<CharT>;
56+
using OutIt = std::back_insert_iterator<String>;
57+
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
58+
std::array<parse_call_validator, 2> arg;
59+
60+
String result;
61+
OutIt out = std::back_inserter(result);
62+
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
63+
64+
std::formatter<decltype(arg), CharT> formatter;
65+
std::basic_format_parse_context<CharT> ctx{fmt};
66+
67+
formatter.parse(ctx);
68+
formatter.format(arg, format_ctx);
69+
}
70+
71+
template <class CharT>
72+
void test_assure_parse_is_called() {
73+
using String = std::basic_string<CharT>;
74+
using OutIt = std::back_insert_iterator<String>;
75+
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
76+
std::array<parse_call_validator, 2> arg;
77+
78+
String result;
79+
OutIt out = std::back_inserter(result);
80+
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
81+
82+
{ // parse not called
83+
[[maybe_unused]] const std::formatter<decltype(arg), CharT> formatter;
84+
TEST_THROWS_TYPE(parse_call_validator::parse_function_not_called, formatter.format(arg, format_ctx));
85+
}
86+
87+
test_assure_parse_is_called(SV("5"));
88+
test_assure_parse_is_called(SV("n"));
89+
test_assure_parse_is_called(SV("5n"));
90+
}
91+
5192
template <class CharT>
5293
void test_fmt() {
5394
test_format(SV("[1, 42]"), std::array<int, 2>{{1, 42}});
5495
test_format(SV("[0, 99]"), std::array<int, 2>{{0, 99}});
96+
97+
test_assure_parse_is_called<CharT>();
5598
}
5699

57100
void test() {

libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.format.pass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ auto test = []<class CharT, class... Args>(
3737
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
3838
TEST_REQUIRE(out == expected,
3939
TEST_WRITE_CONCATENATED(
40-
"\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
40+
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
4141
};
4242

4343
auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {

libcxx/test/std/utilities/format/format.range/format.range.fmtmap/format.functions.tests.h

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ template <class CharT, class TestFunction, class ExceptionTest>
2626
void test_char(TestFunction check, ExceptionTest check_exception) {
2727
std::map<CharT, CharT> input{{CharT('a'), CharT('A')}, {CharT('c'), CharT('C')}, {CharT('b'), CharT('B')}};
2828

29-
check(SV("{a: A, b: B, c: C}"), SV("{}"), input);
29+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{}"), input);
3030

3131
// ***** underlying has no format-spec
3232

3333
// *** align-fill & width ***
34-
check(SV("{a: A, b: B, c: C} "), SV("{:23}"), input);
35-
check(SV("{a: A, b: B, c: C}*****"), SV("{:*<23}"), input);
36-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^23}"), input);
37-
check(SV("#####{a: A, b: B, c: C}"), SV("{:#>23}"), input);
34+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'} "), SV("{:35}"), input);
35+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}*****"), SV("{:*<35}"), input);
36+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^35}"), input);
37+
check(SV("#####{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{:#>35}"), input);
3838

39-
check(SV("{a: A, b: B, c: C} "), SV("{:{}}"), input, 23);
40-
check(SV("{a: A, b: B, c: C}*****"), SV("{:*<{}}"), input, 23);
41-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^{}}"), input, 23);
42-
check(SV("#####{a: A, b: B, c: C}"), SV("{:#>{}}"), input, 23);
39+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'} "), SV("{:{}}"), input, 35);
40+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}*****"), SV("{:*<{}}"), input, 35);
41+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^{}}"), input, 35);
42+
check(SV("#####{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{:#>{}}"), input, 35);
4343

4444
check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
4545
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
@@ -63,10 +63,10 @@ void test_char(TestFunction check, ExceptionTest check_exception) {
6363
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
6464

6565
// *** n
66-
check(SV("__a: A, b: B, c: C___"), SV("{:_^21n}"), input);
66+
check(SV("__'a': 'A', 'b': 'B', 'c': 'C'___"), SV("{:_^33n}"), input);
6767

6868
// *** type ***
69-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^23m}"), input); // the m type does the same as the default.
69+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^35m}"), input); // the m type does the same as the default.
7070
check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
7171
check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
7272

@@ -134,20 +134,20 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
134134
std::map<char, char> input{{'a', 'A'}, {'c', 'C'}, {'b', 'B'}};
135135

136136
using CharT = wchar_t;
137-
check(SV("{a: A, b: B, c: C}"), SV("{}"), input);
137+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{}"), input);
138138

139139
// ***** underlying has no format-spec
140140

141141
// *** align-fill & width ***
142-
check(SV("{a: A, b: B, c: C} "), SV("{:23}"), input);
143-
check(SV("{a: A, b: B, c: C}*****"), SV("{:*<23}"), input);
144-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^23}"), input);
145-
check(SV("#####{a: A, b: B, c: C}"), SV("{:#>23}"), input);
142+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'} "), SV("{:35}"), input);
143+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}*****"), SV("{:*<35}"), input);
144+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^35}"), input);
145+
check(SV("#####{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{:#>35}"), input);
146146

147-
check(SV("{a: A, b: B, c: C} "), SV("{:{}}"), input, 23);
148-
check(SV("{a: A, b: B, c: C}*****"), SV("{:*<{}}"), input, 23);
149-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^{}}"), input, 23);
150-
check(SV("#####{a: A, b: B, c: C}"), SV("{:#>{}}"), input, 23);
147+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'} "), SV("{:{}}"), input, 35);
148+
check(SV("{'a': 'A', 'b': 'B', 'c': 'C'}*****"), SV("{:*<{}}"), input, 35);
149+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^{}}"), input, 35);
150+
check(SV("#####{'a': 'A', 'b': 'B', 'c': 'C'}"), SV("{:#>{}}"), input, 35);
151151

152152
check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
153153
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
@@ -171,10 +171,10 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
171171
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
172172

173173
// *** n
174-
check(SV("__a: A, b: B, c: C___"), SV("{:_^21n}"), input);
174+
check(SV("__'a': 'A', 'b': 'B', 'c': 'C'___"), SV("{:_^33n}"), input);
175175

176176
// *** type ***
177-
check(SV("__{a: A, b: B, c: C}___"), SV("{:_^23m}"), input); // the m type does the same as the default.
177+
check(SV("__{'a': 'A', 'b': 'B', 'c': 'C'}___"), SV("{:_^35m}"), input); // the m type does the same as the default.
178178
check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
179179
check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
180180

@@ -642,20 +642,20 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
642642
std::map<std::basic_string<CharT>, std::basic_string<CharT>> input{
643643
{STR("hello"), STR("HELLO")}, {STR("world"), STR("WORLD")}};
644644

645-
check(SV(R"({hello: HELLO, world: WORLD})"), SV("{}"), input);
645+
check(SV(R"({"hello": "HELLO", "world": "WORLD"})"), SV("{}"), input);
646646

647647
// ***** underlying has no format-spec
648648

649649
// *** align-fill & width ***
650-
check(SV(R"({hello: HELLO, world: WORLD} )"), SV("{:33}"), input);
651-
check(SV(R"({hello: HELLO, world: WORLD}*****)"), SV("{:*<33}"), input);
652-
check(SV(R"(__{hello: HELLO, world: WORLD}___)"), SV("{:_^33}"), input);
653-
check(SV(R"(#####{hello: HELLO, world: WORLD})"), SV("{:#>33}"), input);
650+
check(SV(R"({"hello": "HELLO", "world": "WORLD"} )"), SV("{:41}"), input);
651+
check(SV(R"({"hello": "HELLO", "world": "WORLD"}*****)"), SV("{:*<41}"), input);
652+
check(SV(R"(__{"hello": "HELLO", "world": "WORLD"}___)"), SV("{:_^41}"), input);
653+
check(SV(R"(#####{"hello": "HELLO", "world": "WORLD"})"), SV("{:#>41}"), input);
654654

655-
check(SV(R"({hello: HELLO, world: WORLD} )"), SV("{:{}}"), input, 33);
656-
check(SV(R"({hello: HELLO, world: WORLD}*****)"), SV("{:*<{}}"), input, 33);
657-
check(SV(R"(__{hello: HELLO, world: WORLD}___)"), SV("{:_^{}}"), input, 33);
658-
check(SV(R"(#####{hello: HELLO, world: WORLD})"), SV("{:#>{}}"), input, 33);
655+
check(SV(R"({"hello": "HELLO", "world": "WORLD"} )"), SV("{:{}}"), input, 41);
656+
check(SV(R"({"hello": "HELLO", "world": "WORLD"}*****)"), SV("{:*<{}}"), input, 41);
657+
check(SV(R"(__{"hello": "HELLO", "world": "WORLD"}___)"), SV("{:_^{}}"), input, 41);
658+
check(SV(R"(#####{"hello": "HELLO", "world": "WORLD"})"), SV("{:#>{}}"), input, 41);
659659

660660
check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input);
661661
check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input);
@@ -677,10 +677,10 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
677677
check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input);
678678

679679
// *** n
680-
check(SV(R"(__hello: HELLO, world: WORLD___)"), SV("{:_^31n}"), input);
680+
check(SV(R"(__"hello": "HELLO", "world": "WORLD"___)"), SV("{:_^39n}"), input);
681681

682682
// *** type ***
683-
check(SV(R"(__{hello: HELLO, world: WORLD}___)"), SV("{:_^33m}"), input);
683+
check(SV(R"(__{"hello": "HELLO", "world": "WORLD"}___)"), SV("{:_^41m}"), input);
684684
check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input);
685685
check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input);
686686

0 commit comments

Comments
 (0)