From 8b8db030184761b883041f7f0ed0234409a3c1a9 Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:21:50 +0000 Subject: [PATCH 1/4] Pin clang-tidy to LLVM 22 everywhere and add an opt-in pre-commit hook CI ran clang-tidy 21 while .clang-tidy already referenced a check that only exists in clang-tidy 22 and clang-format was pinned to 22, so local and CI runs could disagree on the check set. Move every pin to 22: - clang-tidy.yml and iwyu.yml: COMPILER_VERSION 22 (IWYU has a clang_22 branch). - docker/Dockerfile: install clang-tidy-22 (the unversioned package on Ubuntu 26.04 is 21) and symlink it as clang-tidy so CMake finds it. - FindClangTidy.cmake: prefer clang-tidy-22, and warn at configure time when the found major version differs from ClangTidy_EXPECTED_MAJOR_VERSION. - cmake.yml: add Clang-22 (C++20 and C++23) matrix entries. Add a manual-stage pre-commit hook that runs the same `./scripts/cmake.sh --debug --clang-tidy` build as CI, so there is one documented local invocation without a full build on every commit. Being manual-stage, it is skipped by `pre-commit run --all-files` in CI. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QHjiPBayDu7hLZyhcQK3tt --- .github/workflows/clang-tidy.yml | 5 ++++- .github/workflows/cmake.yml | 26 ++++++++++++++++++++++++++ .github/workflows/iwyu.yml | 3 ++- .pre-commit-config.yaml | 16 ++++++++++++++++ CONTRIBUTING.md | 16 +++++++++++++++- cmake/modules/FindClangTidy.cmake | 17 ++++++++++++++++- docker/Dockerfile | 10 ++++++++-- 7 files changed, 87 insertions(+), 6 deletions(-) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index da36e32..f2b3a9c 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -27,7 +27,10 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Debug - COMPILER_VERSION: 21 + # Keep in sync with the clang-tidy package in docker/Dockerfile and the + # expected version in cmake/modules/FindClangTidy.cmake, so that local runs + # and CI use the same check set. + COMPILER_VERSION: 22 UV_FROZEN: 1 jobs: diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ed90f44..299a3da 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -179,6 +179,32 @@ jobs: }, lib: "libc++21", } + - { + name: "Ubuntu Clang-22 + libc++", + os: ubuntu-24.04, + compiler: + { + type: CLANG, + version: 22, + cc: "clang-22", + cxx: "clang++-22", + std: 20, + }, + lib: "libc++22", + } + - { + name: "Ubuntu Clang-22 (C++23) + libc++", + os: ubuntu-24.04, + compiler: + { + type: CLANG, + version: 22, + cc: "clang-22", + cxx: "clang++-22", + std: 23, + }, + lib: "libc++22", + } - { name: "Visual Studio 2019", os: windows-latest, diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index d98643f..9a90842 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -27,7 +27,8 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Debug - COMPILER_VERSION: 21 + # include-what-you-use is built from its clang_ branch. + COMPILER_VERSION: 22 UV_FROZEN: 1 jobs: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a921a73..ead27a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,3 +56,19 @@ repos: rev: v0.8.0 hooks: - id: action-validator +- repo: local + hooks: + # Runs the same full clang-tidy build as the "Clang Tidy" CI workflow, so + # local and CI findings match. It is a full build, so it is opt-in (manual + # stage) rather than run on every commit: + # + # uv run pre-commit run --hook-stage manual clang-tidy + # + # Requires clang-tidy 22 on PATH (installed in the devcontainer). + - id: clang-tidy + name: clang-tidy + entry: ./scripts/cmake.sh --debug --clang-tidy + language: system + pass_filenames: false + files: '(\.(cc|h|j2)|CMakeLists\.txt|\.clang-tidy)$|^cmake/' + stages: [manual] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46521d4..96d90e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,12 +37,26 @@ The script will configure, build, and execute tests. CI runs clang-tidy on every translation unit and fails on any finding (`WarningsAsErrors: '*'` in `.clang-tidy`). To reproduce locally, with -`clang-tidy` on your `PATH` (it is installed in the devcontainer): +`clang-tidy` on your `PATH` (the devcontainer installs `clang-tidy-22`): ```bash ./scripts/cmake.sh --debug --clang-tidy ``` +The same run is available as an opt-in pre-commit hook. It is a full build, so +it is not part of the default commit-time hooks: + +```bash +uv run pre-commit run --hook-stage manual clang-tidy +``` + +CI pins clang-tidy 22 (`COMPILER_VERSION` in `.github/workflows/clang-tidy.yml`); +the check set differs between releases, so a different local version may +report different findings. CMake warns at configure time when the version it +found does not match. The pinned version is recorded in three places that must +be updated together: the workflow, `docker/Dockerfile`, and +`ClangTidy_EXPECTED_MAJOR_VERSION` in `cmake/modules/FindClangTidy.cmake`. + Fix genuine findings. Suppress false positives at the site with a `NOLINT` comment that names the check and gives a reason, or, for a check that does not apply to a whole directory, in that directory's `.clang-tidy` (see diff --git a/cmake/modules/FindClangTidy.cmake b/cmake/modules/FindClangTidy.cmake index 5b09311..c144952 100644 --- a/cmake/modules/FindClangTidy.cmake +++ b/cmake/modules/FindClangTidy.cmake @@ -24,8 +24,15 @@ option(CLANG_TIDY_ENABLE "Build with support for clang-tidy" OFF) set(ClangTidy_VERBOSITY_LEVEL 3 CACHE STRING "Clang Tidy verbosity level (the higher the level, the more output)") +# The major version that CI runs (COMPILER_VERSION in +# .github/workflows/clang-tidy.yml) and that docker/Dockerfile installs. The +# check set differs between clang-tidy releases, so a different local version +# may report findings that CI does not, or miss findings that CI reports. +set(ClangTidy_EXPECTED_MAJOR_VERSION 22 CACHE STRING "clang-tidy major version used by CI") + +# Prefer the versioned binary matching CI when several are installed. find_program(ClangTidy_EXECUTABLE - NAMES clang-tidy + NAMES clang-tidy-${ClangTidy_EXPECTED_MAJOR_VERSION} clang-tidy ) macro(_clang_tidy_log) @@ -90,6 +97,14 @@ if(ClangTidy_EXECUTABLE) mark_as_advanced(ClangTidy_EXECUTABLE) get_clang_tidy_version(EXECUTABLE ${ClangTidy_EXECUTABLE} RESULT ClangTidy_VERSION) _clang_tidy_log("clang-tidy version ${ClangTidy_VERSION}") + string(REGEX REPLACE "\\..*" "" ClangTidy_MAJOR_VERSION "${ClangTidy_VERSION}") + if(CLANG_TIDY_ENABLE AND NOT ClangTidy_MAJOR_VERSION STREQUAL ClangTidy_EXPECTED_MAJOR_VERSION) + message(WARNING + "clang-tidy ${ClangTidy_VERSION} found at ${ClangTidy_EXECUTABLE}, but CI " + "runs clang-tidy ${ClangTidy_EXPECTED_MAJOR_VERSION}; findings may differ. " + "Install clang-tidy-${ClangTidy_EXPECTED_MAJOR_VERSION} or set " + "-DClangTidy_EXECUTABLE=.") + endif() endif() include(FindPackageHandleStandardArgs) diff --git a/docker/Dockerfile b/docker/Dockerfile index f2cda9e..adf5e15 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,13 @@ FROM ubuntu:26.04 # Install base tools and C++ development tools. +# +# clang-tidy is pinned to the same major version as the "Clang Tidy" CI +# workflow (COMPILER_VERSION in .github/workflows/clang-tidy.yml) so that a +# local `./scripts/cmake.sh --clang-tidy` run reports the same findings as CI. +# The unversioned `clang-tidy` package on this Ubuntu release is LLVM 21. RUN apt-get update && apt-get install -y --no-install-recommends \ - clang-tidy \ + clang-tidy-22 \ cmake \ curl \ g++ \ @@ -20,7 +25,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ssh \ unzip \ wget \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && ln -s /usr/bin/clang-tidy-22 /usr/local/bin/clang-tidy # Install bazelisk. RUN ARCH=$(dpkg --print-architecture) && \ From 2cf9c6e7576cb803068f6d81b9138d11beca6d8d Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:26:19 +0000 Subject: [PATCH 2/4] Bump google benchmark to v1.9.5 for Clang 22 Clang 22 warns that __COUNTER__ is a C2y extension, and benchmark builds its own sources with -Werror -pedantic-errors, so v1.9.1 failed to compile in the new Clang-22 matrix jobs. v1.9.5 suppresses the warning around its __COUNTER__ probe. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QHjiPBayDu7hLZyhcQK3tt --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2aa2c11..f247350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,9 @@ if(XYZ_PROTOCOL_IS_NOT_SUBPROJECT) FetchContent_Declare( google_benchmark GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG v1.9.1 + # v1.9.5 or later is required for Clang 22, which warns that __COUNTER__ + # is a C2y extension; benchmark's own -Werror build failed on it before. + GIT_TAG v1.9.5 SYSTEM) # For Windows: Prevent overriding the parent project's compiler/linker # settings From a6abb35410b76c758eaed92b16d914f27c80068a Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:31:45 +0000 Subject: [PATCH 3/4] Install Clang 20 for the Clang-20 matrix entry The entry asked setup-clang for version 19 and then compiled with clang-20, which only worked because the runner image already ships it. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QHjiPBayDu7hLZyhcQK3tt --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 299a3da..c8255dd 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -146,7 +146,7 @@ jobs: compiler: { type: CLANG, - version: 19, + version: 20, cc: "clang-20", cxx: "clang++-20", std: 20, From 539dc22abaf2b0fd8110a060ef3c87b64553394f Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:34:31 +0000 Subject: [PATCH 4/4] Run the sanitizer jobs with Clang 22 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QHjiPBayDu7hLZyhcQK3tt --- .github/workflows/sanitizers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index d65a107..4a07b86 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -35,7 +35,7 @@ jobs: fail-fast: false matrix: sanitizer: ["asan", "tsan", "msan"] - clang_version: ["19"] + clang_version: ["22"] steps: - uses: actions/checkout@v5 - uses: lukka/get-cmake@v4.4.2