Improves both the compile-time and run-time errors.
At compile-time it does a bit more work to get more specific errors.
This could be done at run-time too, but that has a performance penalty.
Since it's expected most use-cases use format* instead of vformat* the
compile-time errors are more common.
For example when using
std::format_to("{:-c}", 42);
Before compile output would contain
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
Now it contains
std::__throw_format_error("The format specifier does not allow the sign option");
Given a better indication the sign option is not allowed. Note the
output is still not user-friendly; C++ doesn't have good facilities to
generate nice messages from the library.
In general all messages have been reviewed and improved, using a more
consistent style and using less terms used in the standard. For example
format-spec -> format specifier
arg-id -> argument index
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D152624
140 lines
6.2 KiB
C++
140 lines
6.2 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: GCC-ALWAYS_INLINE-FIXME
|
|
|
|
// 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 specifier contains malformed Unicode characters", SV("{:\xed\xa0\x80^}"), 42); // U+D800
|
|
check_exception("The format specifier contains malformed Unicode characters", SV("{:\xed\xa0\xbf^}"), 42); // U+DBFF
|
|
check_exception("The format specifier contains malformed Unicode characters", SV("{:\xed\xbf\x80^}"), 42); // U+DC00
|
|
check_exception("The format specifier contains malformed Unicode characters", SV("{:\xed\xbf\xbf^}"), 42); // U+DFFF
|
|
|
|
check_exception(
|
|
"The format specifier contains malformed Unicode characters", SV("{:\xf4\x90\x80\x80^}"), 42); // U+110000
|
|
check_exception(
|
|
"The format specifier contains malformed Unicode characters", SV("{:\xf4\x90\xbf\xbf^}"), 42); // U+11FFFF
|
|
|
|
check_exception("The format specifier contains malformed Unicode characters",
|
|
SV("{:\x80^}"),
|
|
42); // Trailing code unit with no leading one.
|
|
check_exception("The format specifier contains malformed Unicode characters",
|
|
SV("{:\xc0^}"),
|
|
42); // Missing trailing code unit.
|
|
check_exception("The format specifier contains malformed Unicode characters",
|
|
SV("{:\xe0\x80^}"),
|
|
42); // Missing trailing code unit.
|
|
check_exception("The format specifier contains malformed Unicode characters",
|
|
SV("{:\xf0\x80^}"),
|
|
42); // Missing two trailing code units.
|
|
check_exception("The format specifier 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 specifier contains malformed Unicode characters", std::wstring_view{L"{:\xd800^}"}, 42);
|
|
check_exception("The format specifier contains malformed Unicode characters", std::wstring_view{L"{:\xdbff^}"}, 42);
|
|
check_exception("The format specifier contains malformed Unicode characters", std::wstring_view{L"{:\xdc00^}"}, 42);
|
|
check_exception("The format specifier contains malformed Unicode characters", std::wstring_view{L"{:\xddff^}"}, 42);
|
|
|
|
check_exception("The format specifier contains malformed Unicode characters",
|
|
std::wstring_view{L"{:\xdc00\xd800^}"},
|
|
42); // Reverted surrogates.
|
|
|
|
# else // TEST_SHORT_WCHAR
|
|
check_exception("The fill option contains an invalid value", std::wstring_view{L"{:\xd800^}"}, 42);
|
|
check_exception("The fill option contains an invalid value", std::wstring_view{L"{:\xdbff^}"}, 42);
|
|
check_exception("The fill option contains an invalid value", std::wstring_view{L"{:\xdc00^}"}, 42);
|
|
check_exception("The fill option contains an invalid value", std::wstring_view{L"{:\xddff^}"}, 42);
|
|
|
|
check_exception(
|
|
"The format specifier should consume the input or end with a '}'", std::wstring_view{L"{:\xdc00\xd800^}"}, 42);
|
|
|
|
check_exception("The fill option contains an invalid value", std::wstring_view{L"{:\x00110000^}"}, 42);
|
|
check_exception("The fill option 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;
|
|
}
|