149d4fee9SMark de Wever //===----------------------------------------------------------------------===// 26a54dfbfSLouis Dionne // 349d4fee9SMark de Wever // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 449d4fee9SMark de Wever // See https://llvm.org/LICENSE.txt for license information. 549d4fee9SMark de Wever // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 649d4fee9SMark de Wever // 749d4fee9SMark de Wever //===----------------------------------------------------------------------===// 849d4fee9SMark de Wever 949d4fee9SMark de Wever #ifndef TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H 1049d4fee9SMark de Wever #define TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H 1149d4fee9SMark de Wever 1249d4fee9SMark de Wever // Contains the common part of the formatter tests for different papers. 1349d4fee9SMark de Wever 14bc21af6aSMark de Wever #include <algorithm> 1578f17b2aSKonstantin Varlamov #include <cctype> 16bc21af6aSMark de Wever #include <charconv> 17aa9f14a5SKonstantin Varlamov #include <cstddef> 18*09e3a360SLouis Dionne #include <cstdint> 19aa9f14a5SKonstantin Varlamov #include <cstdlib> 2049d4fee9SMark de Wever #include <format> 2122e8525dSMark de Wever #include <ranges> 22af5fc4b4SMark de Wever #include <string_view> 23*09e3a360SLouis Dionne #include <string> 24af5fc4b4SMark de Wever #include <vector> 2549d4fee9SMark de Wever 2649d4fee9SMark de Wever #include "make_string.h" 2749d4fee9SMark de Wever 2849d4fee9SMark de Wever #define STR(S) MAKE_STRING(CharT, S) 2949d4fee9SMark de Wever #define SV(S) MAKE_STRING_VIEW(CharT, S) 3049d4fee9SMark de Wever #define CSTR(S) MAKE_CSTRING(CharT, S) 3149d4fee9SMark de Wever 3249d4fee9SMark de Wever template <class T> 3349d4fee9SMark de Wever struct context {}; 3449d4fee9SMark de Wever 3549d4fee9SMark de Wever template <> 3649d4fee9SMark de Wever struct context<char> { 3749d4fee9SMark de Wever using type = std::format_context; 3849d4fee9SMark de Wever }; 3949d4fee9SMark de Wever 4049d4fee9SMark de Wever #ifndef TEST_HAS_NO_WIDE_CHARACTERS 4149d4fee9SMark de Wever template <> 4249d4fee9SMark de Wever struct context<wchar_t> { 4349d4fee9SMark de Wever using type = std::wformat_context; 4449d4fee9SMark de Wever }; 4549d4fee9SMark de Wever #endif 4649d4fee9SMark de Wever 4749d4fee9SMark de Wever template <class T> 4849d4fee9SMark de Wever using context_t = typename context<T>::type; 4949d4fee9SMark de Wever 5049d4fee9SMark de Wever // A user-defined type used to test the handle formatter. 51bd5d0feeSMark de Wever enum class status : std::uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 }; 5249d4fee9SMark de Wever 5349d4fee9SMark de Wever // The formatter for a user-defined type used to test the handle formatter. 5449d4fee9SMark de Wever template <class CharT> 5549d4fee9SMark de Wever struct std::formatter<status, CharT> { 569b43aedeSMark de Wever // During the 2023 Issaquah meeting LEWG made it clear a formatter is 579b43aedeSMark de Wever // required to call its parse function. LWG3892 Adds the wording for that 589b43aedeSMark de Wever // requirement. Therefore this formatter is initialized in an invalid state. 599b43aedeSMark de Wever // A call to parse sets it in a valid state and a call to format validates 609b43aedeSMark de Wever // the state. 619b43aedeSMark de Wever int type = -1; 6249d4fee9SMark de Wever 6349d4fee9SMark de Wever constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) { 6449d4fee9SMark de Wever auto begin = parse_ctx.begin(); 6549d4fee9SMark de Wever auto end = parse_ctx.end(); 669b43aedeSMark de Wever type = 0; 6749d4fee9SMark de Wever if (begin == end) 6849d4fee9SMark de Wever return begin; 6949d4fee9SMark de Wever 7049d4fee9SMark de Wever switch (*begin) { 7149d4fee9SMark de Wever case CharT('x'): 7249d4fee9SMark de Wever break; 7349d4fee9SMark de Wever case CharT('X'): 7449d4fee9SMark de Wever type = 1; 7549d4fee9SMark de Wever break; 7649d4fee9SMark de Wever case CharT('s'): 7749d4fee9SMark de Wever type = 2; 7849d4fee9SMark de Wever break; 7949d4fee9SMark de Wever case CharT('}'): 8049d4fee9SMark de Wever return begin; 8149d4fee9SMark de Wever default: 82402eb2efSMark de Wever throw_format_error("The type option contains an invalid value for a status formatting argument"); 8349d4fee9SMark de Wever } 8449d4fee9SMark de Wever 8549d4fee9SMark de Wever ++begin; 8649d4fee9SMark de Wever if (begin != end && *begin != CharT('}')) 87402eb2efSMark de Wever throw_format_error("The format specifier should consume the input or end with a '}'"); 8849d4fee9SMark de Wever 8949d4fee9SMark de Wever return begin; 9049d4fee9SMark de Wever } 9149d4fee9SMark de Wever 9249d4fee9SMark de Wever template <class Out> 9349d4fee9SMark de Wever auto format(status s, basic_format_context<Out, CharT>& ctx) const -> decltype(ctx.out()) { 9449d4fee9SMark de Wever const char* names[] = {"foo", "bar", "foobar"}; 9549d4fee9SMark de Wever char buffer[7]; 9649d4fee9SMark de Wever const char* begin = names[0]; 9749d4fee9SMark de Wever const char* end = names[0]; 9849d4fee9SMark de Wever switch (type) { 999b43aedeSMark de Wever case -1: 1009b43aedeSMark de Wever throw_format_error("The formatter's parse function has not been called."); 1019b43aedeSMark de Wever 10249d4fee9SMark de Wever case 0: 10349d4fee9SMark de Wever begin = buffer; 10449d4fee9SMark de Wever buffer[0] = '0'; 10549d4fee9SMark de Wever buffer[1] = 'x'; 106bd5d0feeSMark de Wever end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr; 10749d4fee9SMark de Wever buffer[6] = '\0'; 10849d4fee9SMark de Wever break; 10949d4fee9SMark de Wever 11049d4fee9SMark de Wever case 1: 11149d4fee9SMark de Wever begin = buffer; 11249d4fee9SMark de Wever buffer[0] = '0'; 11349d4fee9SMark de Wever buffer[1] = 'X'; 114bd5d0feeSMark de Wever end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr; 11549d4fee9SMark de Wever std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) { 11649d4fee9SMark de Wever return static_cast<char>(std::toupper(c)); }); 11749d4fee9SMark de Wever buffer[6] = '\0'; 11849d4fee9SMark de Wever break; 11949d4fee9SMark de Wever 12049d4fee9SMark de Wever case 2: 12149d4fee9SMark de Wever switch (s) { 12249d4fee9SMark de Wever case status::foo: 12349d4fee9SMark de Wever begin = names[0]; 12449d4fee9SMark de Wever break; 12549d4fee9SMark de Wever case status::bar: 12649d4fee9SMark de Wever begin = names[1]; 12749d4fee9SMark de Wever break; 12849d4fee9SMark de Wever case status::foobar: 12949d4fee9SMark de Wever begin = names[2]; 13049d4fee9SMark de Wever break; 13149d4fee9SMark de Wever } 13249d4fee9SMark de Wever end = begin + strlen(begin); 13349d4fee9SMark de Wever break; 13449d4fee9SMark de Wever } 13549d4fee9SMark de Wever 13649d4fee9SMark de Wever return std::copy(begin, end, ctx.out()); 13749d4fee9SMark de Wever } 13849d4fee9SMark de Wever 13949d4fee9SMark de Wever private: 14032d8c242SMark de Wever [[noreturn]] void throw_format_error([[maybe_unused]] const char* s) const { 14149d4fee9SMark de Wever #ifndef TEST_HAS_NO_EXCEPTIONS 14249d4fee9SMark de Wever throw std::format_error(s); 14349d4fee9SMark de Wever #else 14449d4fee9SMark de Wever std::abort(); 14549d4fee9SMark de Wever #endif 14649d4fee9SMark de Wever } 14749d4fee9SMark de Wever }; 14849d4fee9SMark de Wever 1499b43aedeSMark de Wever struct parse_call_validator { 1509b43aedeSMark de Wever struct parse_function_not_called {}; 1519b43aedeSMark de Wever 1529b43aedeSMark de Wever friend constexpr auto operator<=>(const parse_call_validator& lhs, const parse_call_validator& rhs) { 1539b43aedeSMark de Wever return &lhs <=> &rhs; 1549b43aedeSMark de Wever } 1559b43aedeSMark de Wever }; 1569b43aedeSMark de Wever 1579b43aedeSMark de Wever // The formatter for a user-defined type used to test the handle formatter. 1589b43aedeSMark de Wever // 1599b43aedeSMark de Wever // Like std::formatter<status, CharT> this formatter validates that parse is 1609b43aedeSMark de Wever // called. This formatter is intended to be used when the formatter's parse is 1619b43aedeSMark de Wever // called directly and not with format. In that case the format-spec does not 1629b43aedeSMark de Wever // require a terminating }. The tests must be written in a fashion where this 1639b43aedeSMark de Wever // formatter is always called with an empty format-spec. This requirement 1649b43aedeSMark de Wever // allows testing of certain code paths that are never reached by using a 1659b43aedeSMark de Wever // well-formed format-string in the format functions. 1669b43aedeSMark de Wever template <class CharT> 1679b43aedeSMark de Wever struct std::formatter<parse_call_validator, CharT> { 1689b43aedeSMark de Wever bool parse_called{false}; 1699b43aedeSMark de Wever 1709b43aedeSMark de Wever constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) { 1719b43aedeSMark de Wever auto begin = parse_ctx.begin(); 1729b43aedeSMark de Wever auto end = parse_ctx.end(); 1739b43aedeSMark de Wever assert(begin == end); 1749b43aedeSMark de Wever parse_called = true; 1759b43aedeSMark de Wever return begin; 1769b43aedeSMark de Wever } 1779b43aedeSMark de Wever 1789b43aedeSMark de Wever auto format(parse_call_validator, auto& ctx) const -> decltype(ctx.out()) { 1799b43aedeSMark de Wever if (!parse_called) 1809b43aedeSMark de Wever throw_error<parse_call_validator::parse_function_not_called>(); 1819b43aedeSMark de Wever return ctx.out(); 1829b43aedeSMark de Wever } 1839b43aedeSMark de Wever 1849b43aedeSMark de Wever private: 1859b43aedeSMark de Wever template <class T> 1869b43aedeSMark de Wever [[noreturn]] void throw_error() const { 1879b43aedeSMark de Wever #ifndef TEST_HAS_NO_EXCEPTIONS 1889b43aedeSMark de Wever throw T{}; 1899b43aedeSMark de Wever #else 1909b43aedeSMark de Wever std::abort(); 1919b43aedeSMark de Wever #endif 1929b43aedeSMark de Wever } 1939b43aedeSMark de Wever }; 1949b43aedeSMark de Wever 19522e8525dSMark de Wever // Creates format string for the invalid types. 19622e8525dSMark de Wever // 19722e8525dSMark de Wever // valid contains a list of types that are valid. 19822e8525dSMark de Wever // - The type ?s is the only type requiring 2 characters, use S for that type. 19922e8525dSMark de Wever // - Whether n is a type or not depends on the context, is is always used. 20022e8525dSMark de Wever // 20122e8525dSMark de Wever // The return value is a collection of basic_strings, instead of 20222e8525dSMark de Wever // basic_string_views since the values are temporaries. 20322e8525dSMark de Wever namespace detail { 204fb855eb9SMark de Wever template <class CharT, std::size_t N> 20522e8525dSMark de Wever std::basic_string<CharT> get_colons() { 20622e8525dSMark de Wever static std::basic_string<CharT> result(N, CharT(':')); 20722e8525dSMark de Wever return result; 20822e8525dSMark de Wever } 20922e8525dSMark de Wever 21022e8525dSMark de Wever constexpr std::string_view get_format_types() { 211a9e5773fSMark de Wever return "aAbBcdeEfFgGopPsxX" 21222e8525dSMark de Wever #if TEST_STD_VER > 20 21322e8525dSMark de Wever "?" 21422e8525dSMark de Wever #endif 21522e8525dSMark de Wever ; 21622e8525dSMark de Wever } 21722e8525dSMark de Wever 21822e8525dSMark de Wever template <class CharT, /*format_types types,*/ size_t N> 21922e8525dSMark de Wever std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) { 22022e8525dSMark de Wever // std::ranges::to is not available in C++20. 22122e8525dSMark de Wever std::vector<std::basic_string<CharT>> result; 22222e8525dSMark de Wever std::ranges::copy( 22322e8525dSMark de Wever get_format_types() | std::views::filter([&](char type) { return valid.find(type) == std::string_view::npos; }) | 22422e8525dSMark de Wever std::views::transform([&](char type) { return std::format(SV("{{{}{}}}"), get_colons<CharT, N>(), type); }), 22522e8525dSMark de Wever std::back_inserter(result)); 22622e8525dSMark de Wever return result; 22722e8525dSMark de Wever } 22822e8525dSMark de Wever 22922e8525dSMark de Wever } // namespace detail 23022e8525dSMark de Wever 23122e8525dSMark de Wever // Creates format string for the invalid types. 23222e8525dSMark de Wever // 23322e8525dSMark de Wever // valid contains a list of types that are valid. 23422e8525dSMark de Wever // 23522e8525dSMark de Wever // The return value is a collection of basic_strings, instead of 23622e8525dSMark de Wever // basic_string_views since the values are temporaries. 23722e8525dSMark de Wever template <class CharT> 23822e8525dSMark de Wever std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) { 23922e8525dSMark de Wever return detail::fmt_invalid_types<CharT, 1>(valid); 24022e8525dSMark de Wever } 24122e8525dSMark de Wever 24222e8525dSMark de Wever // Like fmt_invalid_types but when the format spec is for an underlying formatter. 24322e8525dSMark de Wever template <class CharT> 24422e8525dSMark de Wever std::vector<std::basic_string<CharT>> fmt_invalid_nested_types(std::string_view valid) { 24522e8525dSMark de Wever return detail::fmt_invalid_types<CharT, 2>(valid); 24622e8525dSMark de Wever } 24722e8525dSMark de Wever 24849d4fee9SMark de Wever #endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H 249