diff --git a/doc/syntax.rst b/doc/syntax.rst index ab8787df256c..f6f43afb3a8a 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -157,7 +157,8 @@ displayed after the decimal point for a floating-point value formatted with ``'f'`` and ``'F'``, or before and after the decimal point for a floating-point value formatted with ``'g'`` or ``'G'``. For non-number types the field indicates the maximum field size - in other words, how many characters will be -used from the field content. The *precision* is not allowed for integer values. +used from the field content. The *precision* is not allowed for integer values +or pointers. Finally, the *type* determines how the data should be presented. diff --git a/format.cc b/format.cc index 01380504a099..0c400aef4eaa 100644 --- a/format.cc +++ b/format.cc @@ -561,9 +561,13 @@ class fmt::internal::ArgFormatter : if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) FMT_THROW(FormatError("invalid format specifier for char")); typedef typename fmt::BasicWriter::CharPtr CharPtr; + Char fill = static_cast(spec_.fill()); + if (spec_.precision_ == 0) { + std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); + return; + } CharPtr out = CharPtr(); if (spec_.width_ > 1) { - Char fill = static_cast(spec_.fill()); out = writer_.grow_buffer(spec_.width_); if (spec_.align_ == fmt::ALIGN_RIGHT) { std::fill_n(out, spec_.width_ - 1, fill); @@ -615,6 +619,8 @@ void fmt::BasicWriter::write_str( if (*str_value) str_size = std::char_traits::length(str_value); } + if (spec.precision_ >= 0 && spec.precision_ < str_size) + str_size = spec.precision_; write_str(str_value, str_size, spec); } @@ -1011,9 +1017,10 @@ const Char *fmt::BasicFormatter::format( } else { FMT_THROW(FormatError("missing precision specifier")); } - if (arg.type != Arg::DOUBLE && arg.type != Arg::LONG_DOUBLE) { + if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { FMT_THROW(FormatError( - "precision specifier requires floating-point argument")); + fmt::format("precision not allowed in {} format specifier", + arg.type == Arg::POINTER ? "pointer" : "integer"))); } } diff --git a/test/format-test.cc b/test/format-test.cc index 6aa79cb8eecb..d1f4f074f23f 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -824,53 +824,42 @@ TEST(FormatterTest, Precision) { FormatError, "missing precision specifier"); EXPECT_THROW_MSG(format("{0:.2", 0), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42u), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42u), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42l), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42l), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42ul), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42ul), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42ll), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42ll), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2}", 42ull), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", 42ull), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_EQ("1.2", format("{0:.2}", 1.2345)); EXPECT_EQ("1.2", format("{0:.2}", 1.2345l)); EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast(0xcafe)), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in pointer format specifier"); EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast(0xcafe)), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in pointer format specifier"); - EXPECT_THROW_MSG(format("{0:.2}", 'x'), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.2f}", 'x'), - FormatError, "precision specifier requires floating-point argument"); - - EXPECT_THROW_MSG(format("{0:.2}", "str"), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.2f}", "str"), - FormatError, "precision specifier requires floating-point argument"); - - EXPECT_THROW_MSG(format("{0:.2}", TestString()), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.2f}", TestString()), - FormatError, "precision specifier requires floating-point argument"); + EXPECT_EQ(" ", format("{0:3.0}", 'x')); + EXPECT_EQ("st", format("{0:.2}", "str")); + EXPECT_EQ("te", format("{0:.2}", TestString("test"))); } TEST(FormatterTest, RuntimePrecision) { @@ -893,7 +882,7 @@ TEST(FormatterTest, RuntimePrecision) { EXPECT_THROW_MSG(format("{0:.{x}}", 0), FormatError, "invalid format string"); EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 0), FormatError, "argument index out of range"); @@ -920,51 +909,40 @@ TEST(FormatterTest, RuntimePrecision) { FormatError, "precision is not integer"); EXPECT_THROW_MSG(format("{0:.{1}}", 42, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 42u, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42u, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 42l, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42l, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 42ul, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42ul, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 42ll, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42ll, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}}", 42ull, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", 42ull, 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in integer format specifier"); EXPECT_EQ("1.2", format("{0:.{1}}", 1.2345, 2)); EXPECT_EQ("1.2", format("{1:.{0}}", 2, 1.2345l)); EXPECT_THROW_MSG(format("{0:.{1}}", reinterpret_cast(0xcafe), 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in pointer format specifier"); EXPECT_THROW_MSG(format("{0:.{1}f}", reinterpret_cast(0xcafe), 2), - FormatError, "precision specifier requires floating-point argument"); - - EXPECT_THROW_MSG(format("{0:.{1}}", 'x', 2), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.{1}f}", 'x', 2), - FormatError, "precision specifier requires floating-point argument"); - - EXPECT_THROW_MSG(format("{0:.{1}}", "str", 2), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.{1}f}", "str", 2), - FormatError, "precision specifier requires floating-point argument"); - - EXPECT_THROW_MSG(format("{0:.{1}}", TestString(), 2), - FormatError, "precision specifier requires floating-point argument"); - EXPECT_THROW_MSG(format("{0:.{1}f}", TestString(), 2), - FormatError, "precision specifier requires floating-point argument"); + FormatError, "precision not allowed in pointer format specifier"); + + EXPECT_EQ(" ", format("{0:3.{1}}", 'x', 0)); + EXPECT_EQ("st", format("{0:.{1}}", "str", 2)); + EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2)); } template