brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.3 KiB · a6a6635 Raw
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