Mark de Wever 5db033e204 [libc++][format] Improves fill character.
The main change is to allow a UCS scalar value as fill character.
Especially for char based formatting this increase the number of valid
characters. Originally this was to be expected ABI breaking, however the
current change does not seem to break the ABI.

Implements
- P2572 std::format() fill character allowances

Depends on D144499

Reviewed By: ldionne, tahonermann, #libc

Differential Revision: https://reviews.llvm.org/D144742
2023-05-19 17:20:50 +02:00

139 lines
6.1 KiB
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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Evaluate gcc-12 status
// This version runs the test when the platform has Unicode support.
// UNSUPPORTED: libcpp-has-no-unicode
// XFAIL: availability-fp_to_chars-missing
// <format>
// The paper
// P2572R1 std::format fill character allowances
// adds support for Unicode Scalar Values as fill character.
#include <format>
#include "assert_macros.h"
#include "concat_macros.h"
#include "format.functions.common.h"
#include "make_string.h"
#include "string_literal.h"
#include "test_format_string.h"
#include "test_macros.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
auto check = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
TEST_REQUIRE(out == expected,
TEST_WRITE_CONCATENATED(
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
};
auto check_exception =
[]<class CharT, class... Args>(
[[maybe_unused]] std::string_view what,
[[maybe_unused]] std::basic_string_view<CharT> fmt,
[[maybe_unused]] Args&&... args) {
TEST_VALIDATE_EXCEPTION(
std::format_error,
[&]([[maybe_unused]] const std::format_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED(
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...)));
};
template <class CharT>
void test() {
// 1, 2, 3, 4 code unit UTF-8 transitions
check(SV("\u000042\u0000"), SV("{:\u0000^4}"), 42);
check(SV("\u007f42\u007f"), SV("{:\u007f^4}"), 42);
check(SV("\u008042\u0080"), SV("{:\u0080^4}"), 42);
check(SV("\u07ff42\u07ff"), SV("{:\u07ff^4}"), 42);
check(SV("\u080042\u0800"), SV("{:\u0800^4}"), 42);
check(SV("\uffff42\uffff"), SV("{:\uffff^4}"), 42);
check(SV("\U0010000042\U00100000"), SV("{:\U00100000^4}"), 42);
check(SV("\U0010ffff42\U0010ffff"), SV("{:\U0010ffff^4}"), 42);
// Examples of P2572R1
check(SV("🤡🤡x🤡🤡🤡"), SV("{:🤡^6}"), SV("x"));
check(SV("🤡🤡🤡"), SV("{:*^6}"), SV("🤡🤡🤡"));
check(SV("12345678"), SV("{:*>6}"), SV("12345678"));
// Invalid Unicode Scalar Values
if constexpr (std::same_as<CharT, char>) {
check_exception("The format-spec contains malformed Unicode characters", SV("{:\xed\xa0\x80^}"), 42); // U+D800
check_exception("The format-spec contains malformed Unicode characters", SV("{:\xed\xa0\xbf^}"), 42); // U+DBFF
check_exception("The format-spec contains malformed Unicode characters", SV("{:\xed\xbf\x80^}"), 42); // U+DC00
check_exception("The format-spec contains malformed Unicode characters", SV("{:\xed\xbf\xbf^}"), 42); // U+DFFF
check_exception(
"The format-spec contains malformed Unicode characters", SV("{:\xf4\x90\x80\x80^}"), 42); // U+110000
check_exception(
"The format-spec contains malformed Unicode characters", SV("{:\xf4\x90\xbf\xbf^}"), 42); // U+11FFFF
check_exception("The format-spec contains malformed Unicode characters",
SV("{:\x80^}"),
42); // Trailing code unit with no leading one.
check_exception(
"The format-spec contains malformed Unicode characters", SV("{:\xc0^}"), 42); // Missing trailing code unit.
check_exception(
"The format-spec contains malformed Unicode characters", SV("{:\xe0\x80^}"), 42); // Missing trailing code unit.
check_exception("The format-spec contains malformed Unicode characters",
SV("{:\xf0\x80^}"),
42); // Missing two trailing code units.
check_exception("The format-spec contains malformed Unicode characters",
SV("{:\xf0\x80\x80^}"),
42); // Missing trailing code unit.
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
} else {
# ifdef TEST_SHORT_WCHAR
check_exception("The format-spec contains malformed Unicode characters", std::wstring_view{L"{:\xd800^}"}, 42);
check_exception("The format-spec contains malformed Unicode characters", std::wstring_view{L"{:\xdbff^}"}, 42);
check_exception("The format-spec contains malformed Unicode characters", std::wstring_view{L"{:\xdc00^}"}, 42);
check_exception("The format-spec contains malformed Unicode characters", std::wstring_view{L"{:\xddff^}"}, 42);
check_exception("The format-spec contains malformed Unicode characters",
std::wstring_view{L"{:\xdc00\xd800^}"},
42); // Reverted surrogates.
# else // TEST_SHORT_WCHAR
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\xd800^}"}, 42);
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\xdbff^}"}, 42);
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\xdc00^}"}, 42);
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\xddff^}"}, 42);
check_exception(
"The format-spec should consume the input or end with a '}'", std::wstring_view{L"{:\xdc00\xd800^}"}, 42);
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\x00110000^}"}, 42);
check_exception("The fill character contains an invalid value", std::wstring_view{L"{:\x0011ffff^}"}, 42);
# endif // TEST_SHORT_WCHAR
#endif // TEST_HAS_NO_WIDE_CHARACTERS
}
}
int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
return 0;
}