aboutsummaryrefslogtreecommitdiff
path: root/libcxx/include/__format
diff options
context:
space:
mode:
authorMark de Wever <koraq@xs4all.nl>2023-06-04 21:32:10 +0200
committerMark de Wever <koraq@xs4all.nl>2023-07-18 21:01:52 +0200
commita0ffeccc707679d50cd8d4fba0e6f20a3450d474 (patch)
tree348b608b5c940dc1f5fa7d4d401a43604cab473e /libcxx/include/__format
parent7cc6b80d9a97602bd8f3bc5333cd26020a3726da (diff)
downloadllvm-a0ffeccc707679d50cd8d4fba0e6f20a3450d474.zip
llvm-a0ffeccc707679d50cd8d4fba0e6f20a3450d474.tar.gz
llvm-a0ffeccc707679d50cd8d4fba0e6f20a3450d474.tar.bz2
[libc++][format] Improves run-time diagnostics.
After parsing a std-format-spec it's validated, depending on the type used some format options are not allowed. This improves the error messages in the exceptions thrown upon failure. Depends on D155364 Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D155366
Diffstat (limited to 'libcxx/include/__format')
-rw-r--r--libcxx/include/__format/formatter_bool.h2
-rw-r--r--libcxx/include/__format/formatter_char.h2
-rw-r--r--libcxx/include/__format/formatter_floating_point.h2
-rw-r--r--libcxx/include/__format/formatter_integer.h2
-rw-r--r--libcxx/include/__format/formatter_pointer.h2
-rw-r--r--libcxx/include/__format/parser_std_format_spec.h152
6 files changed, 130 insertions, 32 deletions
diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h
index ad50f04..3c8ae95f 100644
--- a/libcxx/include/__format/formatter_bool.h
+++ b/libcxx/include/__format/formatter_bool.h
@@ -39,7 +39,7 @@ public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_integral);
- __format_spec::__process_parsed_bool(__parser_);
+ __format_spec::__process_parsed_bool(__parser_, "a bool");
return __result;
}
diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h
index 8b1b357..d6e61e8 100644
--- a/libcxx/include/__format/formatter_char.h
+++ b/libcxx/include/__format/formatter_char.h
@@ -37,7 +37,7 @@ public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_integral);
- __format_spec::__process_parsed_char(__parser_);
+ __format_spec::__process_parsed_char(__parser_, "a character");
return __result;
}
diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h
index e01d7b8..fbb8aa9 100644
--- a/libcxx/include/__format/formatter_floating_point.h
+++ b/libcxx/include/__format/formatter_floating_point.h
@@ -762,7 +762,7 @@ public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_floating_point);
- __format_spec::__process_parsed_floating_point(__parser_);
+ __format_spec::__process_parsed_floating_point(__parser_, "a floating-point");
return __result;
}
diff --git a/libcxx/include/__format/formatter_integer.h b/libcxx/include/__format/formatter_integer.h
index dca3cb3..5590bff 100644
--- a/libcxx/include/__format/formatter_integer.h
+++ b/libcxx/include/__format/formatter_integer.h
@@ -37,7 +37,7 @@ public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_integral);
- __format_spec::__process_parsed_integer(__parser_);
+ __format_spec::__process_parsed_integer(__parser_, "an integer");
return __result;
}
diff --git a/libcxx/include/__format/formatter_pointer.h b/libcxx/include/__format/formatter_pointer.h
index 18aaee1..a221261 100644
--- a/libcxx/include/__format/formatter_pointer.h
+++ b/libcxx/include/__format/formatter_pointer.h
@@ -35,7 +35,7 @@ public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_pointer);
- __format_spec::__process_display_type_pointer(__parser_.__type_);
+ __format_spec::__process_display_type_pointer(__parser_.__type_, "a pointer");
return __result;
}
diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h
index 29cce4c..5147b5e 100644
--- a/libcxx/include/__format/parser_std_format_spec.h
+++ b/libcxx/include/__format/parser_std_format_spec.h
@@ -36,6 +36,7 @@
#include <__type_traits/is_trivially_copyable.h>
#include <__variant/monostate.h>
#include <cstdint>
+#include <string>
#include <string_view>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -51,6 +52,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __format_spec {
+_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI inline void
+__throw_invalid_option_format_error(const char* __id, const char* __option) {
+ std::__throw_format_error(
+ (string("The format specifier for ") + __id + " does not allow the " + __option + " option").c_str());
+}
+
+_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI inline void __throw_invalid_type_format_error(const char* __id) {
+ std::__throw_format_error(
+ (string("The type option contains an invalid value for ") + __id + " formatting argument").c_str());
+}
+
template <contiguous_iterator _Iterator, class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr __format::__parse_number_result<_Iterator>
__parse_arg_id(_Iterator __begin, _Iterator __end, _ParseContext& __ctx) {
@@ -186,7 +198,7 @@ enum class _LIBCPP_ENUM_VIS __sign : uint8_t {
};
enum class _LIBCPP_ENUM_VIS __type : uint8_t {
- __default,
+ __default = 0,
__string,
__binary_lower_case,
__binary_upper_case,
@@ -208,6 +220,25 @@ enum class _LIBCPP_ENUM_VIS __type : uint8_t {
__debug
};
+_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __create_type_mask(__type __t) {
+ uint32_t __shift = static_cast<uint32_t>(__t);
+ if (__shift == 0)
+ return 1;
+
+ if (__shift > 31)
+ std::__throw_format_error("The type does not fit in the mask");
+
+ return 1 << __shift;
+}
+
+inline constexpr uint32_t __type_mask_integer =
+ __create_type_mask(__type::__binary_lower_case) | //
+ __create_type_mask(__type::__binary_upper_case) | //
+ __create_type_mask(__type::__decimal) | //
+ __create_type_mask(__type::__octal) | //
+ __create_type_mask(__type::__hexadecimal_lower_case) | //
+ __create_type_mask(__type::__hexadecimal_upper_case);
+
struct __std {
__alignment __alignment_ : 3;
__sign __sign_ : 2;
@@ -383,11 +414,86 @@ public:
return __begin;
if (__begin != __end && *__begin != _CharT('}'))
- std::__throw_format_error("The format-spec should consume the input or end with a '}'");
+ std::__throw_format_error("The format specifier should consume the input or end with a '}'");
return __begin;
}
+ // Validates the selected the parsed data.
+ //
+ // The valid fields in the parser may depend on the display type
+ // selected. But the type is the last optional field, so by the time
+ // it's known an option can't be used, it already has been parsed.
+ // This does the validation again.
+ //
+ // For example an integral may have a sign, zero-padding, or alternate
+ // form when the type option is not 'c'. So the generic approach is:
+ //
+ // typename _ParseContext::iterator __result = __parser_.__parse(__ctx, __format_spec::__fields_integral);
+ // if (__parser.__type_ == __format_spec::__type::__char) {
+ // __parser.__validate((__format_spec::__fields_bool, "an integer");
+ // ... // more char adjustments
+ // } else {
+ // ... // validate an integral type.
+ // }
+ //
+ // For some types all valid options need a second validation run, like
+ // boolean types.
+ //
+ // Depending on whether the validation is done at compile-time or
+ // run-time the error differs
+ // - run-time the exception is thrown and contains the type of field
+ // being validated.
+ // - at compile-time the line with `std::__throw_format_error` is shown
+ // in the output. In that case it's important for the error to be on one
+ // line.
+ // Note future versions of C++ may allow better compile-time error
+ // reporting.
+ _LIBCPP_HIDE_FROM_ABI constexpr void
+ __validate(__fields __fields, const char* __id, uint32_t __type_mask = -1) const {
+ if (!__fields.__sign_ && __sign_ != __sign::__default) {
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier does not allow the sign option");
+ else
+ __format_spec::__throw_invalid_option_format_error(__id, "sign");
+ }
+
+ if (!__fields.__alternate_form_ && __alternate_form_) {
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier does not allow the alternate form option");
+ else
+ __format_spec::__throw_invalid_option_format_error(__id, "alternate form");
+ }
+
+ if (!__fields.__zero_padding_ && __alignment_ == __alignment::__zero_padding) {
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier does not allow the zero-padding option");
+ else
+ __format_spec::__throw_invalid_option_format_error(__id, "zero-padding");
+ }
+
+ if (!__fields.__precision_ && __precision_ != -1) { // Works both when the precision has a value or an arg-id.
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier does not allow the precision option");
+ else
+ __format_spec::__throw_invalid_option_format_error(__id, "precision");
+ }
+
+ if (!__fields.__locale_specific_form_ && __locale_specific_form_) {
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier does not allow the locale-specific form option");
+ else
+ __format_spec::__throw_invalid_option_format_error(__id, "locale-specific form");
+ }
+
+ if ((__create_type_mask(__type_) & __type_mask) == 0) {
+ if (std::is_constant_evaluated())
+ std::__throw_format_error("The format specifier uses an invalid value for the type option");
+ else
+ __format_spec::__throw_invalid_type_format_error(__id);
+ }
+ }
+
/// \returns the `__parsed_specifications` with the resolved dynamic sizes..
_LIBCPP_HIDE_FROM_ABI
__parsed_specifications<_CharT> __get_parsed_std_specifications(auto& __ctx) const {
@@ -788,31 +894,23 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_string(__format_spec
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_bool_string(__parser<_CharT>& __parser) {
- if (__parser.__sign_ != __sign::__default)
- std::__throw_format_error("A sign field isn't allowed in this format-spec");
-
- if (__parser.__alternate_form_)
- std::__throw_format_error("An alternate form field isn't allowed in this format-spec");
-
- if (__parser.__alignment_ == __alignment::__zero_padding)
- std::__throw_format_error("A zero-padding field isn't allowed in this format-spec");
-
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_bool_string(__parser<_CharT>& __parser, const char* __id) {
+ __parser.__validate(__format_spec::__fields_bool, __id);
if (__parser.__alignment_ == __alignment::__default)
__parser.__alignment_ = __alignment::__left;
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_char(__parser<_CharT>& __parser) {
- __format_spec::__process_display_type_bool_string(__parser);
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_char(__parser<_CharT>& __parser, const char* __id) {
+ __format_spec::__process_display_type_bool_string(__parser, __id);
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __parser) {
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __parser, const char* __id) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
case __format_spec::__type::__string:
- __format_spec::__process_display_type_bool_string(__parser);
+ __format_spec::__process_display_type_bool_string(__parser, __id);
break;
case __format_spec::__type::__binary_lower_case:
@@ -824,17 +922,17 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __p
break;
default:
- std::__throw_format_error("The format-spec type has a type not supported for a bool argument");
+ __format_spec::__throw_invalid_type_format_error(__id);
}
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __parser) {
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __parser, const char* __id) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
case __format_spec::__type::__char:
case __format_spec::__type::__debug:
- __format_spec::__process_display_type_char(__parser);
+ __format_spec::__process_display_type_char(__parser, __id);
break;
case __format_spec::__type::__binary_lower_case:
@@ -846,12 +944,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __p
break;
default:
- std::__throw_format_error("The format-spec type has a type not supported for a char argument");
+ __format_spec::__throw_invalid_type_format_error(__id);
}
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>& __parser) {
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>& __parser, const char* __id) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
case __format_spec::__type::__binary_lower_case:
@@ -863,16 +961,16 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>&
break;
case __format_spec::__type::__char:
- __format_spec::__process_display_type_char(__parser);
+ __format_spec::__process_display_type_char(__parser, __id);
break;
default:
- std::__throw_format_error("The format-spec type has a type not supported for an integer argument");
+ __format_spec::__throw_invalid_type_format_error(__id);
}
}
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) {
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser, const char* __id) {
switch (__parser.__type_) {
case __format_spec::__type::__default:
case __format_spec::__type::__hexfloat_lower_case:
@@ -891,11 +989,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_C
break;
default:
- std::__throw_format_error("The format-spec type has a type not supported for a floating-point argument");
+ __format_spec::__throw_invalid_type_format_error(__id);
}
}
-_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spec::__type __type) {
+_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spec::__type __type, const char* __id) {
switch (__type) {
case __format_spec::__type::__default:
case __format_spec::__type::__pointer_lower_case:
@@ -903,7 +1001,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spe
break;
default:
- std::__throw_format_error("The format-spec type has a type not supported for a pointer argument");
+ __format_spec::__throw_invalid_type_format_error(__id);
}
}