cpp-linter-hooks
pre-commit hooks that pip-install the clang-format and clang-tidy version you pin, on every developer's machine.
Website · Get started · Discussions
Quick start
Add this configuration to your .pre-commit-config.yaml file:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1
hooks:
- id: clang-format
args: [--style=file]
- id: clang-tidy
Run pre-commit install once in each clone. --style=file loads the style from your
.clang-format file, and clang-tidy reads your .clang-tidy file by itself. clang-tidy 22 and
newer run no checks without one, so add a .clang-tidy file that contains, for example,
Checks: 'bugprone-*,performance-*,readability-*'. The clang-tidy hook
reports only problems on the lines that your commit adds or changes (see
Only changed lines). It needs
a compile_commands.json, which it looks for in build/ and a few other directories; without one
it skips clang-tidy on your machine and fails in CI (see
Compilation database).
This uses the newest clang-format and clang-tidy. To choose a version, add --version (see
Clang tool version).
Usage
Clang tool version
The rev tag (e.g. v1.8.1) is the version of these hooks, not of clang-format or clang-tidy.
Choose the clang tool version with --version:
- id: clang-format
args: [--style=file, --version=21]
- id: clang-tidy
args: [--version=21]
- Without
--version, the hooks use the newest wheel on PyPI: pre-commit installs the newest clang-format together with the hook, and the clang-tidy hook installs the newest clang-tidy the first time it runs. Both are kept until pre-commit rebuilds the hook (a newrevorpre-commit clean), so two machines can end up with different versions. Teams and CI should set--version. --version=21installs the newest 21.x wheel, and--version=21.1.8an exact release. clang-tidy wheels are released separately from clang-format wheels and skip some releases, so give clang-tidy the major version.- clang-format wheels cover LLVM 6 to 23 and clang-tidy wheels LLVM 13 to 23. For a version without a wheel, the hook fails and lists some of the versions that exist.
- Each version is installed once, the first time the hook runs with it, and later runs use it
without contacting pypi.org. pre-commit shares the hook between all your projects that use the
same
rev, and projects that ask for different versions each keep their own copy.
Without network access
clang-format without --version is installed together with the hook and runs offline. clang-tidy
without --version, and any --version that is not installed yet, is installed the first time
the hook runs, which needs network access. On pre-commit.ci, which runs
hooks without network access, or where pypi.org is blocked, pin the version with pre-commit's own
additional_dependencies instead of --version. pre-commit installs it together with the hook, through the package index it uses for
the hook itself:
- id: clang-tidy
additional_dependencies: [clang-tidy==21.1.6]
clang-format
To use a predefined coding style instead of your .clang-format file:
- id: clang-format
args: [--style=Google] # Other coding style: LLVM, GNU, Chromium, Microsoft, Mozilla, WebKit.
When clang-format changes a file, the hook fails and pre-commit stops the commit:
clang-format.............................................................Failed
- hook id: clang-format
- files were modified by this hook
Here’s a sample diff showing the formatting applied with --style=Google:
--- a/testing/main.c
+++ b/testing/main.c
@@ -1,3 +1,6 @@
#include <stdio.h>
-int main() {for (;;) break; printf("Hello world!\n");return 0;}
-
+int main() {
+ for (;;) break;
+ printf("Hello world!\n");
+ return 0;
+}
clang-format.............................................................Failed
- hook id: clang-format
- exit code: 1
main.c:2:13: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:21: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:28: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:54: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:63: error: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
clang-tidy
clang-tidy 22 and newer enable no checks by themselves. Without a .clang-tidy file or
--checks, the hook fails and says how to choose checks, for example with a .clang-tidy file
that contains Checks: 'bugprone-*,performance-*,readability-*'.
To set the checks in args instead of your .clang-tidy file, quote the whole option: inside
[...], YAML splits an unquoted value at each comma. When that happens, the hook joins the pieces
again and adds a note on how to fix the configuration. --checks takes check names, not a file:
clang-tidy reads the nearest .clang-tidy file by itself, so --checks=.clang-tidy does nothing.
- id: clang-tidy
args: ["--checks=boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*"]
When clang-tidy reports a warning or an error, the hook fails:
clang-tidy...............................................................Failed
- hook id: clang-tidy
- exit code: 1
/home/runner/work/cpp-linter-hooks/cpp-linter-hooks/testing/main.c:4:13: warning: statement should be inside braces [readability-braces-around-statements]
for (;;)
^
{
The hook passes -quiet to clang-tidy and drops status lines such as 522 warnings generated.,
which count warnings in system headers that clang-tidy does not show. Add --verbose to args to
keep clang-tidy's full output.
Only changed lines
By default the hook reports only the problems on lines that the commit adds or changes, so old
warnings elsewhere in a file do not block your commit. It reads the changes with git diff:
- On
git commitandpre-commit run: the staged changes. - With
pre-commit run --from-ref A --to-ref Band in thepre-pushstage: the changes between the two commits.
The hook passes these lines to clang-tidy's --line-filter, so --fix applies only the fixes
for problems reported on those lines (a fix can still touch a nearby line, for example to add a
closing brace). Compiler errors are always reported. Files with no added lines, such as a pure rename, are
not checked.
The hook checks every line when the files it gets have no changes, for example with
pre-commit run --all-files on a clean working tree, when a file on disk differs from the
version the lines come from (unstaged changes in a direct run, or uncommitted changes on
pre-push), or outside a git repository. To always check
every line, for example in a manual stage, add --all-lines:
- id: clang-tidy
args: [--all-lines]
Checking only changed lines does not make clang-tidy faster: it still analyzes the whole file. See Performance optimization.
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1 # includes --fix support
hooks:
- id: clang-tidy
args: [--fix]
Compilation database
For CMake or Meson projects, clang-tidy works best with a compile_commands.json
file that records the exact compiler flags used for each file. Without it, clang-tidy
may report false positives from missing include paths or wrong compiler flags.
The hook auto-detects compile_commands.json in common build directories and passes -p <dir>
to clang-tidy automatically — no configuration needed for most projects. It looks in build/,
out/, cmake-build-debug/, _build/ and builddir/ first, then in cmake-build-*/,
build-*/, build/*/ and out/build/*/ (CMake presets, Visual Studio), where the most recently
written one wins. Without -p, clang-tidy also finds a compile_commands.json or
compile_flags.txt next to each file or in its parent directories, so a database in the
repository root works too.
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1
hooks:
- id: clang-tidy
# Auto-detects ./build/compile_commands.json if present
To specify the build directory explicitly:
- id: clang-tidy
args: [--compile-commands=build]
When no compilation database is found, clang-tidy would report missing headers instead of problems in your code, so the hook:
- skips clang-tidy on your machine and passes. The message says how to generate the database;
pre-commit shows it with
pre-commit run -vorverbose: trueon the hook. - fails in CI, when the
CIenvironment variable is set (GitHub Actions, GitLab CI and most CI services set it), so a broken setup does not pass unnoticed.
The hook runs clang-tidy anyway when args tell it how to compile: -p, --extra-arg or
--extra-arg-before.
To disable auto-detection and always run clang-tidy without a database (e.g. in a monorepo where auto-detect might pick the wrong database, or for code that needs no compiler flags):
- id: clang-tidy
args: [--no-compile-commands]
Examples
Three self-contained templates plus quick snippets for other common setups.
- CMake minimal config
- Meson minimal config
- Large project
files:regex — scoping hooks for speed - Quick snippets — Meson, clang-format-only, monorepo, CI,
compile_commands.json
Troubleshooting
Performance optimization
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1
hooks:
- id: clang-format
args: [--style=file]
files: ^(src|include)/.*\.(cpp|cc|cxx|h|hpp)$ # Limits to specific dirs and file types
- id: clang-tidy
files: ^(src|include)/.*\.(cpp|cc|cxx|h|hpp)$
For clang-tidy, you can also process multiple files in parallel by adding --jobs
or -j:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1
hooks:
- id: clang-tidy
args: [--jobs=4]
With LLVM 21 and older, clang-tidy enables the clang-analyzer-* checks by default unless your
Checks start with -*, and they take most of its time: in our measurements on
fmt, more than 90%. LLVM 22 removed them from the defaults. If
clang-tidy is slow and you do not need them, add -clang-analyzer-* to Checks in .clang-tidy.
Alternatively, if you want to run the hooks manually on only the changed files, you can use the following command:
pre-commit run --files $(git diff --name-only)
This approach ensures that only modified files are checked, further speeding up the linting process during development.
Verbose output
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.8.1
hooks:
- id: clang-format
args: [--style=file, --verbose] # Shows processed files
- id: clang-tidy
args: [--verbose] # Shows which compile_commands.json is used
Compared with mirrors-clang-format
mirrors-clang-format is pre-commit's mirror of the clang-format wheel.
| Feature | cpp-linter-hooks |
mirrors-clang-format |
|---|---|---|
Supports clang-format and clang-tidy |
Both | clang-format only |
| Custom configuration files | .clang-format, .clang-tidy |
.clang-format |
| Specify tool version | --version |
via rev tag (e.g. rev: v21.1.8) |
rev tag meaning |
Project version, not the tool version | Equals the clang-format version directly |
| Default file types | C, C++ | C, C++, C#, CUDA, Java, JavaScript, JSON, Objective-C, proto, textproto, Metal |
| Supports passing format style string | via --style |
via --style |
| Verbose output | via --verbose |
via --verbose |
| Dry-run mode | via --dry-run |
via --dry-run --Werror |
| Auto-fix mode | via --fix (clang-tidy only) |
No |
| Compilation database support | auto-detect or --compile-commands |
No |
Used by
These organizations run cpp-linter-hooks on their default branch:
MIT ACL ·
Bazel Contrib ·
CodSpeed ·
doldecomp ·
HKUST Aerial Robotics ·
Kubewarden ·
Computational Geography ·
IMSY ·
CONVINCE-Project
The showcase lists more projects that use cpp-linter tools.
Sponsors
cpp-linter is maintained by two volunteers. Sponsor the project through Open Collective. Silver and Gold sponsors get their logo here.
Contributing
See the contributing guide and open an issue for bugs and feature requests.
License
This project is licensed under the MIT License.
Metadata
Release files for cpp-linter-hooks 1.8.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cpp_linter_hooks-1.8.1-py3-none-any.whl | Python 3 | none | any | Details |
Release files / cpp_linter_hooks-1.8.1-py3-none-any.whl
| Download URL | cpp_linter_hooks-1.8.1-py3-none-any.whl |
|---|---|
| Size | 25.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
94e208d6945aeeafc9f03c611ae885166a44c1f731142cb3864bc26b8e08008f
|
|
BLAKE2b-256 checksum How to use checksums |
f9b9753f740f66d5ad691dc0ab41283cfb48ddeb99eac92b1b313f80df384aa1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for cpp-linter-hooks 1.8.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 1 file in this release has been uploaded with provenance metadata.
Attestation for cpp_linter_hooks-1.8.1-py3-none-any.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.