Skip to content

Update doctest to 2.5.3 and fmt to 12.0.0 - #196

Merged
martinus merged 2 commits into
mainfrom
update-third-party-deps
Aug 15, 2026
Merged

Update doctest to 2.5.3 and fmt to 12.0.0#196
martinus merged 2 commits into
mainfrom
update-third-party-deps

Conversation

@martinus

Copy link
Copy Markdown
Owner

Updates the meson wrap subprojects to their newest packaged versions, which as a side effect restores the local clang build that #195 was filed about.

dependency before after note
doctest 2.4.12 2.5.3
fmt 11.2.0 12.0.0 newest in wrapdb; upstream 12.2.0 has no meson packaging
abseil-cpp 20250814.1 unchanged already the newest wrapdb has, and the dependency is commented out

Why this fixes #195, and why the fix there was the wrong one

doctest 2.4.12 does not build with clang 22 at all — 153 errors and no binary:

error: '__COUNTER__' is a C2y extension [-Werror,-Wc2y-extensions]

DOCTEST_ANONYMOUS uses __COUNTER__, C2y standardises it, and clang 22 warns under werror=true. That is why gcc has been the only compiler able to build this suite locally, and it is why the structured-binding bug in #192 reached CI instead of being caught before the push.

Worth recording: commit 794aa86 tried to solve this with include_type: 'system', and it never worked. A diagnostic about what a macro expands to is reported against the line that used the macro, so it counts as this project's code no matter how the header was included. I've corrected that comment rather than leaving it asserting something untrue.

The regression 2.5.3 brings, which is why this is not a one-line wrap bump

2.5.3 fixes the warning by using __LINE__ instead of __COUNTER__ on clang 22+. But __LINE__ is only unique within one file, so two test cases on the same line collide as soon as a unity build puts their files in one translation unit:

error: redefinition of 'DOCTEST_ANON_TMP_9ITERATOR<std::tuple<Type, Rest...>>'
  test/unit/insert.cpp:9  <-  previous definition at test/unit/insert_or_assign.cpp:9

Both files open with TEST_CASE_MAP on line 9. This is not a corner case:

  • there is a Linux (gcc, C++17, unity) CI leg, and
  • every lane of scripts/mutate/mutate.py is --unity=on — there the failure would surface as every mutant returning compiler instead of a real verdict, i.e. a mutation run that looks like it ran and tells you nothing.

The fix takes __COUNTER__ back via DOCTEST_COUNTER, which is doctest's own customisation point (it only picks a default #if !defined), and silences the warning doctest was avoiding. Both halves are required — the flag alone leaves doctest choosing __LINE__ (unity stays broken), and the define alone reintroduces the warning as an error. compiler.has_argument() keeps the clang-only flag off gcc; verified it is absent from gcc's compile_commands.json and present in clang's.

Verification

Every configuration built and the full suite run (539 cases, ~5.4M assertions):

configuration result
clang C++17 / C++20 / C++23 pass
gcc C++17 pass
gcc unity (--unity-size=16, the CI leg) pass
clang unity — broken before the fix pass
gcc 32-bit (-m32, switches fmt to builtin) pass
gcc asan+ubsan, clang asan+ubsan (CI's flags) pass
libFuzzer fuzz_api / fuzz_string, corpus replay pass
scripts/test_mutate.py (155 hermetic tests) pass
end-to-end mutation run under clang unity lanes caught

Two local failures were checked against the baseline and are pre-existing and environmental, not from this change:

  • clang-tidy 22 flags 20 issues in unordered_dense.h, a file this PR does not touch — CI pins clang-tidy-18 precisely because check families grow between releases.
  • clang-tidy-18 cannot parse gcc 16's libstdc++ (system_error), so the lint job is not locally reproducible either way.

libc++ is not installed on this machine, so that leg is left to CI.

Follow-up

Item 1 of #195 (add -Wno-c2y-extensions to restore the local clang build) is superseded by this PR — the flag is here, but on its own it would not have been enough, since the real blocker after the bump is the unity collision. Items 2 and 3 (self-calibrating estimates, incremental --json) are untouched and still stand.

🤖 Generated with Claude Code

martinus and others added 2 commits August 15, 2026 20:19
doctest 2.4.12 does not build with clang 22 at all: DOCTEST_ANONYMOUS uses
__COUNTER__, C2y standardises it, and clang 22 rejects it under -Wc2y-extensions
with werror=true. That is 153 errors and no binary, which left gcc as the only
compiler this suite could be built with locally -- and a clang-only error has
already reached CI once because of it.

2.5.3 fixes that by switching the counter to __LINE__ on clang 22+, but __LINE__
is only unique within a file, so two test cases sharing a line number collide
once a unity build merges their files. unit/insert.cpp and unit/insert_or_assign.cpp
both open with TEST_CASE_MAP on line 9, so the clang unity build fails to compile.
That configuration is not exotic: there is a unity CI leg, and every lane of
scripts/mutate/mutate.py is --unity=on, where this would present as every mutant
coming back `compiler` rather than as an honest verdict.

So take __COUNTER__ back through DOCTEST_COUNTER, doctest's own customisation
point, and silence the extension warning that made doctest move away from it.
Both are needed; either alone leaves one of the two configurations broken.

Verified with clang and gcc across C++17/20/23, unity, 32 bit, asan+ubsan, the
libFuzzer targets, and one end-to-end mutation run that comes back `caught`.

abseil-cpp is already at the newest version wrapdb packages (20250814.1); the
newer upstream release has no wrap, and the dependency is commented out anyway.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The MSVC 32 bit legs failed the wrap bump, and not because of anything in this
repository: warning_level=3 and werror=true are inherited by subprojects, so
fmt was being compiled with /W4 /WX and fmt 12.0.0 emits a warning there that
11.2.0 did not (format.h:2509, 32 bit only).

Left alone that makes every future fmt release a coin flip -- a warning added
anywhere in a dependency's own code becomes a red build here, on whichever
platform happens to trigger it. -Werror is worth having for code this project
wrote, which is the same line already drawn by giving doctest include_type
'system', and by the default_options on the commented-out absl dependency.

The dependency has to be resolved after the add_global_arguments calls, since
resolving it declares fmt's target and meson refuses a global argument once any
target exists. Hoisting it into one variable also stops the test and fuzz
targets from being able to disagree about how fmt is found.

Verified fmt's translation units no longer carry -Werror while the project's
still do, across gcc, clang, both unity builds, 32 bit, asan+ubsan and fuzz.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@martinus
martinus merged commit cad5414 into main Aug 15, 2026
31 checks passed
@martinus
martinus deleted the update-third-party-deps branch August 15, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tooling papercuts from the mutation work: self-calibrating estimates, interruptible runs

1 participant