blob: 930a6b43ef722fc13dad1ad263f4335e2777e8ab [file] [log] [blame]
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-format
// <format>
// Tests the parsing of the format string as specified in [format.string.std].
// It validates whether the std-format-spec is valid for a string type.
#include <format>
#include <cassert>
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
# include <iostream>
#endif
#include "test_macros.h"
#include "make_string.h"
#include "test_exception.h"
#define CSTR(S) MAKE_CSTRING(CharT, S)
using namespace std::__format_spec;
template <class CharT>
using Parser = __parser_string<CharT>;
template <class CharT>
struct Expected {
CharT fill = CharT(' ');
_Flags::_Alignment alignment = _Flags::_Alignment::__left;
uint32_t width = 0;
bool width_as_arg = false;
uint32_t precision = std::__format::__number_max;
bool precision_as_arg = true;
_Flags::_Type type = _Flags::_Type::__default;
};
template <class CharT>
constexpr void test(Expected<CharT> expected, size_t size,
std::basic_string_view<CharT> fmt) {
// Initialize parser with sufficient arguments to avoid the parsing to fail
// due to insufficient arguments.
std::basic_format_parse_context<CharT> parse_ctx(fmt,
std::__format::__number_max);
auto begin = parse_ctx.begin();
auto end = parse_ctx.end();
Parser<CharT> parser;
auto it = parser.parse(parse_ctx);
assert(begin == parse_ctx.begin());
assert(end == parse_ctx.end());
assert(begin + size == it);
assert(parser.__fill == expected.fill);
assert(parser.__alignment == expected.alignment);
assert(parser.__sign == _Flags::_Sign::__default);
assert(parser.__alternate_form == false);
assert(parser.__zero_padding == false);
assert(parser.__width == expected.width);
assert(parser.__width_as_arg == expected.width_as_arg);
assert(parser.__precision == expected.precision);
assert(parser.__precision_as_arg == expected.precision_as_arg);
assert(parser.__locale_specific_form == false);
assert(parser.__type == expected.type);
}
template <class CharT>
constexpr void test(Expected<CharT> expected, size_t size, const CharT* f) {
// The format-spec is valid if completely consumed or terminates at a '}'.
// The valid inputs all end with a '}'. The test is executed twice:
// - first with the terminating '}',
// - second consuming the entire input.
std::basic_string_view<CharT> fmt{f};
assert(fmt.back() == CharT('}') && "Pre-condition failure");
test(expected, size, fmt);
fmt.remove_suffix(1);
test(expected, size, fmt);
}
template <class CharT>
constexpr void test() {
Parser<CharT> parser;
assert(parser.__fill == CharT(' '));
assert(parser.__alignment == _Flags::_Alignment::__left);
assert(parser.__sign == _Flags::_Sign::__default);
assert(parser.__alternate_form == false);
assert(parser.__zero_padding == false);
assert(parser.__width == 0);
assert(parser.__width_as_arg == false);
assert(parser.__precision == std::__format::__number_max);
assert(parser.__precision_as_arg == true);
assert(parser.__locale_specific_form == false);
assert(parser.__type == _Flags::_Type::__default);
test({}, 0, CSTR("}"));
// *** Align-fill ***
test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}"));
test({.alignment = _Flags::_Alignment::__center}, 1, "^}");
test({.alignment = _Flags::_Alignment::__right}, 1, ">}");
test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2,
CSTR("L<}"));
test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2,
CSTR("#^}"));
test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2,
CSTR("0>}"));
test_exception<Parser<CharT>>(
"The format-spec fill field contains an invalid character", CSTR("{<"));
test_exception<Parser<CharT>>(
"The format-spec fill field contains an invalid character", CSTR("}<"));
// *** Sign ***
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR("+"));
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR("-"));
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR(" "));
// *** Alternate form ***
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR("#"));
// *** Zero padding ***
test_exception<Parser<CharT>>(
"A format-spec width field shouldn't have a leading zero", CSTR("0"));
// *** Width ***
test({.width = 0, .width_as_arg = false}, 0, CSTR("}"));
test({.width = 1, .width_as_arg = false}, 1, CSTR("1}"));
test({.width = 10, .width_as_arg = false}, 2, CSTR("10}"));
test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}"));
test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}"));
test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}"));
test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}"));
test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}"));
test_exception<Parser<CharT>>(
"A format-spec width field shouldn't have a leading zero", CSTR("00"));
static_assert(std::__format::__number_max == 2'147'483'647,
"Update the assert and the test.");
test({.width = 2'147'483'647, .width_as_arg = false}, 10,
CSTR("2147483647}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large", CSTR("2147483648"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large", CSTR("5000000000"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large", CSTR("10000000000"));
test_exception<Parser<CharT>>("End of input while parsing format-spec arg-id",
CSTR("{"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR("{0"));
test_exception<Parser<CharT>>(
"The arg-id of the format-spec starts with an invalid character",
CSTR("{a"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR("{1"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR("{9"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR("{9:"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR("{9a"));
static_assert(std::__format::__number_max == 2'147'483'647,
"Update the assert and the test.");
// Note the static_assert tests whether the arg-id is valid.
// Therefore the following should be true arg-id < __format::__number_max.
test({.width = 2'147'483'646, .width_as_arg = true}, 12,
CSTR("{2147483646}}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR("{2147483648}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR("{5000000000}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR("{10000000000}"));
// *** Precision ***
test({.precision = 0, .precision_as_arg = false}, 2, CSTR(".0}"));
test({.precision = 1, .precision_as_arg = false}, 2, CSTR(".1}"));
test({.precision = 10, .precision_as_arg = false}, 3, CSTR(".10}"));
test({.precision = 1000, .precision_as_arg = false}, 5, CSTR(".1000}"));
test({.precision = 1000000, .precision_as_arg = false}, 8, CSTR(".1000000}"));
test({.precision = 0, .precision_as_arg = true}, 3, CSTR(".{}}"));
test({.precision = 0, .precision_as_arg = true}, 4, CSTR(".{0}}"));
test({.precision = 1, .precision_as_arg = true}, 4, CSTR(".{1}}"));
test_exception<Parser<CharT>>(
"A format-spec precision field shouldn't have a leading zero",
CSTR(".00"));
test_exception<Parser<CharT>>(
"A format-spec precision field shouldn't have a leading zero",
CSTR(".01"));
test_exception<Parser<CharT>>(
"The format-spec precision field doesn't contain a value or arg-id",
CSTR(".a"));
test_exception<Parser<CharT>>(
"The format-spec precision field doesn't contain a value or arg-id",
CSTR(".:"));
static_assert(std::__format::__number_max == 2'147'483'647,
"Update the assert and the test.");
test({.precision = 2'147'483'647, .precision_as_arg = false}, 11,
CSTR(".2147483647}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large", CSTR(".2147483648"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large", CSTR(".5000000000"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR(".10000000000"));
test_exception<Parser<CharT>>("End of input while parsing format-spec arg-id",
CSTR(".{"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR(".{0"));
test_exception<Parser<CharT>>(
"The arg-id of the format-spec starts with an invalid character",
CSTR(".{a"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR(".{1"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR(".{9"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR(".{9:"));
test_exception<Parser<CharT>>(
"A format-spec arg-id should terminate at a '}'", CSTR(".{9a"));
static_assert(std::__format::__number_max == 2'147'483'647,
"Update the assert and the test.");
// Note the static_assert tests whether the arg-id is valid.
// Therefore the following should be true arg-id < __format::__number_max.
test({.precision = 2'147'483'646, .precision_as_arg = true}, 13,
CSTR(".{2147483646}}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR(".{2147483648}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR(".{5000000000}"));
test_exception<Parser<CharT>>(
"The numeric value of the format-spec is too large",
CSTR(".{10000000000}"));
// *** Width & Precision ***
test({.width = 1,
.width_as_arg = false,
.precision = 0,
.precision_as_arg = false},
3, CSTR("1.0}"));
test({.width = 0,
.width_as_arg = true,
.precision = 1,
.precision_as_arg = true},
5, CSTR("{}.{}}"));
test({.width = 10,
.width_as_arg = true,
.precision = 9,
.precision_as_arg = true},
8, CSTR("{10}.{9}}"));
// *** Locale-specific form ***
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR("L"));
// *** Type ***
{
const char* unsuported_type =
"The format-spec type has a type not supported for a string argument";
const char* not_a_type =
"The format-spec should consume the input or end with a '}'";
test_exception<Parser<CharT>>(unsuported_type, CSTR("A}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("B}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("C}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("D}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("E}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("F}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("G}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("H}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("I}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("J}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("K}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("L}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("M}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("N}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("O}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("P}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("Q}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("R}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("S}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("T}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("U}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("V}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("W}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("X}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("Y}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("Z}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("a}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("b}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("c}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("d}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("e}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("f}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("g}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("h}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("i}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("j}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("k}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("l}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("m}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("n}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("o}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("p}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("q}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("r}"));
test({.type = _Flags::_Type::__string}, 1, CSTR("s}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("t}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("u}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("v}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("w}"));
test_exception<Parser<CharT>>(unsuported_type, CSTR("x}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("y}"));
test_exception<Parser<CharT>>(not_a_type, CSTR("z}"));
}
// **** General ***
test_exception<Parser<CharT>>(
"The format-spec should consume the input or end with a '}'", CSTR("ss"));
}
constexpr bool test() {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
#ifndef _LIBCPP_HAS_NO_CHAR8_T
test<char8_t>();
#endif
#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS
test<char16_t>();
test<char32_t>();
#endif
return true;
}
int main(int, char**) {
#ifndef _WIN32
// Make sure the parsers match the expectations. The layout of the
// subobjects is chosen to minimize the size required.
LIBCPP_STATIC_ASSERT(sizeof(Parser<char>) == 3 * sizeof(uint32_t));
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
LIBCPP_STATIC_ASSERT(
sizeof(Parser<wchar_t>) ==
(sizeof(wchar_t) <= 2 ? 3 * sizeof(uint32_t) : 4 * sizeof(uint32_t)));
#endif
#endif // _WIN32
test();
static_assert(test());
return 0;
}