| //===----------------------------------------------------------------------===// |
| // 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 |
| // XFAIL: LIBCXX-AIX-FIXME |
| |
| // <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; |
| } |