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