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