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