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 <format> 17 #include <ranges> 18 #include <string> 19 #include <string_view> 20 #include <vector> 21 22 #include "make_string.h" 23 24 #define STR(S) MAKE_STRING(CharT, S) 25 #define SV(S) MAKE_STRING_VIEW(CharT, S) 26 #define CSTR(S) MAKE_CSTRING(CharT, S) 27 28 template <class T> 29 struct context {}; 30 31 template <> 32 struct context<char> { 33 using type = std::format_context; 34 }; 35 36 #ifndef TEST_HAS_NO_WIDE_CHARACTERS 37 template <> 38 struct context<wchar_t> { 39 using type = std::wformat_context; 40 }; 41 #endif 42 43 template <class T> 44 using context_t = typename context<T>::type; 45 46 // A user-defined type used to test the handle formatter. 47 enum class status : std::uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 }; 48 49 // The formatter for a user-defined type used to test the handle formatter. 50 template <class CharT> 51 struct std::formatter<status, CharT> { 52 int type = 0; 53 54 constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) { 55 auto begin = parse_ctx.begin(); 56 auto end = parse_ctx.end(); 57 if (begin == end) 58 return begin; 59 60 switch (*begin) { 61 case CharT('x'): 62 break; 63 case CharT('X'): 64 type = 1; 65 break; 66 case CharT('s'): 67 type = 2; 68 break; 69 case CharT('}'): 70 return begin; 71 default: 72 throw_format_error("The format-spec type has a type not supported for a status argument"); 73 } 74 75 ++begin; 76 if (begin != end && *begin != CharT('}')) 77 throw_format_error("The format-spec should consume the input or end with a '}'"); 78 79 return begin; 80 } 81 82 template <class Out> 83 auto format(status s, basic_format_context<Out, CharT>& ctx) const -> decltype(ctx.out()) { 84 const char* names[] = {"foo", "bar", "foobar"}; 85 char buffer[7]; 86 const char* begin = names[0]; 87 const char* end = names[0]; 88 switch (type) { 89 case 0: 90 begin = buffer; 91 buffer[0] = '0'; 92 buffer[1] = 'x'; 93 end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr; 94 buffer[6] = '\0'; 95 break; 96 97 case 1: 98 begin = buffer; 99 buffer[0] = '0'; 100 buffer[1] = 'X'; 101 end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr; 102 std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) { 103 return static_cast<char>(std::toupper(c)); }); 104 buffer[6] = '\0'; 105 break; 106 107 case 2: 108 switch (s) { 109 case status::foo: 110 begin = names[0]; 111 break; 112 case status::bar: 113 begin = names[1]; 114 break; 115 case status::foobar: 116 begin = names[2]; 117 break; 118 } 119 end = begin + strlen(begin); 120 break; 121 } 122 123 return std::copy(begin, end, ctx.out()); 124 } 125 126 private: 127 void throw_format_error(const char* s) { 128 #ifndef TEST_HAS_NO_EXCEPTIONS 129 throw std::format_error(s); 130 #else 131 (void)s; 132 std::abort(); 133 #endif 134 } 135 }; 136 137 // Creates format string for the invalid types. 138 // 139 // valid contains a list of types that are valid. 140 // - The type ?s is the only type requiring 2 characters, use S for that type. 141 // - Whether n is a type or not depends on the context, is is always used. 142 // 143 // The return value is a collection of basic_strings, instead of 144 // basic_string_views since the values are temporaries. 145 namespace detail { 146 template <class CharT, size_t N> 147 std::basic_string<CharT> get_colons() { 148 static std::basic_string<CharT> result(N, CharT(':')); 149 return result; 150 } 151 152 constexpr std::string_view get_format_types() { 153 return "aAbBcdeEfFgGopsxX" 154 #if TEST_STD_VER > 20 155 "?" 156 #endif 157 ; 158 } 159 160 template <class CharT, /*format_types types,*/ size_t N> 161 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) { 162 // std::ranges::to is not available in C++20. 163 std::vector<std::basic_string<CharT>> result; 164 std::ranges::copy( 165 get_format_types() | std::views::filter([&](char type) { return valid.find(type) == std::string_view::npos; }) | 166 std::views::transform([&](char type) { return std::format(SV("{{{}{}}}"), get_colons<CharT, N>(), type); }), 167 std::back_inserter(result)); 168 return result; 169 } 170 171 } // namespace detail 172 173 // Creates format string for the invalid types. 174 // 175 // valid contains a list of types that are valid. 176 // 177 // The return value is a collection of basic_strings, instead of 178 // basic_string_views since the values are temporaries. 179 template <class CharT> 180 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) { 181 return detail::fmt_invalid_types<CharT, 1>(valid); 182 } 183 184 // Like fmt_invalid_types but when the format spec is for an underlying formatter. 185 template <class CharT> 186 std::vector<std::basic_string<CharT>> fmt_invalid_nested_types(std::string_view valid) { 187 return detail::fmt_invalid_types<CharT, 2>(valid); 188 } 189 190 #endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H 191