xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/support/format.functions.common.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 //===----------------------------------------------------------------------===//
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 
8 #ifndef TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
9 #define TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
10 
11 // Contains the common part of the formatter tests for different papers.
12 
13 #include <algorithm>
14 #include <cctype>
15 #include <charconv>
16 #include <cstddef>
17 #include <cstdlib>
18 #include <format>
19 #include <ranges>
20 #include <string>
21 #include <string_view>
22 #include <vector>
23 
24 #include "make_string.h"
25 
26 #define STR(S) MAKE_STRING(CharT, S)
27 #define SV(S) MAKE_STRING_VIEW(CharT, S)
28 #define CSTR(S) MAKE_CSTRING(CharT, S)
29 
30 template <class T>
31 struct context {};
32 
33 template <>
34 struct context<char> {
35   using type = std::format_context;
36 };
37 
38 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
39 template <>
40 struct context<wchar_t> {
41   using type = std::wformat_context;
42 };
43 #endif
44 
45 template <class T>
46 using context_t = typename context<T>::type;
47 
48 // A user-defined type used to test the handle formatter.
49 enum class status : std::uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 };
50 
51 // The formatter for a user-defined type used to test the handle formatter.
52 template <class CharT>
53 struct std::formatter<status, CharT> {
54   // During the 2023 Issaquah meeting LEWG made it clear a formatter is
55   // required to call its parse function. LWG3892 Adds the wording for that
56   // requirement. Therefore this formatter is initialized in an invalid state.
57   // A call to parse sets it in a valid state and a call to format validates
58   // the state.
59   int type = -1;
60 
61   constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) {
62     auto begin = parse_ctx.begin();
63     auto end   = parse_ctx.end();
64     type       = 0;
65     if (begin == end)
66       return begin;
67 
68     switch (*begin) {
69     case CharT('x'):
70       break;
71     case CharT('X'):
72       type = 1;
73       break;
74     case CharT('s'):
75       type = 2;
76       break;
77     case CharT('}'):
78       return begin;
79     default:
80       throw_format_error("The type option contains an invalid value for a status formatting argument");
81     }
82 
83     ++begin;
84     if (begin != end && *begin != CharT('}'))
85       throw_format_error("The format specifier should consume the input or end with a '}'");
86 
87     return begin;
88   }
89 
90   template <class Out>
91   auto format(status s, basic_format_context<Out, CharT>& ctx) const -> decltype(ctx.out()) {
92     const char* names[] = {"foo", "bar", "foobar"};
93     char buffer[7];
94     const char* begin = names[0];
95     const char* end = names[0];
96     switch (type) {
97     case -1:
98       throw_format_error("The formatter's parse function has not been called.");
99 
100     case 0:
101       begin = buffer;
102       buffer[0] = '0';
103       buffer[1] = 'x';
104       end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr;
105       buffer[6] = '\0';
106       break;
107 
108     case 1:
109       begin = buffer;
110       buffer[0] = '0';
111       buffer[1] = 'X';
112       end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr;
113       std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) {
114         return static_cast<char>(std::toupper(c)); });
115       buffer[6] = '\0';
116       break;
117 
118     case 2:
119       switch (s) {
120       case status::foo:
121         begin = names[0];
122         break;
123       case status::bar:
124         begin = names[1];
125         break;
126       case status::foobar:
127         begin = names[2];
128         break;
129       }
130       end = begin + strlen(begin);
131       break;
132     }
133 
134     return std::copy(begin, end, ctx.out());
135   }
136 
137 private:
138   [[noreturn]] void throw_format_error([[maybe_unused]] const char* s) const {
139 #ifndef TEST_HAS_NO_EXCEPTIONS
140     throw std::format_error(s);
141 #else
142     std::abort();
143 #endif
144   }
145 };
146 
147 struct parse_call_validator {
148   struct parse_function_not_called {};
149 
150   friend constexpr auto operator<=>(const parse_call_validator& lhs, const parse_call_validator& rhs) {
151     return &lhs <=> &rhs;
152   }
153 };
154 
155 // The formatter for a user-defined type used to test the handle formatter.
156 //
157 // Like std::formatter<status, CharT> this formatter validates that parse is
158 // called. This formatter is intended to be used when the formatter's parse is
159 // called directly and not with format. In that case the format-spec does not
160 // require a terminating }. The tests must be written in a fashion where this
161 // formatter is always called with an empty format-spec. This requirement
162 // allows testing of certain code paths that are never reached by using a
163 // well-formed format-string in the format functions.
164 template <class CharT>
165 struct std::formatter<parse_call_validator, CharT> {
166   bool parse_called{false};
167 
168   constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) {
169     auto begin = parse_ctx.begin();
170     auto end   = parse_ctx.end();
171     assert(begin == end);
172     parse_called = true;
173     return begin;
174   }
175 
176   auto format(parse_call_validator, auto& ctx) const -> decltype(ctx.out()) {
177     if (!parse_called)
178       throw_error<parse_call_validator::parse_function_not_called>();
179     return ctx.out();
180   }
181 
182 private:
183   template <class T>
184   [[noreturn]] void throw_error() const {
185 #ifndef TEST_HAS_NO_EXCEPTIONS
186     throw T{};
187 #else
188     std::abort();
189 #endif
190   }
191 };
192 
193 // Creates format string for the invalid types.
194 //
195 // valid contains a list of types that are valid.
196 // - The type ?s is the only type requiring 2 characters, use S for that type.
197 // - Whether n is a type or not depends on the context, is is always used.
198 //
199 // The return value is a collection of basic_strings, instead of
200 // basic_string_views since the values are temporaries.
201 namespace detail {
202 template <class CharT, std::size_t N>
203 std::basic_string<CharT> get_colons() {
204   static std::basic_string<CharT> result(N, CharT(':'));
205   return result;
206 }
207 
208 constexpr std::string_view get_format_types() {
209   return "aAbBcdeEfFgGopPsxX"
210 #if TEST_STD_VER > 20
211          "?"
212 #endif
213       ;
214 }
215 
216 template <class CharT, /*format_types types,*/ size_t N>
217 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
218   // std::ranges::to is not available in C++20.
219   std::vector<std::basic_string<CharT>> result;
220   std::ranges::copy(
221       get_format_types() | std::views::filter([&](char type) { return valid.find(type) == std::string_view::npos; }) |
222           std::views::transform([&](char type) { return std::format(SV("{{{}{}}}"), get_colons<CharT, N>(), type); }),
223       std::back_inserter(result));
224   return result;
225 }
226 
227 } // namespace detail
228 
229 // Creates format string for the invalid types.
230 //
231 // valid contains a list of types that are valid.
232 //
233 // The return value is a collection of basic_strings, instead of
234 // basic_string_views since the values are temporaries.
235 template <class CharT>
236 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
237   return detail::fmt_invalid_types<CharT, 1>(valid);
238 }
239 
240 // Like fmt_invalid_types but when the format spec is for an underlying formatter.
241 template <class CharT>
242 std::vector<std::basic_string<CharT>> fmt_invalid_nested_types(std::string_view valid) {
243   return detail::fmt_invalid_types<CharT, 2>(valid);
244 }
245 
246 #endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
247