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