Skip to content

Commit 08fe7df

Browse files
authored
[libc++][format] Don't treat a closing '}' as part of format-spec (#81305)
This allows: ``` std::println("{}>42", std::thread::id{}); std::println("{}>42", std::span<int>{}); std::println("{}>42", std::pair{42, "Hello"sv}); std::println("{:}>42", std::thread::id{}); std::println("{:}>42", std::span<int>{}); std::println("{:}>42", std::pair{42, "Hello"sv}); ``` to compile and run.
1 parent 267d6b5 commit 08fe7df

File tree

9 files changed

+189
-93
lines changed

9 files changed

+189
-93
lines changed

libcxx/include/__format/parser_std_format_spec.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ class _LIBCPP_TEMPLATE_VIS __parser {
355355
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator __parse(_ParseContext& __ctx, __fields __fields) {
356356
auto __begin = __ctx.begin();
357357
auto __end = __ctx.end();
358-
if (__begin == __end)
358+
if (__begin == __end || *__begin == _CharT('}'))
359359
return __begin;
360360

361361
if (__parse_fill_align(__begin, __end, __fields.__use_range_fill_) && __begin == __end)
@@ -577,9 +577,9 @@ class _LIBCPP_TEMPLATE_VIS __parser {
577577
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_fill_character(_CharT __fill, bool __use_range_fill) {
578578
// The forbidden fill characters all code points formed from a single code unit, thus the
579579
// check can be omitted when more code units are used.
580-
if (__use_range_fill && (__fill == _CharT('{') || __fill == _CharT('}') || __fill == _CharT(':')))
580+
if (__use_range_fill && (__fill == _CharT('{') || __fill == _CharT(':')))
581581
std::__throw_format_error("The fill option contains an invalid value");
582-
else if (__fill == _CharT('{') || __fill == _CharT('}'))
582+
else if (__fill == _CharT('{'))
583583
std::__throw_format_error("The fill option contains an invalid value");
584584
}
585585

libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ template <class CharT, class TestFunction, class ExceptionTest>
3030
void test_char_default(TestFunction check, ExceptionTest check_exception, auto&& input) {
3131
// Note when no range-underlying-spec is present the char is escaped,
3232
check(SV("['H', 'e', 'l', 'l', 'o']"), SV("{}"), input);
33+
check(SV("['H', 'e', 'l', 'l', 'o']^42"), SV("{}^42"), input);
34+
check(SV("['H', 'e', 'l', 'l', 'o']^42"), SV("{:}^42"), input);
3335

3436
// when one is present there is no escaping,
3537
check(SV("[H, e, l, l, o]"), SV("{::}"), input);
@@ -49,7 +51,7 @@ void test_char_default(TestFunction check, ExceptionTest check_exception, auto&&
4951
check(SV("__['H', 'e', 'l', 'l', 'o']___"), SV("{:_^{}}"), input, 30);
5052
check(SV("#####['H', 'e', 'l', 'l', 'o']"), SV("{:#>{}}"), input, 30);
5153

52-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
54+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
5355
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
5456
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
5557

@@ -89,7 +91,7 @@ void test_char_default(TestFunction check, ExceptionTest check_exception, auto&&
8991
check(SV("[_H__, _e__, _l__, _l__, _o__]"), SV("{::_^{}}"), input, 4);
9092
check(SV("[:::H, :::e, :::l, :::l, :::o]"), SV("{:::>{}}"), input, 4);
9193

92-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
94+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
9395
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
9496

9597
// *** sign ***
@@ -149,7 +151,7 @@ void test_char_string(TestFunction check, ExceptionTest check_exception, auto&&
149151
check(SV("_Hello__"), SV("{:_^{}s}"), input, 8);
150152
check(SV("###Hello"), SV("{:#>{}s}"), input, 8);
151153

152-
check_exception("The fill option contains an invalid value", SV("{:}<s}"), input);
154+
check_exception("The format string contains an invalid escape sequence", SV("{:}<s}"), input);
153155
check_exception("The fill option contains an invalid value", SV("{:{<s}"), input);
154156
check_exception("The fill option contains an invalid value", SV("{::<s}"), input);
155157

@@ -202,7 +204,7 @@ void test_char_escaped_string(TestFunction check, ExceptionTest check_exception,
202204
check(SV(R"(_"Hello"__)"), SV("{:_^{}?s}"), input, 10);
203205
check(SV(R"(###"Hello")"), SV("{:#>{}?s}"), input, 10);
204206

205-
check_exception("The fill option contains an invalid value", SV("{:}<?s}"), input);
207+
check_exception("The format string contains an invalid escape sequence", SV("{:}<?s}"), input);
206208
check_exception("The fill option contains an invalid value", SV("{:{<?s}"), input);
207209
check_exception("The fill option contains an invalid value", SV("{::<?s}"), input);
208210

@@ -304,6 +306,8 @@ void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) {
304306
template <class CharT, class TestFunction, class ExceptionTest>
305307
void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input) {
306308
check(SV("[true, true, false]"), SV("{}"), input);
309+
check(SV("[true, true, false]^42"), SV("{}^42"), input);
310+
check(SV("[true, true, false]^42"), SV("{:}^42"), input);
307311

308312
// ***** underlying has no format-spec
309313

@@ -318,7 +322,7 @@ void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input)
318322
check(SV("__[true, true, false]___"), SV("{:_^{}}"), input, 24);
319323
check(SV("#####[true, true, false]"), SV("{:#>{}}"), input, 24);
320324

321-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
325+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
322326
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
323327
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
324328

@@ -360,7 +364,7 @@ void test_bool(TestFunction check, ExceptionTest check_exception, auto&& input)
360364
check(SV("[_true__, _true__, _false_]"), SV("{::_^{}}"), input, 7);
361365
check(SV("[:::true, :::true, ::false]"), SV("{:::>{}}"), input, 7);
362366

363-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
367+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
364368
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
365369

366370
// *** sign ***
@@ -418,6 +422,8 @@ void test_bool(TestFunction check, ExceptionTest check_exception) {
418422
template <class CharT, class TestFunction, class ExceptionTest>
419423
void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
420424
check(SV("[-42, 1, 2, 42]"), SV("{}"), input);
425+
check(SV("[-42, 1, 2, 42]^42"), SV("{}^42"), input);
426+
check(SV("[-42, 1, 2, 42]^42"), SV("{:}^42"), input);
421427

422428
// ***** underlying has no format-spec
423429

@@ -432,7 +438,7 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
432438
check(SV("__[-42, 1, 2, 42]___"), SV("{:_^{}}"), input, 20);
433439
check(SV("#####[-42, 1, 2, 42]"), SV("{:#>{}}"), input, 20);
434440

435-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
441+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
436442
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
437443
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
438444

@@ -474,7 +480,7 @@ void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) {
474480
check(SV("[_-42_, __1__, __2__, _42__]"), SV("{::_^{}}"), input, 5);
475481
check(SV("[::-42, ::::1, ::::2, :::42]"), SV("{:::>{}}"), input, 5);
476482

477-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
483+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
478484
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
479485

480486
// *** sign ***
@@ -526,6 +532,8 @@ void test_int(TestFunction check, ExceptionTest check_exception) {
526532
template <class CharT, class TestFunction, class ExceptionTest>
527533
void test_floating_point(TestFunction check, ExceptionTest check_exception, auto&& input) {
528534
check(SV("[-42.5, 0, 1.25, 42.5]"), SV("{}"), input);
535+
check(SV("[-42.5, 0, 1.25, 42.5]^42"), SV("{}^42"), input);
536+
check(SV("[-42.5, 0, 1.25, 42.5]^42"), SV("{:}^42"), input);
529537

530538
// ***** underlying has no format-spec
531539

@@ -540,7 +548,7 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception, auto
540548
check(SV("__[-42.5, 0, 1.25, 42.5]___"), SV("{:_^{}}"), input, 27);
541549
check(SV("#####[-42.5, 0, 1.25, 42.5]"), SV("{:#>{}}"), input, 27);
542550

543-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
551+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
544552
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
545553
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
546554

@@ -582,7 +590,7 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception, auto
582590
check(SV("[-42.5, __0__, 1.25_, 42.5_]"), SV("{::_^{}}"), input, 5);
583591
check(SV("[-42.5, ::::0, :1.25, :42.5]"), SV("{:::>{}}"), input, 5);
584592

585-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
593+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
586594
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
587595

588596
// *** sign ***
@@ -662,6 +670,8 @@ void test_floating_point(TestFunction check, ExceptionTest check_exception) {
662670
template <class CharT, class TestFunction, class ExceptionTest>
663671
void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& input) {
664672
check(SV("[0x0]"), SV("{}"), input);
673+
check(SV("[0x0]^42"), SV("{}^42"), input);
674+
check(SV("[0x0]^42"), SV("{:}^42"), input);
665675

666676
// ***** underlying has no format-spec
667677

@@ -676,7 +686,7 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu
676686
check(SV("__[0x0]___"), SV("{:_^{}}"), input, 10);
677687
check(SV("#####[0x0]"), SV("{:#>{}}"), input, 10);
678688

679-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
689+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
680690
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
681691
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
682692

@@ -716,7 +726,7 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu
716726
check(SV("[_0x0_]"), SV("{::_^{}}"), input, 5);
717727
check(SV("[::0x0]"), SV("{:::>{}}"), input, 5);
718728

719-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
729+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
720730
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
721731

722732
// *** sign ***
@@ -770,6 +780,8 @@ void test_pointer(TestFunction check, ExceptionTest check_exception) {
770780
template <class CharT, class TestFunction, class ExceptionTest>
771781
void test_string(TestFunction check, ExceptionTest check_exception, auto&& input) {
772782
check(SV(R"(["Hello", "world"])"), SV("{}"), input);
783+
check(SV(R"(["Hello", "world"]^42)"), SV("{}^42"), input);
784+
check(SV(R"(["Hello", "world"]^42)"), SV("{:}^42"), input);
773785

774786
// ***** underlying has no format-spec
775787

@@ -784,7 +796,7 @@ void test_string(TestFunction check, ExceptionTest check_exception, auto&& input
784796
check(SV(R"(__["Hello", "world"]___)"), SV("{:_^{}}"), input, 23);
785797
check(SV(R"(#####["Hello", "world"])"), SV("{:#>{}}"), input, 23);
786798

787-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
799+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
788800
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
789801
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
790802

@@ -824,7 +836,7 @@ void test_string(TestFunction check, ExceptionTest check_exception, auto&& input
824836
check(SV(R"([_Hello__, _world__])"), SV("{::_^{}}"), input, 8);
825837
check(SV(R"([:::Hello, :::world])"), SV("{:::>{}}"), input, 8);
826838

827-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
839+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
828840
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
829841

830842
// *** sign ***
@@ -880,6 +892,8 @@ void test_string(TestFunction check, ExceptionTest check_exception) {
880892
template <class CharT, class TestFunction, class ExceptionTest>
881893
void test_status(TestFunction check, ExceptionTest check_exception, auto&& input) {
882894
check(SV("[0xaaaa, 0x5555, 0xaa55]"), SV("{}"), input);
895+
check(SV("[0xaaaa, 0x5555, 0xaa55]^42"), SV("{}^42"), input);
896+
check(SV("[0xaaaa, 0x5555, 0xaa55]^42"), SV("{:}^42"), input);
883897

884898
// ***** underlying has no format-spec
885899

@@ -894,7 +908,7 @@ void test_status(TestFunction check, ExceptionTest check_exception, auto&& input
894908
check(SV("__[0xaaaa, 0x5555, 0xaa55]___"), SV("{:_^{}}"), input, 29);
895909
check(SV("#####[0xaaaa, 0x5555, 0xaa55]"), SV("{:#>{}}"), input, 29);
896910

897-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
911+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
898912
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
899913
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
900914

libcxx/test/std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.tests.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
template <class CharT, class TestFunction, class ExceptionTest>
1717
void format_test_vector_bool(TestFunction check, ExceptionTest check_exception, auto&& input) {
1818
check(SV("[true, true, false]"), SV("{}"), input);
19+
check(SV("[true, true, false]^42"), SV("{}^42"), input);
20+
check(SV("[true, true, false]^42"), SV("{:}^42"), input);
1921

2022
// ***** underlying has no format-spec
2123

@@ -30,7 +32,7 @@ void format_test_vector_bool(TestFunction check, ExceptionTest check_exception,
3032
check(SV("__[true, true, false]___"), SV("{:_^{}}"), input, 24);
3133
check(SV("#####[true, true, false]"), SV("{:#>{}}"), input, 24);
3234

33-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
35+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
3436
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
3537
check_exception("The fill option contains an invalid value", SV("{::<}"), input);
3638

@@ -73,7 +75,7 @@ void format_test_vector_bool(TestFunction check, ExceptionTest check_exception,
7375
check(SV("[_true__, _true__, _false_]"), SV("{::_^{}}"), input, 7);
7476
check(SV("[:::true, :::true, ::false]"), SV("{:::>{}}"), input, 7);
7577

76-
check_exception("The fill option contains an invalid value", SV("{::}<}"), input);
78+
check_exception("The format string contains an invalid escape sequence", SV("{::}<}"), input);
7779
check_exception("The fill option contains an invalid value", SV("{::{<}"), input);
7880

7981
// *** sign ***

libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
2323
/***** Test the type specific part *****/
2424
#if !defined(__APPLE__) && !defined(__FreeBSD__)
2525
check(SV("0"), SV("{}"), input);
26+
check(SV("0^42"), SV("{}^42"), input);
27+
check(SV("0^42"), SV("{:}^42"), input);
2628

2729
// *** align-fill & width ***
2830
check(SV(" 0"), SV("{:5}"), input);
@@ -36,6 +38,8 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
3638
check(SV("####0"), SV("{:#>{}}"), input, 5);
3739
#else // !defined(__APPLE__) && !defined(__FreeBSD__)
3840
check(SV("0x0"), SV("{}"), input);
41+
check(SV("0x0^42"), SV("{}^42"), input);
42+
check(SV("0x0^42"), SV("{:}^42"), input);
3943

4044
// *** align-fill & width ***
4145
check(SV(" 0x0"), SV("{:7}"), input);
@@ -50,7 +54,7 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
5054
#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
5155

5256
/***** Test the type generic part *****/
53-
check_exception("The fill option contains an invalid value", SV("{:}<}"), input);
57+
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
5458
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
5559

5660
// *** sign ***

0 commit comments

Comments
 (0)