diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv --- a/libcxx/docs/Status/FormatPaper.csv +++ b/libcxx/docs/Status/FormatPaper.csv @@ -37,8 +37,8 @@ `[format.range] `_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|Complete|,Clang 16 `[format.range] `_,"Formatting for ranges: ``vector``",,Mark de Wever,|Complete|,Clang 16 "`P2585R0 `__","Improving default container formatting" -`[format.range.fmtstr] `_,"Formatting for ranges: strings",,Mark de Wever,|In Progress|, -`[format.range.fmtstr] `_,"Formatting for ranges: strings",,Mark de Wever,|In Progress|, +`[format.range.fmtstr] `_,"Formatting for ranges: strings",,Mark de Wever,|Complete|,Clang 17 +`[format.range.fmtstr] `_,"Formatting for ranges: debug_strings",,Mark de Wever,|Complete|,Clang 17 "`P2693R1 `__","Formatting ``thread::id`` and ``stacktrace``" `[thread.thread.id] `_,"Formatting ``thread::id``",,Mark de Wever,|Complete|,Clang 17 `[stacktrace.format] `_,"Formatting ``stacktrace``",A ```` implementation,Mark de Wever,, diff --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h --- a/libcxx/include/__format/range_default_formatter.h +++ b/libcxx/include/__format/range_default_formatter.h @@ -14,6 +14,7 @@ # pragma GCC system_header #endif +#include <__algorithm/ranges_copy.h> #include <__availability> #include <__chrono/statically_widen.h> #include <__concepts/same_as.h> @@ -21,7 +22,10 @@ #include <__format/concepts.h> #include <__format/formatter.h> #include <__format/range_formatter.h> +#include <__iterator/back_insert_iterator.h> #include <__ranges/concepts.h> +#include <__ranges/data.h> +#include <__ranges/size.h> #include <__type_traits/remove_cvref.h> #include <__utility/pair.h> #include @@ -169,7 +173,40 @@ template requires(_Kp == range_format::string || _Kp == range_format::debug_string) struct _LIBCPP_TEMPLATE_VIS __range_default_formatter<_Kp, _Rp, _CharT> { - __range_default_formatter() = delete; // TODO FMT Implement +private: + // This deviates from the Standard, there the exposition only type is + // formatter, charT> underlying_; + // Using a string_view allows the format function to avoid a copy of the + // input range when it is a contigious range. + formatter, _CharT> __underlying_; + +public: + template + _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { + typename _ParseContext::iterator __i = __underlying_.parse(__ctx); + if constexpr (_Kp == range_format::debug_string) + __underlying_.set_debug_format(); + return __i; + } + + template + _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator + format(conditional_t, const _Rp&, _Rp&> __range, _FormatContext& __ctx) const { + // When the range is contiguous use a basic_string_view instead to avoid a + // copy of the underlying data. The basic_string_view formatter + // specialization is the "basic" string formatter in libc++. + if constexpr (ranges::contiguous_range<_Rp> && std::ranges::sized_range<_Rp>) + return __underlying_.format(basic_string_view<_CharT>{ranges::data(__range), ranges::size(__range)}, __ctx); + else { + // P2106's from_range has not been implemented yet. Instead use a simple + // copy operation. + // TODO FMT use basic_string's "from_range" constructor. + // return __underlying_.format(basic_string<_CharT>{from_range, __range}, __ctx); + basic_string<_CharT> __str; + std::ranges::copy(__range, back_insert_iterator{__str}); + return __underlying_.format(static_cast>(__str), __ctx); + } + } }; template diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.format.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.format.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.format.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// TODO FMT Fix this test using GCC, it currently times out. +// UNSUPPORTED: gcc-12 + +// XFAIL: availability-fp_to_chars-missing + +// + +// template +// requires (K == range_format::string || K == range_format::debug_string) +// struct range-default-formatter +// +// template +// string format(format_string fmt, Args&&... args); +// template +// wstring format(wformat_string fmt, Args&&... args); + +#include +#include + +#include "format.functions.tests.h" +#include "test_format_string.h" +#include "test_macros.h" +#include "assert_macros.h" +#include "concat_macros.h" + +auto test = []( + std::basic_string_view expected, test_format_string fmt, Args&&... args) { + std::basic_string out = std::format(fmt, std::forward(args)...); + TEST_REQUIRE(out == expected, + TEST_WRITE_CONCATENATED( + "\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n')); +}; + +auto test_exception = [](std::string_view, std::basic_string_view, Args&&...) { + // After P2216 most exceptions thrown by std::format become ill-formed. + // Therefore this tests does nothing. +}; + +int main(int, char**) { + format_tests(test, test_exception); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + format_tests(test, test_exception); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.tests.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.tests.h @@ -0,0 +1,439 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSTR_FORMAT_FUNCTIONS_TESTS_H +#define TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSTR_FORMAT_FUNCTIONS_TESTS_H + +#include +#include +#include + +#include "format.functions.common.h" +#include "make_string.h" +#include "platform_support.h" // locale name macros +#include "test_macros.h" + +// +// Types +// + +template +class test_range_format_string { +public: + explicit test_range_format_string(Container str) : str_(std::move(str)) {} + + typename Container::const_iterator begin() const { return str_.begin(); } + typename Container::const_iterator end() const { return str_.end(); } + +private: + Container str_; +}; + +template +constexpr std::range_format std::format_kind> = std::range_format::string; + +template +class test_range_format_debug_string { +public: + explicit test_range_format_debug_string(Container str) : str_(std::move(str)) {} + + typename Container::const_iterator begin() const { return str_.begin(); } + typename Container::const_iterator end() const { return str_.end(); } + +private: + Container str_; +}; + +template +constexpr std::range_format std::format_kind> = + std::range_format::debug_string; + +// +// String +// + +template +void test_string(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV("hello"), SV("{}"), input); + + // *** align-fill & width *** + check(SV("hello "), SV("{:10}"), input); + check(SV("hello*****"), SV("{:*<10}"), input); + check(SV("__hello___"), SV("{:_^10}"), input); + check(SV(":::::hello"), SV("{::>10}"), input); + + check(SV("hello "), SV("{:{}}"), input, 10); + check(SV("hello*****"), SV("{:*<{}}"), input, 10); + check(SV("__hello___"), SV("{:_^{}}"), input, 10); + check(SV(":::::hello"), SV("{::>{}}"), input, 10); + + check_exception("The format-spec fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{:{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check(SV("hel"), SV("{:.3}"), input); + check(SV("hel"), SV("{:.{}}"), input, 3); + + check(SV("hel "), SV("{:5.3}"), input); + check(SV("hel "), SV("{:{}.{}}"), input, 5, 3); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** type *** + check(SV("hello"), SV("{:s}"), input); + check(SV("\"hello\""), SV("{:?}"), input); + for (std::basic_string_view fmt : fmt_invalid_types("s?")) + check_exception("The format-spec type has a type not supported for a string argument", fmt, input); +} + +template +void test_string(TestFunction check, ExceptionTest check_exception) { + // libc++ uses different containers for contiguous and non-contiguous ranges. + std::basic_string input = STR("hello"); + test_string(check, check_exception, test_range_format_string>{input}); + test_string(check, check_exception, test_range_format_string>{input}); + test_string( + check, check_exception, test_range_format_string>{std::list{input.begin(), input.end()}}); +} + +// +// String range +// + +template +void test_range_string(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV(R"([Hello, world])"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV(R"([Hello, world] )"), SV("{:19}"), input); + check(SV(R"([Hello, world]*****)"), SV("{:*<19}"), input); + check(SV(R"(__[Hello, world]___)"), SV("{:_^19}"), input); + check(SV(R"(#####[Hello, world])"), SV("{:#>19}"), input); + + check(SV(R"([Hello, world] )"), SV("{:{}}"), input, 19); + check(SV(R"([Hello, world]*****)"), SV("{:*<{}}"), input, 19); + check(SV(R"(__[Hello, world]___)"), SV("{:_^{}}"), input, 19); + check(SV(R"(#####[Hello, world])"), SV("{:#>{}}"), input, 19); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV(R"(_Hello, world_)"), SV("{:_^14n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV(R"([Hello , world ])"), SV("{::8}"), input); + check(SV(R"([Hello***, world***])"), SV("{::*<8}"), input); + check(SV(R"([_Hello__, _world__])"), SV("{::_^8}"), input); + check(SV(R"([:::Hello, :::world])"), SV("{:::>8}"), input); + + check(SV(R"([Hello , world ])"), SV("{::{}}"), input, 8); + check(SV(R"([Hello***, world***])"), SV("{::*<{}}"), input, 8); + check(SV(R"([_Hello__, _world__])"), SV("{::_^{}}"), input, 8); + check(SV(R"([:::Hello, :::world])"), SV("{:::>{}}"), input, 8); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check(SV(R"([Hel, wor])"), SV("{::.3}"), input); + + check(SV(R"([Hel, wor])"), SV("{::.{}}"), input, 3); + + check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("s?")) + check_exception("The format-spec type has a type not supported for a string argument", fmt, input); + + // ***** Both have a format-spec + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^25::>8}"), input); + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>8}"), input, 25); + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>{}}"), input, 25, 8); + + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^25::>8}"), input); + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>8}"), input, 25); + check(SV(R"(^^[:::Hello, :::world]^^^)"), SV("{:^^{}::>{}}"), input, 25, 8); + + check_exception("Argument index out of bounds", SV("{:^^{}::>8}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 25); +} + +template +void test_range_string(TestFunction check, ExceptionTest check_exception) { + // libc++ uses different containers for contiguous and non-contiguous ranges. + std::array input{STR("Hello"), STR("world")}; + test_range_string( + check, + check_exception, + std::array{test_range_format_string>{input[0]}, + test_range_format_string>{input[1]}}); + test_range_string( + check, + check_exception, + std::array{test_range_format_string>{input[0]}, + test_range_format_string>{input[1]}}); + test_range_string( + check, + check_exception, + std::array{test_range_format_string>{std::list{input[0].begin(), input[0].end()}}, + test_range_format_string>{std::list{input[1].begin(), input[1].end()}}}); + test_range_string( + check, + check_exception, + std::list{test_range_format_string>{std::list{input[0].begin(), input[0].end()}}, + test_range_format_string>{std::list{input[1].begin(), input[1].end()}}}); +} + +// +// Debug string +// + +template +void test_debug_string(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV("\"hello\""), SV("{}"), input); + + // *** align-fill & width *** + check(SV("\"hello\" "), SV("{:12}"), input); + check(SV("\"hello\"*****"), SV("{:*<12}"), input); + check(SV("__\"hello\"___"), SV("{:_^12}"), input); + check(SV(":::::\"hello\""), SV("{::>12}"), input); + + check(SV("\"hello\" "), SV("{:{}}"), input, 12); + check(SV("\"hello\"*****"), SV("{:*<{}}"), input, 12); + check(SV("__\"hello\"___"), SV("{:_^{}}"), input, 12); + check(SV(":::::\"hello\""), SV("{::>{}}"), input, 12); + + check_exception("The format-spec fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{:{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check(SV("\"he"), SV("{:.3}"), input); + check(SV("\"he"), SV("{:.{}}"), input, 3); + + check(SV("\"he "), SV("{:5.3}"), input); + check(SV("\"he "), SV("{:{}.{}}"), input, 5, 3); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** type *** + check(SV("\"hello\""), SV("{:s}"), input); // escape overrides the type option s + check(SV("\"hello\""), SV("{:?}"), input); + for (std::basic_string_view fmt : fmt_invalid_types("s?")) + check_exception("The format-spec type has a type not supported for a string argument", fmt, input); +} + +template +void test_debug_string(TestFunction check, ExceptionTest check_exception) { + // libc++ uses different containers for contiguous and non-contiguous ranges. + std::basic_string input = STR("hello"); + test_debug_string(check, check_exception, test_range_format_debug_string>{input}); + test_debug_string( + check, check_exception, test_range_format_debug_string>{input}); + test_debug_string( + check, + check_exception, + test_range_format_debug_string>{std::list{input.begin(), input.end()}}); +} + +// +// Debug string range +// + +template +void test_range_debug_string(TestFunction check, ExceptionTest check_exception, auto&& input) { + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV(R"(["Hello", "world"] )"), SV("{:23}"), input); + check(SV(R"(["Hello", "world"]*****)"), SV("{:*<23}"), input); + check(SV(R"(__["Hello", "world"]___)"), SV("{:_^23}"), input); + check(SV(R"(#####["Hello", "world"])"), SV("{:#>23}"), input); + + check(SV(R"(["Hello", "world"] )"), SV("{:{}}"), input, 23); + check(SV(R"(["Hello", "world"]*****)"), SV("{:*<{}}"), input, 23); + check(SV(R"(__["Hello", "world"]___)"), SV("{:_^{}}"), input, 23); + check(SV(R"(#####["Hello", "world"])"), SV("{:#>{}}"), input, 23); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV(R"(_"Hello", "world"_)"), SV("{:_^18n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV(R"(["Hello" , "world" ])"), SV("{::10}"), input); + check(SV(R"(["Hello"***, "world"***])"), SV("{::*<10}"), input); + check(SV(R"([_"Hello"__, _"world"__])"), SV("{::_^10}"), input); + check(SV(R"([:::"Hello", :::"world"])"), SV("{:::>10}"), input); + + check(SV(R"(["Hello" , "world" ])"), SV("{::{}}"), input, 10); + check(SV(R"(["Hello"***, "world"***])"), SV("{::*<{}}"), input, 10); + check(SV(R"([_"Hello"__, _"world"__])"), SV("{::_^{}}"), input, 10); + check(SV(R"([:::"Hello", :::"world"])"), SV("{:::>{}}"), input, 10); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check(SV(R"(["He, "wo])"), SV("{::.3}"), input); + + check(SV(R"(["He, "wo])"), SV("{::.{}}"), input, 3); + + check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("s?")) + check_exception("The format-spec type has a type not supported for a string argument", fmt, input); + + // ***** Both have a format-spec + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^29::>10}"), input); + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^{}::>10}"), input, 29); + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^{}::>{}}"), input, 29, 10); + + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^29::>10}"), input); + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^{}::>10}"), input, 29); + check(SV(R"(^^[:::"Hello", :::"world"]^^^)"), SV("{:^^{}::>{}}"), input, 29, 10); + + check_exception("Argument index out of bounds", SV("{:^^{}::>10}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 29); +} + +template +void test_range_debug_string(TestFunction check, ExceptionTest check_exception) { + // libc++ uses different containers for contiguous and non-contiguous ranges. + std::array input{STR("Hello"), STR("world")}; + test_range_debug_string( + check, + check_exception, + std::array{test_range_format_debug_string>{input[0]}, + test_range_format_debug_string>{input[1]}}); + test_range_debug_string( + check, + check_exception, + std::array{test_range_format_debug_string>{input[0]}, + test_range_format_debug_string>{input[1]}}); + test_range_debug_string( + check, + check_exception, + std::array{test_range_format_debug_string>{std::list{input[0].begin(), input[0].end()}}, + test_range_format_debug_string>{std::list{input[1].begin(), input[1].end()}}}); + test_range_debug_string( + check, + check_exception, + std::list{test_range_format_debug_string>{std::list{input[0].begin(), input[0].end()}}, + test_range_format_debug_string>{std::list{input[1].begin(), input[1].end()}}}); +} + +// +// Driver +// + +template +void format_tests(TestFunction check, ExceptionTest check_exception) { + test_string(check, check_exception); + test_range_string(check, check_exception); + + test_debug_string(check, check_exception); + test_range_debug_string(check, check_exception); +} + +#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSTR_FORMAT_FUNCTIONS_TESTS_H diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.vformat.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.vformat.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.functions.vformat.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// TODO FMT Fix this test using GCC, it currently times out. +// UNSUPPORTED: gcc-12 + +// XFAIL: availability-fp_to_chars-missing + +// + +// template +// requires (K == range_format::string || K == range_format::debug_string) +// struct range-default-formatter +// +// string vformat(string_view fmt, format_args args); +// wstring vformat(wstring_view fmt, wformat_args args); + +#include +#include + +#include "format.functions.tests.h" +#include "test_macros.h" +#include "assert_macros.h" +#include "concat_macros.h" + +auto test = []( + std::basic_string_view expected, std::basic_string_view fmt, Args&&... args) { + std::basic_string out = std::vformat(fmt, std::make_format_args>(args...)); + TEST_REQUIRE(out == expected, + TEST_WRITE_CONCATENATED( + "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n')); +}; + +auto test_exception = + []( + [[maybe_unused]] std::string_view what, + [[maybe_unused]] std::basic_string_view fmt, + [[maybe_unused]] Args&&... args) { + TEST_VALIDATE_EXCEPTION( + std::format_error, + [&]([[maybe_unused]] const std::format_error& e) { + TEST_LIBCPP_REQUIRE( + e.what() == what, + TEST_WRITE_CONCATENATED( + "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n')); + }, + TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...))); + }; + +int main(int, char**) { + format_tests(test, test_exception); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + format_tests(test, test_exception); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/format.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// TODO FMT Investigate why this fails. +// UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} + +// + +// template +// requires (K == range_format::string || K == range_format::debug_string) +// struct range-default-formatter + +// template +// typename FormatContext::iterator +// format(const T& ref, FormatContext& ctx) const; + +// Note this tests the basics of this function. It's tested in more detail in +// the format.functions test. + +#include +#include +#include + +#include "format.functions.tests.h" +#include "test_format_context.h" +#include "test_macros.h" + +template +void test_format(StringViewT expected, ArgT arg) { + using CharT = typename StringViewT::value_type; + using String = std::basic_string; + using OutIt = std::back_insert_iterator; + using FormatCtxT = std::basic_format_context; + + std::formatter formatter; + + String result; + OutIt out = std::back_inserter(result); + FormatCtxT format_ctx = test_format_context_create(out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_fmt() { + test_format(SV("hello"), test_range_format_string>{STR("hello")}); + test_format(SV("hello"), test_range_format_debug_string>{STR("hello")}); +} + +void test() { + test_fmt(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_fmt(); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/parse.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/parse.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtstr/parse.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// TODO FMT Investigate why this fails. +// UNSUPPORTED: stdlib=apple-libc++ && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}} + +// + +// template +// requires (K == range_format::string || K == range_format::debug_string) +// struct range-default-formatter + +// template +// constexpr typename ParseContext::iterator +// parse(ParseContext& ctx); + +// Note this tests the basics of this function. It's tested in more detail in +// the format.functions test. + +#include +#include +#include + +#include "format.functions.tests.h" +#include "test_format_context.h" +#include "test_macros.h" + +template +constexpr void test_parse(StringViewT fmt) { + using CharT = typename StringViewT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + FormatterT formatter; + static_assert(std::semiregular); + + std::same_as auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); +} + +template +constexpr void test_formatters(StringViewT fmt) { + using CharT = typename StringViewT::value_type; + test_parse>, CharT>>(fmt); + test_parse>, CharT>>(fmt); +} + +template +constexpr void test_char_type() { + test_formatters(SV("")); + test_formatters(SV("}")); +} + +constexpr bool test() { + test_char_type(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_char_type(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}