From f1ff922eafc6972dd6437d6e9908146fa92577fa Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Sun, 9 Jan 2022 17:43:10 +0100 Subject: Improve exception message for out of range numbers in format strings --- include/cxxformat/core.hpp | 32 +++++++++++++++++++++----------- include/cxxformat/runtime.hpp | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/cxxformat/core.hpp b/include/cxxformat/core.hpp index 6eec0ef..2a78b26 100644 --- a/include/cxxformat/core.hpp +++ b/include/cxxformat/core.hpp @@ -224,6 +224,16 @@ namespace format { return {val, p}; } + template + constexpr std::pair, std::size_t> parseNumberWithMessage(std::string_view s, std::size_t pos, std::string_view numberName) + { + try { + return parseNumber(s, pos); + } catch(const std::out_of_range&) { + throw std::out_of_range{std::string{numberName} + " is too big; maximum is " + std::to_string(std::numeric_limits::max())}; + } + } + namespace exceptions { struct incomplete_specifier : std::invalid_argument { using std::invalid_argument::invalid_argument; @@ -263,7 +273,7 @@ namespace format { }; bool hasExplicitArgIndex = false; - const auto argIndex = parseNumber(fmt, pos); + const auto argIndex = parseNumberWithMessage(fmt, pos, "Argument index or field width"); if (argIndex.first) { checkEos(argIndex.second); @@ -315,8 +325,8 @@ namespace format { checkEos(); ArgIndex maxArg = 0; - const auto argIndexOrNextArg = [&pos, &nextArg, &maxArg, checkEos, fmt](decltype(*format_specifier::minWidth)& argIndex) { - const auto index = parseNumber(fmt, pos); + const auto argIndexOrNextArg = [&pos, &nextArg, &maxArg, checkEos, fmt](decltype(*format_specifier::minWidth)& argIndex, std::string_view numberName) { + const auto index = parseNumberWithMessage(fmt, pos, numberName); checkEos(index.second); if (index.first && fmt[index.second] == '$') { @@ -336,22 +346,22 @@ namespace format { maxArg = std::max(maxArg, argIndex); }; - const auto handleWidthPrecision = [&pos, argIndexOrNextArg, fmt, checkEos](decltype(format_specifier::minWidth)& indexOrValue, bool allowEmpty = false){ + const auto handleWidthPrecision = [&pos, argIndexOrNextArg, fmt, checkEos](decltype(format_specifier::minWidth)& indexOrValue, std::string_view numberName, bool allowEmpty = false){ bool asArg = false; if (fmt[pos] == '*') { asArg = true; ++pos; checkEos(); - argIndexOrNextArg(indexOrValue.emplace()); + argIndexOrNextArg(indexOrValue.emplace(), numberName); } else { - const auto width = parseNumber(fmt, pos); - if (width.first) + const auto value = parseNumberWithMessage(fmt, pos, numberName); + if (value.first) { - indexOrValue = *width.first; - pos = width.second; + indexOrValue = *value.first; + pos = value.second; } else if (allowEmpty) { @@ -360,14 +370,14 @@ namespace format { } return asArg; }; - spec.flags.widthAsArg = handleWidthPrecision(spec.minWidth); + spec.flags.widthAsArg = handleWidthPrecision(spec.minWidth, "Field width"); if (fmt[pos] == '.') { ++pos; checkEos(); - spec.flags.precisionAsArg = handleWidthPrecision(spec.precision, true); + spec.flags.precisionAsArg = handleWidthPrecision(spec.precision, "Precision", true); } { diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp index 85c6ecf..7659468 100644 --- a/include/cxxformat/runtime.hpp +++ b/include/cxxformat/runtime.hpp @@ -184,7 +184,7 @@ namespace format { throw exceptions::incomplete_specifier{"Incomplete format specifier “%s” at end of format string"_format(fmt.substr(nextSpec))}; } catch(const std::exception& e) { constexpr std::size_t showPartSize = 10; - throw std::invalid_argument{"%s when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; + throw std::invalid_argument{"%s; when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; } } } -- cgit v1.2.3-54-g00ecf