2254 Commits

Author SHA1 Message Date
Victor Zverovich
f87a75e152 Clean up duration_formatter and simplify duration sign tests 2026-10-25 09:34:12 -07:00
Marvel Harisson
2e60743c67
Fix formatting of nullptr with FMT_COMPILE and to_string (#4993)
* Fix formatting of nullptr with FMT_COMPILE and to_string

std::nullptr_t is implicitly convertible to string_view and const char*,
so compiled format strings and fmt::to_string treated nullptr as a null
C string: FMT_COMPILE("{}!") crashed and FMT_COMPILE("{}") and to_string
threw "string pointer is null". Format it as a pointer ("0x0") like the
runtime API does.

* Write nullptr as a pointer in detail::write

detail::write<Char>(out, nullptr) picked the const Char* overload, so
fmt::to_string(nullptr) and FMT_COMPILE("{}") threw "string pointer is
null". Add a nullptr_t overload that forwards to the const void* one.
2026-10-25 09:30:45 -07:00
Marvel Harisson
88a0ae2020
Use the locale decimal point in localized hexfloat formatting (#4992)
With the L flag, floating-point values are formatted with the locale's
decimal point, but the hexfloat presentation ('a'/'A') always emitted
'.'. Replace it with the locale decimal point when the value is
localized, like the other floating-point presentations do.
2026-10-24 11:15:30 -07:00
Mohith
434e30f81f
Fix signs in floating-point duration formatting (#4981) 2026-10-24 10:28:23 -07:00
Victor Zverovich
6b528261ec Fix allocation size overflow in detail::allocator
allocator<T>::allocate(n) computed n * sizeof(T) with only an FMT_ASSERT
guarding against overflow, which is a no-op under NDEBUG. On 32-bit targets
a width of 2^30 or more with a 4-byte character type, e.g.
fmt::format(L"{:>{}}", s, 1100000000), wraps the byte count around, and
basic_memory_buffer records a capacity much larger than the block it got,
so padding is written past the end of the allocation.

Make the check unconditional and throw std::bad_alloc, as std::allocator
did before it was replaced in 11.1.0. Move <new> back to format.h since the
header now throws std::bad_alloc.
2026-10-24 10:03:53 -07:00
DeviousCardi
011e5a911f
Fix ISO week number and year for negative years (#4989)
iso_year_weeks computed the weekday of December 31 with truncating
division and a remainder that is negative for negative years, so it
returned the wrong number of ISO weeks for years before year 0. As a
result %V and %G/%g were wrong around the start and end of such years,
e.g. 1 January of year -1 was formatted as week 52 of year -2 instead
of week 53, and 31 December of year -2 as week 1 of year -1.

Use floored division and a non-negative remainder.


Claude-Session: https://claude.ai/code/session_01Y1b55sEL1gA1c3FCMcQCbm

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 09:59:35 -07:00
fhgffy
0fc31633e7
Fix weekday and day of year when formatting year_month_day (#4978)
The year_month_day formatter only filled in tm_year, tm_mon and tm_mday,
so specifiers that depend on the weekday or the day of the year (%a, %A,
%u, %w, %j, %U, %W, %V, %G, %g) were formatted from zero-initialized
fields, e.g. every date was printed as a Sunday. Compute tm_wday and
tm_yday from the date before passing it to the tm formatter.
2026-10-18 07:14:35 -07:00
Mayank Jaiswal
89a7f4a7ca
Fix FMT_COMPILE with ranges, tuples and maps (#4974)
nested_format_specs::write formatted the body of a padded value into a
temporary buffer using a context of the same type as the outer one. This
only works when the outer context writes to an appender, so FMT_COMPILE
and format_to with other output iterators failed to compile even when no
width was given.

Use a buffer context that takes arguments and locale from the outer
context when the outer context doesn't write to an appender.

Fixes #4949.
2026-10-18 07:11:07 -07:00
Victor Zverovich
4ded41f186 Simplify pre-epoch local_time formatting
Floor the seconds before calling gmtime instead of adjusting tm_sec, and
keep the subsecond remainder in the common duration type, which fixes
overflow for narrow reps such as duration<int16_t, std::micro>.
2026-10-18 07:02:27 -07:00
Kartikey Negi
6ba294f5a6
Fix subsecond formatting for pre-epoch local_time (#4975) (#4976) 2026-10-18 06:45:15 -07:00
Victor Zverovich
18164eaf8b Simplify printf flag tests
The sign is cleared for unsigned conversions regardless of whether the
'+' or the space flag was given, so "% o" and "% X" only repeat what
"%+o" and "%+X" already cover.
2026-10-17 07:16:33 -07:00
fhgffy
01bef95d34
Ignore '+' and space flags for unsigned printf conversions (#4977)
C only applies the '+' and space flags to signed conversions, so
fmt::sprintf("%+u", 42) should give "42" like printf does, not "+42".
The same goes for %o, %x and %X.
2026-10-17 07:11:43 -07:00
Victor Zverovich
615acd84dd Fix use-after-free in basic_memory_buffer move assignment
operator= deallocates the destination's storage but leaves it pointing at
the freed block with a stale capacity and size. When the allocator doesn't
propagate and the two allocators are unequal, move_alloc copies into that
pointer rather than taking over the source's storage, so it writes to freed
memory and the destructor then frees the same block again.

Restore the inline buffer after deallocating and before calling move().
2026-10-16 12:44:14 -07:00
Vladislav Shchapov
35c58f084e
Fix hexfloat zero padding: zero padding must be placed after the sign and "0x" prefix (#4972)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
2026-10-08 13:06:28 -07:00
Victor Zverovich
62295c1ce2 Simplify compiled variant and expected tests
Drop the assertions that return from write_escaped_alternative before
reaching the advance_to call they were added to cover: the std::string
and char alternatives take the has_to_string_view and is_same_v<T, Char>
early returns, and expected<void, int> never calls the function at all.
All four are already checked in std-test.cc.

Merge format_unexpected into format_expected since both are guarded by
__cpp_lib_expected and the latter was a single assertion, and note that
std::unexpected has a formatter of its own so the adjacent unexpected(42)
checks are not duplicates.
2026-10-08 12:00:06 -07:00
Mishmish Dev
4faf707610
Enable ASan and UBSan in supported Linux CI builds (#4969)
* Fix null allocator reference in non-propagating move test

* Test ostream chunking with a real buffer

* Initialize the file pointer when constructing a buffer writer

* Enable ASan and UBSan in supported Linux CI builds

* Limit sanitizer CI to recent Clang and GCC configurations
2026-10-08 11:42:47 -07:00
Victor Zverovich
7338754774 Simplify high-precision float formatting and tests
Unify the duplicated trailing-zero computation in write_fixed and remove
the redundant guards around fill_n. Reduce the constexpr branch of
basic_memory_buffer::grow to a single construct loop that uses the
buffer's own allocator.

In tests, drop the MSVC version checks that FMT_USE_CONSTEVAL already
implies, replace a loop computing 2^-900 with the 0x1p-900 literal, and
report high_precision_long_double as skipped rather than passed when
long double is not an extended-precision type.
2026-10-08 11:27:22 -07:00
fhgffy
10cda465ed
Fix output position for compiled variant and expected formatting (#4968) 2026-10-07 12:32:48 -07:00
Langning Zhang
c4927e6086
Fix high-precision floating-point formatting (#4960)
* Fix trailing zeros in high-precision floating-point formatting

* fix: preserve high-precision float digits and constexpr growth

* Simplify constexpr allocation and precision tests
2026-10-07 07:16:10 -07:00
Victor Zverovich
3e0ecb247b Remove redundant escape tests
The U+1FAE0 and U+31350 cases added in #4962 duplicate the is_printable
checks for the same code points. For a code point >= 0x80 needs_escape is
exactly !is_printable(cp), and the existing U+1F984 case already covers a
printable 4-byte sequence passing through unmodified.
2026-10-04 08:30:56 -07:00
FenjuFu
85a8f9117d
Update is_printable to Unicode 17.0 (#4962)
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
2026-10-04 07:59:04 -07:00
James Panayis
9197f51593
Optimize buffered padding with chunked fills (#4959)
Fill available appender storage in bulk, continuing across buffer growth and flushes
Preserve the constexpr-compatible scalar fallback
Add testing of large padded sizes
2026-10-02 11:44:42 -07:00
Victor Zverovich
5da4e9a3fb Run format tests without locale support
Build format-test.cc with FMT_USE_LOCALE=0 in nolocale-test instead of a
separate test file. This replaces two ad-hoc checks with the whole format
test suite. The locale-dependent tests were guarded by the removed
FMT_STATIC_THOUSANDS_SEPARATOR macro, so the guard never took effect.
2026-09-27 10:15:42 -07:00
Tiago
1ffdc7687f
Use classic locale grouping without locale support (#4947) 2026-09-27 08:19:52 -07:00
Victor Zverovich
0482001875 Format enums annotated with fmt::as_underlying
Format an enum as its underlying value if the enum is annotated with
fmt::as_underlying, the equivalent of std::format_as_underlying from P4267:

  enum class [[=fmt::as_underlying]] color { red = 1, green = 2, blue = 4 };
  fmt::format("{:04x}", color::blue);  // "0004"

The value is mapped before type erasure via the existing
formatter<T>::format_as hook, as in the std::byte formatter, so it is stored
as a builtin argument and can be used as dynamic width or precision.
Combining the annotation with fmt::as_identifiers is rejected with a
static_assert.
2026-09-26 11:07:11 -07:00
Victor Zverovich
6d71f74624 Remove redundant printf tests
%.0i and %.0u take the same path as %.0d because 'i' and 'u' are normalized
to 'd', the uppercase variants only set the upper flag which is unobservable
when there are no digits, and %-5.0d cannot be distinguished from %5.0d
because the output is empty.
2026-09-20 17:30:26 -07:00
Sachit Madaan
cfb57bfa3a
Fix printf conversion of a zero value with a precision of zero (#4942) 2026-09-19 07:49:15 -07:00
Wu Shuwen
fd0a9b6620
Fix format_as for tuple-like types with ranges.h (#4945) 2026-09-18 15:16:28 -07:00
Nebojša Cvetković
e4751cc709
Don't require std::locale in chrono.h when FMT_USE_LOCALE is 0 (#4921)
tm_writer takes a locale_ref and the localized flag instead of a std::locale
reference, and resolves the locale the same way numeric {:L} does: classic
unless localized, otherwise the one passed to the formatting function or the
global one. The facet calls are confined to three shims, so with locale
support disabled nothing pulls in std::locale. get_locale, which existed only
to materialize and own a std::locale, is no longer needed.

The std::tm formatter treated a missing locale argument as the classic locale
rather than the global one, and since #4935 routes {:L} on weekday and month
through it, {:L} without a locale argument ignored the global locale for all
calendar types. It now uses the global locale, as numeric {:L} does. Output
with an explicit locale is unchanged. With FMT_USE_LOCALE=0, {:L} no longer
consults the global locale, matching numeric formatting under that option. An
-Os test program loses all 16 of its std::locale and time_put symbols.
2026-09-16 08:37:57 -07:00
Tiago
e5d13d826f
Make nolocale-test actually disable locale (#4939)
nolocale-test defines FMT_STATIC_THOUSANDS_SEPARATOR, which stopped doing
anything in b90b4bc9 ("Remove FMT_STATIC_THOUSANDS_SEPARATOR in favor of
FMT_USE_LOCALE"). That macro no longer appears anywhere under include/, so
since then the target has compiled src/format.cc with locale support enabled.

It is not a dead target: the pedantic CI jobs build it (linux.yml, macos.yml
both pass -DFMT_PEDANTIC=ON), and it passes. So the configuration looks
covered while nothing actually tests it. #4627 - a locale-off build break -
landed during that window.

Define FMT_USE_LOCALE=0 instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-11 12:39:06 -07:00
cui fliter
6aa4a5f6f8
Fix debug string formatting for range formatters (#4936)
Signed-off-by: cuishuang <imcusg@gmail.com>
2026-09-11 08:43:46 -07:00
Victor Zverovich
1da57c78d4 Simplify calendar_padding test 2026-09-11 08:31:42 -07:00
IfkumRfnl
2012961e3e
Fix width and alignment for calendar types (#4935) 2026-09-11 08:22:02 -07:00
Victor Zverovich
56fcd0b945 Simplify named_arg_count test 2026-09-11 08:05:34 -07:00
IfkumRfnl
854fc9567a
Fix argument count in dynamic stores with named arguments (#4933) 2026-09-11 08:01:06 -07:00
Victor Zverovich
44f2c7ae01 Make base.h a compatibility header
Complete the header swap by making core.h canonical and updating code, tests, documentation, and tooling.
2026-09-07 17:11:35 -07:00
IfkumRfnl
8a7aea0422
Fix out-of-bounds read in error code format parsing (#4934) 2026-09-07 11:35:29 -07:00
Ethan Stoner
2d4d8a13c2
Build nolocale-test with /utf-8 on MSVC (#4919)
nolocale-test compiles src/format.cc directly, so it does not pick up the
/utf-8 that CMakeLists.txt adds to the fmt target. On MSVC the static_assert
in base.h then fires: "Unicode support requires compiling with /utf-8".

The target only exists under FMT_PEDANTIC, which the Windows workflow does not
set, so this has not shown up in CI. unicode-test already guards the same flag
the same way.
2026-09-06 13:46:11 -07:00
Avionic Harshit
61e3b92396
Support fill, alignment and width in range and tuple formatters (#4910) 2026-09-06 08:47:40 -07:00
Satyakam Mishra
b4b1657c2a
Fix debug format width calculation (#4902) 2026-09-04 09:17:58 -07:00
Victor Zverovich
bc82c408a6 Test dynamic width in nested_formatter 2026-09-02 10:53:56 -07:00
Victor Zverovich
cbb264122a Simplify nested_formatter context handling
Format nested values directly through the stored formatter so dynamic
specifications resolve against the enclosing operation.
2026-09-02 10:14:09 -07:00
Eduardo Gómez
d0cec7ae2e
Fix inconsistent display width for emoji outside East Asian Wide ranges (#4896)
* Widen display_width_of() to cover Emoji_Presentation code points

fmt::detail::display_width_of() only treated East Asian Wide/Fullwidth
code points and two hand-picked emoji ranges as two columns wide, so
emoji outside those ranges (e.g. the Dingbats block: cross mark U+274C,
white heavy check mark U+2705) were measured as one column even though
most terminals render them double-width. This produced visibly
inconsistent padding under {:^N} compared to CJK text (fixes #4851).

Replaced the ad hoc boolean expression with a sorted table of ranges
(East Asian Wide/Fullwidth plus the full Emoji_Presentation set from
Unicode's emoji-data.txt) looked up via binary search, and added
regression tests covering the original report plus edge cases:
multiple emoji, mixed emoji/CJK/ASCII content, precision truncation,
alignment/fill variants, newly covered emoji ranges, and regional
indicator (flag) pairs.

* Make wide_cp_ranges C++11-compatible; apply clang-format

* Move wide_cp_range struct into display_width_of()

* Derive wide_cp_ranges from East_Asian_Width data, restore constexpr

* Move wide_cp_ranges to display_width_of function body

* Update format.h

* Update wide_cp_ranges array to include comments

* Add inline to display_width_of and add 1F300-1F5FF and 1F900-1F9FF ranges to match [format.string.std]

* Update display_width_of lo hi varible declaration

---------

Co-authored-by: Eduardo Gomez Saldias <50159560+edugomez102@users.noreply.github.com>
2026-08-30 08:05:50 -07:00
Victor Zverovich
bb1bcede25 Stop enabling C++26 reflection by default
Don't inject -freflection when building the module, so it isn't forced onto
the module or its importers. Build enum-test unconditionally (it is a no-op
without reflection) and drop the now-unneeded reflection detection. Enable
reflection explicitly in the g++-16 C++26 CI job.
2026-08-29 16:28:09 -07:00
Victor Zverovich
9375eb3792 Simplify enum identifier handling and tidy reflection docs
Use std::string_view directly for enumerator identifiers, dropping the
identifier wrapper and inlining count_enumerators. Update the fmt/enum.h
documentation to point at cppreference and drop an inaccurate note. Also
remove the no-longer-needed -Wno-sfinae-incomplete option for format-test.
2026-08-29 15:11:31 -07:00
TheNonConformist
8a84280f26
Suppress GCC 16 incomplete type warning (#4893) 2026-08-29 15:09:36 -07:00
Avi Kivity
e589a16ecb
Look up sparse enum identifiers in a hash table instead of a linear search (#4899)
Enums that are too sparse for the index table were formatted by scanning
all enumerators. Replace the scan with an open-addressed hash table with
linear probing, sized to the smallest power of two that keeps the load
factor at or below 0.5. This guarantees a free slot, which terminates the
probe sequence and doubles as the not-found result because identifiers
are never empty.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 07:47:28 -07:00
Advit Arora
e76a9520a3
Right-align inf and nan with a bare width (#4895)
fmt::format("{:6}", nan) gave "nan   " where std::format and printf("%6f")
give "   nan". write_nonfinite was the only numeric write path taking
write_padded's align::left default, so a width with no explicit align
left-aligned inf and nan while every finite value right-aligned. That
default only reaches align::none, leaving explicit <, > and ^ and the 0
flag unchanged.
2026-08-27 08:54:40 -07:00
Avi Kivity
e27cc20bd9
Format enums annotated with fmt::as_identifiers (requires C++26 reflection) (#4885)
* Format enums annotated with fmt::as_identifiers (requires C++26 reflection)

Format an enum as the identifier of the matching enumerator if the enum is
annotated with fmt::as_identifiers:

  enum class [[=fmt::as_identifiers]] color { red, green, blue };
  fmt::format("{}", color::green);  // "green"

Values that don't match any enumerator are represented as their underlying
value in decimal before applying string formatting.

Identifiers are retrieved via C++26 reflection (P2996) and the annotation
via P3394. FMT_USE_REFLECTION is autodetected and can be overridden by the
user; without reflection the header is empty.

The header is also part of the fmt module, but, unlike with headers, whether
it provides anything is decided when the module is compiled, so the module
build detects reflection and enables it if the configured standard allows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Look up enum identifiers by index when the values are dense

The formatter for enums annotated with fmt::as_identifiers did a linear
search over all enumerators. Build a table indexed by the distance from
the smallest enumerator value instead, with empty string views in the
holes, which reduces the lookup to a bounds check and one load.

The table is only used if at least 70% of its elements are identifiers,
limiting its size to 10/7 of the number of enumerators. Sparser enums
keep using the linear search. Distances are computed in uint64_t so that
enums with negative values and values spanning the whole range of the
underlying type are handled without overflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Suppress -Wsfinae-incomplete in format-test on GCC 16

GCC 16 warns when a type is completed after it failed to be complete in a
SFINAE context. format-test does this deliberately to check that formatting
of incomplete types works, so the warning is a false positive there and
breaks the build with -Werror.

* Test GCC 16 on CI

GCC 16 is the first compiler with C++26 reflection support, which is needed
by fmt/enum.h, so add a job that builds with it in C++26 mode. It comes from
the ubuntu-toolchain-r/test PPA since Ubuntu 24.04 only ships GCC 14.

Also report when reflection is not detected to make it visible that
enum-test was skipped.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 09:31:28 -07:00
Victor Zverovich
21d4cc15c8 Reject sign-aware zero padding in std::exception and error_code formatters
Treat a leading '0' as the zero flag rather than the start of the width so
it is rejected (as in the core formatter) instead of being silently consumed.
Also trim the exception_align test to the cases this actually introduces.
2026-08-16 10:00:41 -07:00