Skip to content

Move clang-tidy from trunk to a pre-commit hook, and fix the compile DB - #270

Merged
helly25 merged 6 commits into
mainfrom
clang_tidy_precommit
Aug 8, 2026
Merged

Move clang-tidy from trunk to a pre-commit hook, and fix the compile DB#270
helly25 merged 6 commits into
mainfrom
clang_tidy_precommit

Conversation

@helly25

@helly25 helly25 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Adopts helly25/xff#386 here, plus two compile-DB fixes found while verifying it.

clang-tidy out of trunk

trunk pinned clang-tidy 16, which mis-parses this C++23 codebase, and its --export-fixes runs rewrote the working tree with build-breaking "fixes". trunk.io 403s any modern clang-tidy download, so a version bump there is not possible. Confirmed here: which clang-tidy resolves to .trunk/tools/clang-tidy = LLVM 16.0.3, while the hermetic toolchain is 22.1.8.

  • Drop clang-tidy from .trunk/trunk.yaml.
  • Add tools/clang_tidy.sh: resolves the hermetic clang-tidy (mirroring clang_format.sh's ladder), gates on >= 18, and reports only — never --fix. The version gate is load-bearing: a plain clang-tidy on PATH may well be trunk's 16, and it is rejected (verified).
  • Add an opt-in clang-tidy pre-commit hook, plus a CI job that builds the compile DB, uploads it as an artifact, and runs just that hook. The main pre-commit job skips it.

.clang-tidy latent bugs

  • WarningsAsErrors: true — a check-name glob, not a bool, so it matched nothing and findings never escalated. Now '*'.
  • bugprone-signed-char-misuse.CharTypdefsToIgnore — misspelled (missing e), so the option was dead. Surfaced by --verify-config.
  • llvm-header-guard and llvm-prefer-static-over-anonymous-namespace disabled: they contradict STYLE_CPP.md. The former, with no header root, suggests USERS_MARCUS_DOCUMENTS_..._SCOPED_STREAM_H for every header.

compile_commands-update.sh was generating the wrong compile DB

bazel run @…//:refresh_all --config=clang does nothing useful: that flag only configures the build of the extractor tool, and never reaches the aquery it runs internally. Every recorded command therefore named the autodetected Apple clang, while clangd and clang-tidy used the hermetic one — which is why clang-tidy died on 'concepts' file not found.

This fixes clangd too, which had the same mismatch.

Test

  • pre-commit run -a green.
  • clang-tidy 22 now runs with zero hard errors against the regenerated DB.
  • Verified against the real pin (no --override_module): dropped 196 duplicate source entries. Exec-only files (mbo/mope/ini.h, mbo/diff/internal/update_absl_log_flags.h) are retained; status_builder.cc went 2 entries -> 1.
  • Both hook skip paths exit 0 (no compile DB; only trunk's clang-tidy 16 on PATH), and the hook does not run at the default commit stage.

Deliberately not a gate yet

The CI job is continue-on-error: true and the hook is stages: [manual]. WarningsAsErrors: '*' against a tree that is not yet clean would block nearly every commit — one sampled file alone yields ~500 findings, ~80% from cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, which flags every operator[].

Promoting it to a real gate: re-tune .clang-tidy for the noisy new-in-22 checks, sweep the residual findings, then drop both stages: [manual] and continue-on-error. Recorded in the hook description.

Note the whole recipe is verified on macOS; the ubuntu runner path gets its first exercise from this PR's CI, which reports rather than blocks.

helly25 added 4 commits August 8, 2026 13:55
trunk pinned clang-tidy 16, which mis-parses this C++23 codebase, and its
`--export-fixes` runs rewrote the working tree with build-breaking
"fixes". trunk.io 403s any modern clang-tidy download, so a version bump
there is not possible.

- Drop clang-tidy from trunk (.trunk/trunk.yaml).
- Add tools/clang_tidy.sh, resolving the hermetic clang-tidy (>= 18,
  mirroring clang_format.sh's ladder) and reporting only, never --fix.
  The version gate matters: a plain `clang-tidy` on PATH may well be
  trunk's 16.
- Add an opt-in `clang-tidy` pre-commit hook, plus a CI job that builds
  the compile DB, uploads it as an artifact and runs just that hook. The
  main pre-commit job skips it.

.clang-tidy carried two latent bugs: `WarningsAsErrors: true` is a check
glob, not a bool, so it matched nothing and findings never escalated; and
`bugprone-signed-char-misuse.CharTypdefsToIgnore` was misspelled, leaving
the option dead. Both fixed. `llvm-header-guard` and
`llvm-prefer-static-over-anonymous-namespace` are disabled as they
contradict STYLE_CPP.md.

compile_commands-update.sh was generating the wrong compile DB entirely:
`--config=clang` only configures the build of the extractor tool and
never reaches the aquery it runs internally, so every recorded command
named the autodetected Apple clang while clangd and clang-tidy used the
hermetic one. It now passes --bcce-compiler explicitly, plus an
-isysroot copt on Darwin (the hermetic clang ships libc++ but no system C
headers). It also adopts --bcce-prefer-target-config, dropping 196
duplicate exec-configuration entries, which requires the extractor pin
bump to 6eb3ff1.

The CI job is continue-on-error for now: WarningsAsErrors: '*' against a
tree that is not yet clean would block everything. Promoting it to a real
gate means re-tuning .clang-tidy for the noisy new-in-22 checks, sweeping
the residual findings, then dropping both `stages: [manual]` and
continue-on-error.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
Job-level continue-on-error masked everything, including a failure to
build the compile DB -- the one thing in that job that must gate. Move it
to the pre-commit step, so only the not-yet-clean findings are tolerated
while a broken extractor or toolchain fails the job.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
Two defects the first CI run of the clang-tidy job exposed.

The compile DB was extracted without the generated headers existing, so
the extractor could not resolve translation units including them
("hash_mangle_seed_gen.h is missing") and they landed in the DB degraded
-- which later reads as clang-tidy findings rather than the build artifact
gap it really is. Build them first. Generated headers are the only build
output a TU needs to parse, so this builds just those (3 targets, ~7s)
rather than all of //..., with a query that stays correct as generated
files are added and a guard that fails loudly if it ever matches nothing.

clang-tidy 22.1.8 also SEGFAULTS (exit -11) in
abseil-unchecked-statusor-access: its dataflow analysis crashes on
mbo/strings/strip.cc, reproducibly on both Linux and macOS. Disable the
check; with it off the same file exits normally.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
The job finished, but its lint step took 24 minutes: ~50s per translation
unit over all 81 sources. More parallelism is not the lever -- pre-commit
already splits the files across every core (the run showed 21 files per
invocation, i.e. 4 concurrent chunks on a 4-core runner), so the runner is
already saturated and adding xargs -P inside the hook would only
oversubscribe it.

Scope the work instead: a branch lints only the sources it changed, while
`main` still lints the whole tree after merge. Requires fetch-depth 0 to
diff against main.

Known gap, accepted deliberately: editing only a header changes findings
in the sources that include it, and none of those appear in the branch's
changed-file set. The post-merge run on `main` catches that.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
@helly25
helly25 requested a review from Fab-Cat August 8, 2026 14:05
@helly25
helly25 enabled auto-merge (squash) August 8, 2026 14:06
helly25 added 2 commits August 8, 2026 15:25
A branch run lints just the sources that branch changed, so its compile
DB describes a near-empty lint and is not worth storing. The `main` run
is the one that lints the whole tree, and its DB is what reproduces a
finding locally against the exact same commands.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
Artifacts are otherwise only visible in the panel at the bottom of a run
page, unlabelled. Write a job summary instead: DB size, how many
duplicate entries survived deduplication, the lint scope actually used,
and -- on main, where the artifact exists -- a direct link to it.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
@helly25
helly25 merged commit 4078dc5 into main Aug 8, 2026
23 checks passed
@helly25
helly25 deleted the clang_tidy_precommit branch August 8, 2026 14:52
helly25 added a commit that referenced this pull request Aug 8, 2026
xff's copy had grown several clarifications worth having here, adapted to
mbo's names and to what mbo actually has.

Notably one is a REVERSAL of the existing rule. The doc said "a by-value
std::string_view is never const"; it now follows ordinary
const-correctness - const when the view is never mutated, non-const
exactly when it is resliced or reassigned - because misc-const-correctness
(enabled) flags a never-mutated view that is not const. The corollary and
a worked example are included, plus the companion rule that a read-only
string parameter is std::string_view by value rather than
const std::string&.

Also adopted: trailing commas on every element of a registry-style table;
"a value or error type IS absl::StatusOr<T>"; matchers named unqualified
via `using`; mbo::testing::EqualsText with a DropIndent raw-string golden
for multi-line text; ElementsAreArray with a trailing comma for long
matcher lists; SizeIs/IsEmpty on the container instead of matching an
extracted .size()/.empty(); and bashtest's content matchers with the
whole-text anchoring caveat.

The clang-tidy paragraph was also stale - it still claimed CI does not run
clang-tidy, which #270 changed. It now describes the dedicated CI job,
the changed-files-on-a-branch vs whole-tree-on-main scoping, and that it
is report-only until the sweep lands.

Deliberately not copied: xff's XFF_ macro prefix, xff/ paths and flags,
its C++23 baseline, its com_helly25_bashtest repo name (mbo uses the
default), its no-shell-grep-in-bashtests hook (mbo has none), and its
diff_golden.bzl - the golden-file guidance instead points at mbo's own
//mbo/diff:diff.bzl.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
helly25 added a commit that referenced this pull request Aug 9, 2026
xff's copy had grown several clarifications worth having here, adapted to
mbo's names and to what mbo actually has.

Notably one is a REVERSAL of the existing rule. The doc said "a by-value
std::string_view is never const"; it now follows ordinary
const-correctness - const when the view is never mutated, non-const
exactly when it is resliced or reassigned - because misc-const-correctness
(enabled) flags a never-mutated view that is not const. The corollary and
a worked example are included, plus the companion rule that a read-only
string parameter is std::string_view by value rather than
const std::string&.

Also adopted: trailing commas on every element of a registry-style table;
"a value or error type IS absl::StatusOr<T>"; matchers named unqualified
via `using`; mbo::testing::EqualsText with a DropIndent raw-string golden
for multi-line text; ElementsAreArray with a trailing comma for long
matcher lists; SizeIs/IsEmpty on the container instead of matching an
extracted .size()/.empty(); and bashtest's content matchers with the
whole-text anchoring caveat.

The clang-tidy paragraph was also stale - it still claimed CI does not run
clang-tidy, which #270 changed. It now describes the dedicated CI job,
the changed-files-on-a-branch vs whole-tree-on-main scoping, and that it
is report-only until the sweep lands.

Deliberately not copied: xff's XFF_ macro prefix, xff/ paths and flags,
its C++23 baseline, its com_helly25_bashtest repo name (mbo uses the
default), its no-shell-grep-in-bashtests hook (mbo has none), and its
diff_golden.bzl - the golden-file guidance instead points at mbo's own
//mbo/diff:diff.bzl.

Signed-off-by: helly25 <6420169+helly25@users.noreply.github.com>
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.

2 participants