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