xref: /llvm-project/libcxx/test/support/format.functions.common.h (revision 09e3a360581dc36d0820d3fb6da9bd7cfed87b5d)
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