diff --git a/libcxx/docs/Cxx2aStatusIssuesStatus.csv b/libcxx/docs/Cxx2aStatusIssuesStatus.csv --- a/libcxx/docs/Cxx2aStatusIssuesStatus.csv +++ b/libcxx/docs/Cxx2aStatusIssuesStatus.csv @@ -202,7 +202,7 @@ "`3233 `__","Broken requirements for ``shared_ptr``\ converting constructors","Prague","","" "`3237 `__","LWG 3038 and 3190 have inconsistent PRs","Prague","","" "`3238 `__","Insufficiently-defined behavior of ``std::function``\ deduction guides","Prague","","" -"`3242 `__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","","" +"`3242 `__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","|Complete|","Clang 13" "`3243 `__","``std::format``\ and negative zeroes","Prague","","" "`3247 `__","``ranges::iter_move``\ should perform ADL-only lookup of ``iter_move``\ ","Prague","","" "`3248 `__","``std::format``\ ``#b``\ , ``#B``\ , ``#o``\ , ``#x``\ , and ``#X``\ presentation types misformat negative numbers","Prague","","" diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -157,7 +157,7 @@ "`P1871 `__","LWG","Should concepts be enabled or disabled?","Belfast","* *","" "`P1872 `__","LWG","span should have size_type, not index_type","Belfast","|Complete|","10.0" "`P1878 `__","LWG","Constraining Readable Types","Belfast","* *","" -"`P1892 `__","LWG","Extended locale-specific presentation specifiers for std::format","Belfast","* *","" +"`P1892 `__","LWG","Extended locale-specific presentation specifiers for std::format","Belfast","|Complete|","13.0" "`P1902 `__","LWG","Missing feature-test macros 2018-2019","Belfast","* *","" "`P1959 `__","LWG","Remove std::weak_equality and std::strong_equality","Belfast","* *","" "`P1960 `__","LWG","NB Comment Changes Reviewed by SG1","Belfast","* *","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -14,6 +14,7 @@ __format/format_fwd.h __format/format_parse_context.h __format/format_string.h + __format/parser_std_format_spec.h __format/formatter.h __function_like.h __functional_03 diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h --- a/libcxx/include/__format/formatter.h +++ b/libcxx/include/__format/formatter.h @@ -11,6 +11,7 @@ #define _LIBCPP___FORMAT_FORMATTER_H #include <__config> +#include <__format/parser_std_format_spec.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -0,0 +1,640 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H +#define _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H + +#include <__config> +#include <__debug> +#include <__format/format_error.h> +#include <__format/format_string.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) && \ + !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) + +namespace __format_spec { + +/** + * Contains the flags for the std-format-spec. + * + * Some format-options can only be used for specific C++types and may depend on + * the selected format-type. + * * The C++type filtering can be done using the proper policies for + * @ref __parser_std. + * * The format-type filtering needs to be done post parsing in the parser + * derived from @ref __parser_std. + */ +class _LIBCPP_TYPE_VIS _Flags { +public: + enum class _LIBCPP_ENUM_VIS _Alignment : uint8_t { + /** + * No alignment is set in the format string. + * + * Zero-padding is ignored when an alignment is selected. + * The default alignment depends on the selected format-type. + */ + __default, + __left, + __center, + __right + }; + enum class _LIBCPP_ENUM_VIS _Sign : uint8_t { + /** + * No sign is set in the format string. + * + * The sign isn't allowed for certain format-types. By using this value + * it's possible to detect whether or not the user explicitly set the sign + * flag. For formatting purposes it behaves the same as @ref __minus. + */ + __default, + __minus, + __plus, + __space + }; + + _Alignment __alignment : 2 {_Alignment::__default}; + _Sign __sign : 2 {_Sign::__default}; + uint8_t __alternate_form : 1 {false}; + uint8_t __zero_padding : 1 {false}; + uint8_t __locale_specific_form : 1 {false}; + + enum class _LIBCPP_ENUM_VIS _Type : uint8_t { + __default, + __string, + __binary_lower_case, + __binary_upper_case, + __octal, + __decimal, + __hexadecimal_lower_case, + __hexadecimal_upper_case, + __pointer, + __char, + __float_hexadecimal_lower_case, + __float_hexadecimal_upper_case, + __scientific_lower_case, + __scientific_upper_case, + __fixed, + __general_lower_case, + __general_upper_case + }; + + _Type __type{_Type::__default}; +}; + +namespace __detail { +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr bool +__parse_alignment(_CharT __c, _Flags& __flags) noexcept { + switch (__c) { + case _CharT('<'): + __flags.__alignment = _Flags::_Alignment::__left; + return true; + + case _CharT('^'): + __flags.__alignment = _Flags::_Alignment::__center; + return true; + + case _CharT('>'): + __flags.__alignment = _Flags::_Alignment::__right; + return true; + } + return false; +} +} // namespace __detail + +template +class _LIBCPP_TEMPLATE_VIS __parser_fill_align { +public: + // TODO FMT The standard doesn't specify this character is a Unicode + // character. Validate what fmt and MSVC have implemented. + _CharT __fill{_CharT(' ')}; + +protected: + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + _LIBCPP_ASSERT(__begin != __end, "Precondition failure"); + if (__begin + 1 != __end) { + if (__detail::__parse_alignment(*(__begin + 1), __flags)) { + if (*__begin == _CharT('{') || *__begin == _CharT('}')) + __throw_format_error( + "Format-spec contains an invalid fill character"); + __fill = *__begin; + return __consume(__begin, __end, 2); + } + } + + if (__detail::__parse_alignment(*__begin, __flags)) + return __consume(__begin, __end, 1); + + return __begin; + } + +private: + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __consume(const _CharT* __begin, const _CharT* __end, size_t __n) { + __begin += __n; + if (__begin == __end) + __throw_format_error( + "End of input while parsing format-spec fill-and-align"); + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_sign { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + switch (*__begin) { + case _CharT('-'): + __flags.__sign = _Flags::_Sign::__minus; + break; + case _CharT('+'): + __flags.__sign = _Flags::_Sign::__plus; + break; + case _CharT(' '): + __flags.__sign = _Flags::_Sign::__space; + break; + default: + return __begin; + } + ++__begin; + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec sign"); + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_sign { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, _Flags&) { + switch (*__begin) { + case _CharT('+'): + case _CharT('-'): + case _CharT(' '): + __throw_format_error("Format-spec sign not allowed"); + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_alternate_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + if (*__begin == _CharT('#')) { + __flags.__alternate_form = true; + ++__begin; + if (__begin == __end) + __throw_format_error( + "End of input while parsing format-spec alternate form"); + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_alternate_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, _Flags&) { + if (*__begin == _CharT('#')) + __throw_format_error("Format-spec alternate form not allowed"); + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_zero_padding { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + if (*__begin == _CharT('0')) { + __flags.__zero_padding = true; + ++__begin; + if (__begin == __end) + __throw_format_error( + "End of input while parsing format-spec zero-padding"); + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_zero_padding { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, _Flags&) { + if (*__begin == _CharT('0')) + __throw_format_error("Format-spec zero-padding not allowed"); + + return __begin; + } +}; + +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr pair +__parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + + // This function is a wrapper to call the real parser. But it does the + // validation for the pre-conditions and post-conditions. + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec arg-id"); + + pair __r = + __format::__parse_arg_id(__begin, __end, __parse_ctx); + + if (*__r.first == _CharT(':')) + __throw_format_error( + "Format-spec arg-id shouldn't contain its own format-spec"); + + if (*__r.first != _CharT('}')) + __throw_format_error("Format-spec arg-id should terminate at a '}'"); + + ++__r.first; + if (__r.first == __end) + __throw_format_error("End of input while parsing format-spec arg-id"); + + return __r; +} + +template +[[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr uint32_t +__substitute_arg_id(basic_format_arg<_Context> __arg) { + return visit_format_arg( + [](auto __arg) -> uint32_t { + using _Type = decay_t; + if constexpr (integral<_Type>) { + if constexpr (signed_integral<_Type>) { + if (__arg < 0) + __throw_format_error("Format-spec arg-id replacement " + "shouldn't contain a negative value"); + } + + using _CT = common_type_t<_Type, decltype(__format::__number_max)>; + if (static_cast<_CT>(__arg) > + static_cast<_CT>(__format::__number_max)) + __throw_format_error("Format-spec arg-id replacement " + "exceeds maximum supported value"); + return __arg; + } else + __throw_format_error("Format-spec arg-id replacement " + "argument isn't an integral type"); + }, + __arg); +} + +class _LIBCPP_TYPE_VIS __parser_width { +public: + // The width can either contain a real width or and arg-id of the argument + // for the width. It would be possible to store it in an int32_t and use + // negative values for a real with and the other values for an arg-id. This + // would be possible since 0 isn't a valid width. However 0 is a valid + // precision. To keep the formats identical the switching between value and + // arg-id uses a separate bit. + uint32_t __width : 31 {0}; + uint32_t __width_as_arg : 1 {0}; + +protected: + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin == _CharT('0')) + __throw_format_error( + "Format-spec width should not contain a leading zero"); + + if (*__begin == _CharT('{')) { + pair __r = + __parse_arg_id(++__begin, __end, __parse_ctx); + __width = __r.second; + __width_as_arg = 1; + return __r.first; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return __begin; + + pair __r = + __format::__parse_number(__begin, __end); + __width = __r.second; + _LIBCPP_ASSERT(__width != 0, + "A zero value isn't allowed and should be impossible, " + "due to validations in this function"); + return __r.first; + } + + void _LIBCPP_INLINE_VISIBILITY constexpr __substitute_width_arg_id( + auto&& __arg) { + _LIBCPP_ASSERT( + __width_as_arg == 1, + "Substitute width called without when no substitution is required."); + // The clearing of the flag isn't required but looks better when debugging + // the code. + __width_as_arg = 0; + __width = __substitute_arg_id(__arg); + if (__width == 0) + __throw_format_error( + "Format-spec width replacement should contain a positive value"); + } +}; + +class _LIBCPP_TYPE_VIS __parser_precision { +public: + uint32_t __precision : 31 {__format::__number_max}; + uint32_t __precision_as_arg : 1 {0}; + +protected: + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin != _CharT('.')) + return __begin; + + ++__begin; + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec precision"); + + if (*__begin == _CharT('0')) { + ++__begin; + if (__begin == __end) + __throw_format_error( + "Unexpected end of format string while parsing arg-id"); + if (*__begin >= '0' && *__begin <= '9') + __throw_format_error( + "Format-spec precision should not contain a leading zero"); + + __precision = 0; + return __begin; + } + + if (*__begin == _CharT('{')) { + pair __arg_id = + __parse_arg_id(++__begin, __end, __parse_ctx); + __precision = __arg_id.second; + __precision_as_arg = 1; + return __arg_id.first; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + __throw_format_error( + "Format-spec precision doesn't contain a value or arg-id"); + + pair __r = + __format::__parse_number(__begin, __end); + __precision = __r.second; + return __r.first; + } + + void _LIBCPP_INLINE_VISIBILITY constexpr __substitute_precision_arg_id( + auto __arg) { + _LIBCPP_ASSERT(__precision_as_arg == 1, + "Substitute precision called without when no substitution " + "is required."); + // The clearing of the flag isn't required but looks better when debugging + // the code. + __precision_as_arg = 0; + __precision = __substitute_arg_id(__arg); + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_precision { +protected: + template + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, auto&) { + if (*__begin == _CharT('.')) + __throw_format_error("Format-spec precision not allowed"); + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_locale_specific_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + if (*__begin == _CharT('L')) { + __flags.__locale_specific_form = true; + ++__begin; + if (__begin == __end) + __throw_format_error( + "End of input while parsing format-spec locale-specific form"); + } + + return __begin; + } +}; + +class _LIBCPP_TYPE_VIS __parser_no_locale_specific_form { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT*, _Flags&) { + if (*__begin == _CharT('L')) + __throw_format_error("Format-spec locale-specific form not allowed"); + + return __begin; + } +}; + +/** Parses the type and validates the presence of the final '}'. */ +class _LIBCPP_TYPE_VIS __parser_type { +protected: + template + [[nodiscard]] static _LIBCPP_INLINE_VISIBILITY constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { + if (*__begin != _CharT('}')) { + // Determines the type. It does not validate whether the selected type + // is valid. Most formatters have optional fields that are only allowed + // for certain types. These parsers need to do validation after the + // type has been parsed. So its easier to implement the validation for + // all types in the specific parse function. + switch (*__begin++) { + case 'A': + __flags.__type = _Flags::_Type::__float_hexadecimal_upper_case; + break; + case 'B': + __flags.__type = _Flags::_Type::__binary_upper_case; + break; + case 'E': + __flags.__type = _Flags::_Type::__scientific_upper_case; + break; + case 'F': + __flags.__type = _Flags::_Type::__fixed; + break; + case 'G': + __flags.__type = _Flags::_Type::__general_upper_case; + break; + case 'X': + __flags.__type = _Flags::_Type::__hexadecimal_upper_case; + break; + case 'a': + __flags.__type = _Flags::_Type::__float_hexadecimal_lower_case; + break; + case 'b': + __flags.__type = _Flags::_Type::__binary_lower_case; + break; + case 'c': + __flags.__type = _Flags::_Type::__char; + break; + case 'd': + __flags.__type = _Flags::_Type::__decimal; + break; + case 'e': + __flags.__type = _Flags::_Type::__scientific_lower_case; + break; + case 'f': + __flags.__type = _Flags::_Type::__fixed; + break; + case 'g': + __flags.__type = _Flags::_Type::__general_lower_case; + break; + case 'o': + __flags.__type = _Flags::_Type::__octal; + break; + case 'p': + __flags.__type = _Flags::_Type::__pointer; + break; + case 's': + __flags.__type = _Flags::_Type::__string; + break; + case 'x': + __flags.__type = _Flags::_Type::__hexadecimal_lower_case; + break; + default: + __throw_format_error("Format-spec type has an unknown type"); + } + + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec type"); + } + + if (*__begin != _CharT('}')) + __throw_format_error("Format-spec should end with a '}'"); + + return __begin; + } +}; + +/** + * The parser for the std-format-space. + * + * The class is a policy based class. The order of the inheritance is picked to + * have the minimal padding for all subclasses. + * + * @note The standard doesn't mandate parsing to be constexpr this is an + * extension. https://wg21.link/P2216 will mandate the parsing happens at + * compile time. + */ +template +class _LIBCPP_TEMPLATE_VIS __parser_std + : public __parser_width, // provides __width(|as_arg) + public _ParserPrecision, // provides __precision(|as_arg) + public __parser_fill_align<_CharT>, // provides __fill and uses __flags + public _Flags, // provides __flags + private _ParserSign, // uses __flags + private _ParserAlternateForm, // uses __flags + private _ParserZeroPadding, // uses __flags + private _ParserLocaleSpecificForm, // uses __flags + private __parser_type // uses __flags +{ +public: + using char_type = _CharT; + + /** + * The low-level std-format-spec parse function. + * + * @pre __begin points at the beginning of the std-format-spec. This means + * directly after the ':'. + * @pre The std-format-spec is terminated by a '}'. The standard allows to + * terminate at __parse_ctx.end(), but the replacement string shall end with + * a '}'. Since the parser doesn't first scan for the terminating '}' it + * should be there. + * + * @returns The iterator pointing at '}'. + */ + [[nodiscard]] auto + _LIBCPP_INLINE_VISIBILITY constexpr parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + auto __begin = __parse_ctx.begin(); + auto __end = __parse_ctx.end(); + + _LIBCPP_ASSERT(__begin != __end, + "Caller should validate this precondition"); + __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, + static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, + "__parser_fill_align violates its post-condition"); + + __begin = _ParserSign::__parse(__begin, __end, static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, "_ParserSign violates its post-condition"); + __begin = _ParserAlternateForm::__parse(__begin, __end, + static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, + "_ParserAlternateForm violates its post-condition"); + __begin = _ParserZeroPadding::__parse(__begin, __end, + static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, + " _ParserZeroPadding violates its post-condition"); + __begin = __parser_width::__parse(__begin, __end, __parse_ctx); + _LIBCPP_ASSERT(__begin != __end, + "__parser_width violates its post-condition"); + __begin = _ParserPrecision::__parse(__begin, __end, __parse_ctx); + _LIBCPP_ASSERT(__begin != __end, + "_ParserPrecision violates its post-condition"); + __begin = _ParserLocaleSpecificForm::__parse(__begin, __end, + static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, + "_ParserLocaleSpecificForm violates its post-condition"); + __begin = + __parser_type::__parse(__begin, __end, static_cast<_Flags&>(*this)); + _LIBCPP_ASSERT(__begin != __end, + "__parser_type violates its post-condition"); + return __begin; + } +}; + +} // namespace __format_spec + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) && !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -282,6 +282,7 @@ #include <__format/format_error.h> #include <__format/format_parse_context.h> #include <__format/format_string.h> +#include <__format/parser_std_format_spec.h> #include <__format/formatter.h> #include #include diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_alternate_form.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the alternate form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(bool expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == expected); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("#}")); + + test_exception("End of input while parsing format-spec alternate form", + CSTR("#")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_fill_align.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the fill and alignment. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +struct Expected { + CharT fill; + _Flags::_Alignment alignment; +}; + +template +constexpr void test(Expected expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + 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 == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test({CharT(' '), _Flags::_Alignment::__default}, 0, CSTR("}")); + test({CharT(' '), _Flags::_Alignment::__left}, 1, CSTR("<}")); + test({CharT(' '), _Flags::_Alignment::__center}, 1, CSTR("^}")); + test({CharT(' '), _Flags::_Alignment::__right}, 1, CSTR(">}")); + + test({CharT('1'), _Flags::_Alignment::__left}, 2, CSTR("1<}")); + test({CharT('a'), _Flags::_Alignment::__center}, 2, CSTR("a^}")); + test({CharT('#'), _Flags::_Alignment::__right}, 2, CSTR("#>}")); + + test_exception("Format-spec contains an invalid fill character", CSTR("{<}")); + test_exception("Format-spec contains an invalid fill character", CSTR("}>}")); + test_exception("End of input while parsing format-spec fill-and-align", + CSTR("<")); + test_exception("End of input while parsing format-spec fill-and-align", + CSTR("X<")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_locale_specific_form.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the locale-specific form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(bool expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == expected); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("L}")); + + test_exception("End of input while parsing format-spec locale-specific form", + CSTR("L")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_alternate_form.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the alternate form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("Format-spec alternate form not allowed", CSTR("#")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_locale_specific_form.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the locale-specific form. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("Format-spec locale-specific form not allowed", CSTR("L")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_precision.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the precision. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("Format-spec precision not allowed", CSTR(".")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_sign.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the sign. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("Format-spec sign not allowed", CSTR("-")); + test_exception("Format-spec sign not allowed", CSTR("+")); + test_exception("Format-spec sign not allowed", CSTR(" ")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_no_zero_padding.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the zero-padding. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(CSTR("}")); + test_exception("Format-spec zero-padding not allowed", CSTR("0")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_precision.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the precision. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +struct Expected { + uint32_t __precision{0}; + bool __precision_as_arg{false}; +}; + +template +constexpr void test(Expected expected, size_t size, const CharT* fmt) { + // Initialize parser with sufficient arguments to avoid the parsing to fail + // due to insufficient arguments. + std::basic_format_parse_context parse_ctx(fmt, + std::__format::__number_max); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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 == 0); + assert(parser.__precision == expected.__precision); + assert(parser.__precision_as_arg == expected.__precision_as_arg); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test({std::__format::__number_max, 0}, 0, CSTR("}")); + test({0, 0}, 2, CSTR(".0}")); + test({1, 0}, 2, CSTR(".1}")); + test({10, 0}, 3, CSTR(".10}")); + test({1000, 0}, 5, CSTR(".1000}")); + test({1000000, 0}, 8, CSTR(".1000000}")); + + test({0, 1}, 3, CSTR(".{}}")); + test({0, 1}, 4, CSTR(".{0}}")); + test({1, 1}, 4, CSTR(".{1}}")); + + test_exception("Format-spec precision should not contain a leading zero", + CSTR(".00")); + test_exception("Format-spec precision should not contain a leading zero", + CSTR(".01")); + test_exception("Format-spec precision doesn't contain a value or arg-id", + CSTR(".a")); + test_exception("Format-spec precision doesn't contain a value or arg-id", + ".:"); + test_exception("End of input while parsing format-spec numeric value", + CSTR(".1")); + static_assert(std::__format::__number_max == 999'999'999, + "Update the assert and the test."); + test_exception("Format-spec numeric value too large", CSTR(".1000000000}")); + + test_exception("End of input while parsing format-spec arg-id", CSTR(".{")); + test_exception("Format-spec arg-id should terminate at a '}'", CSTR(".{0")); + test_exception("Format-spec arg-id starts with an invalid character", ".{a"); + test_exception("End of input while parsing format-spec numeric value", + CSTR(".{1")); + test_exception("End of input while parsing format-spec numeric value", + CSTR(".{9")); + test_exception("Format-spec arg-id shouldn't contain its own format-spec", + CSTR(".{9:")); + test_exception("Format-spec arg-id should terminate at a '}'", CSTR(".{9a")); + test_exception("End of input while parsing format-spec arg-id", CSTR(".{1}")); + + static_assert(std::__format::__number_max == 999'999'999, + "Update the assert and the test."); + test_exception("Format-spec numeric value too large", CSTR(".{1000000000}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_sign.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the sign. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(_Flags::_Sign expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == expected); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == false); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(_Flags::_Sign::__default, 0, CSTR("}")); + test(_Flags::_Sign::__minus, 1, CSTR("-}")); + test(_Flags::_Sign::__plus, 1, CSTR("+}")); + test(_Flags::_Sign::__space, 1, CSTR(" }")); + + test_exception("End of input while parsing format-spec sign", CSTR("-")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_type.pass.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the type. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = + __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(_Flags::_Type expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == expected); +} + +template +constexpr void test() { + test(_Flags::_Type::__default, 0, CSTR("}")); + + test(_Flags::_Type::__float_hexadecimal_upper_case, 1, CSTR("A}")); + test(_Flags::_Type::__binary_upper_case, 1, CSTR("B}")); + test_exception("Format-spec type has an unknown type", CSTR("C")); + test_exception("Format-spec type has an unknown type", CSTR("D")); + test(_Flags::_Type::__scientific_upper_case, 1, CSTR("E}")); + test(_Flags::_Type::__fixed, 1, CSTR("F}")); + test(_Flags::_Type::__general_upper_case, 1, CSTR("G}")); + test_exception("Format-spec type has an unknown type", CSTR("H")); + test_exception("Format-spec type has an unknown type", CSTR("I")); + test_exception("Format-spec type has an unknown type", CSTR("J")); + test_exception("Format-spec type has an unknown type", CSTR("K")); + test_exception("Format-spec locale-specific form not allowed", CSTR("L")); + test_exception("Format-spec type has an unknown type", CSTR("M")); + test_exception("Format-spec type has an unknown type", CSTR("N")); + test_exception("Format-spec type has an unknown type", CSTR("O")); + test_exception("Format-spec type has an unknown type", CSTR("P")); + test_exception("Format-spec type has an unknown type", CSTR("Q")); + test_exception("Format-spec type has an unknown type", CSTR("R")); + test_exception("Format-spec type has an unknown type", CSTR("S")); + test_exception("Format-spec type has an unknown type", CSTR("T")); + test_exception("Format-spec type has an unknown type", CSTR("U")); + test_exception("Format-spec type has an unknown type", CSTR("V")); + test_exception("Format-spec type has an unknown type", CSTR("W")); + test(_Flags::_Type::__hexadecimal_upper_case, 1, CSTR("X}")); + test_exception("Format-spec type has an unknown type", CSTR("Y")); + test_exception("Format-spec type has an unknown type", CSTR("Z")); + + test(_Flags::_Type::__float_hexadecimal_lower_case, 1, CSTR("a}")); + test(_Flags::_Type::__binary_lower_case, 1, CSTR("b}")); + test(_Flags::_Type::__char, 1, CSTR("c}")); + test(_Flags::_Type::__decimal, 1, CSTR("d}")); + test(_Flags::_Type::__scientific_lower_case, 1, CSTR("e}")); + test(_Flags::_Type::__fixed, 1, CSTR("f}")); + test(_Flags::_Type::__general_lower_case, 1, CSTR("g}")); + test_exception("Format-spec type has an unknown type", CSTR("h")); + test_exception("Format-spec type has an unknown type", CSTR("i")); + test_exception("Format-spec type has an unknown type", CSTR("j")); + test_exception("Format-spec type has an unknown type", CSTR("k")); + test_exception("Format-spec type has an unknown type", CSTR("l")); + test_exception("Format-spec type has an unknown type", CSTR("m")); + test_exception("Format-spec type has an unknown type", CSTR("n")); + test(_Flags::_Type::__octal, 1, CSTR("o}")); + test(_Flags::_Type::__pointer, 1, CSTR("p}")); + test_exception("Format-spec type has an unknown type", CSTR("q")); + test_exception("Format-spec type has an unknown type", CSTR("r")); + test(_Flags::_Type::__string, 1, CSTR("s}")); + test_exception("Format-spec type has an unknown type", CSTR("t")); + test_exception("Format-spec type has an unknown type", CSTR("u")); + test_exception("Format-spec type has an unknown type", CSTR("v")); + test_exception("Format-spec type has an unknown type", CSTR("w")); + test(_Flags::_Type::__hexadecimal_lower_case, 1, CSTR("x}")); + test_exception("Format-spec type has an unknown type", CSTR("y")); + test_exception("Format-spec type has an unknown type", CSTR("z")); + + test_exception("End of input while parsing format-spec type", CSTR("a")); + test_exception("Format-spec should end with a '}'", CSTR("ab")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_width.pass.cpp @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the width. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +#include "test_exception.h" // requires parser + +struct Expected { + uint32_t __width{0}; + bool __width_as_arg{false}; +}; + +template +constexpr void test(Expected expected, size_t size, const CharT* fmt) { + // Initialize parser with sufficient arguments to avoid the parsing to fail + // due to insufficient arguments. + std::basic_format_parse_context parse_ctx(fmt, + std::__format::__number_max); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + 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.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test({0, 0}, 0, CSTR("}")); + test({1, 0}, 1, CSTR("1}")); + test({10, 0}, 2, CSTR("10}")); + test({1000, 0}, 4, CSTR("1000}")); + test({1000000, 0}, 7, CSTR("1000000}")); + + test({0, 1}, 2, CSTR("{}}")); + test({0, 1}, 3, CSTR("{0}}")); + test({1, 1}, 3, CSTR("{1}}")); + + test_exception("Format-spec width should not contain a leading zero", + CSTR("00")); + test_exception("End of input while parsing format-spec numeric value", + CSTR("1")); + static_assert(std::__format::__number_max == 999'999'999, + "Update the assert and the test."); + test_exception("Format-spec numeric value too large", CSTR("1000000000}")); + + test_exception("End of input while parsing format-spec arg-id", CSTR("{")); + test_exception("Format-spec arg-id should terminate at a '}'", CSTR("{0")); + test_exception("Format-spec arg-id starts with an invalid character", + CSTR("{a")); + test_exception("End of input while parsing format-spec numeric value", + CSTR("{1")); + test_exception("End of input while parsing format-spec numeric value", + CSTR("{9")); + test_exception("Format-spec arg-id shouldn't contain its own format-spec", + CSTR("{9:")); + test_exception("Format-spec arg-id should terminate at a '}'", CSTR("{9a")); + test_exception("End of input while parsing format-spec arg-id", CSTR("{1}")); + + static_assert(std::__format::__number_max == 999'999'999, + "Update the assert and the test."); + test_exception("Format-spec numeric value too large", CSTR("{1000000000}")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/std_format_spec_zero_padding.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// [format.string.std] Standard format specifiers +// - Tests parsing of the zero-padding. + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "make_string.h" + +#define CSTR(S) MAKE_CSTRING(CharT, S) + +using namespace std::__format_spec; + +template +using parser = __parser_std; + +#include "test_exception.h" // requires parser + +template +constexpr void test(bool expected, size_t size, const CharT* fmt) { + std::basic_format_parse_context parse_ctx(fmt); + auto begin = parse_ctx.begin(); + parser parser; + auto it = parser.parse(parse_ctx); + + assert(begin + size == it); + assert(parser.__fill == CharT(' ')); + assert(parser.__alignment == _Flags::_Alignment::__default); + assert(parser.__sign == _Flags::_Sign::__default); + assert(parser.__alternate_form == false); + assert(parser.__zero_padding == expected); + assert(parser.__width == 0); + assert(parser.__width_as_arg == false); + assert(parser.__locale_specific_form == false); + assert(parser.__type == _Flags::_Type::__default); +} + +template +constexpr void test() { + test(false, 0, CSTR("}")); + test(true, 1, CSTR("0}")); + + test_exception("End of input while parsing format-spec zero-padding", + CSTR("0")); +} + +constexpr bool test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h b/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.string/format.string.std/test_exception.h @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H +#define _LIBCPP_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H + +// Helper header for the tests in this directory. +// Note the header isn't freestanding. + +namespace detail { +template +void test_exception(std::string_view what, const CharT* fmt) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::basic_format_parse_context parse_ctx(fmt); + (void)parser{}.parse(parse_ctx); +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + if constexpr (std::same_as) + std::cerr << "\nFormat string " << fmt + << "\nDidn't throw an exception.\n"; +#endif + assert(false); + } catch (std::format_error& e) { +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + if constexpr (std::same_as) + if (e.what() != what) + std::cerr << "\nFormat string " << fmt << "\nExpected exception " + << what << "\nActual exception " << e.what() << '\n'; +#endif + assert(e.what() == what); + return; + } + + assert(false); +#else + (void)what; + (void)fmt; +#endif +} +} // namespace detail + +/** + * Wrapper for the exception tests. + * + * When using the real function directly during in a constexpr test and add + * the `std::is_constant_evaluated()` test there the compilation fails. This + * happens since assert calls the non-constexpr function '__assert_fail'. + * Solve this issue with an layer of indirection. + */ +template +constexpr void test_exception(std::string_view what, const CharT* fmt) { +#ifndef _LIBCPP_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) + detail::test_exception(what, fmt); +#else + (void)what; + (void)fmt; +#endif +} + +#endif // _LIBCPP_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_TEST_EXCEPTION_H