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