Skip to main content

cpp-linter-hooks

PyPI ci coverage part of cpp-linter

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.0
    hooks:
      - id: clang-format
        args: [--style=file, --version=21]
      - id: clang-tidy
        args: [--version=21]

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. 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).

For teams, CI and machines behind a firewall or a company PyPI mirror, also pin the wheel in additional_dependencies so pre-commit installs it once, with the hook (see Install the tool with the hook).

Usage

Custom clang tool version

  • --version=21 installs the newest 21.x wheel, and --version=21.1.8 pins an 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.
  • When the hook environment already has a wheel that matches --version (or any wheel, without --version), the hook uses it and does not contact pypi.org. Otherwise it looks the version up on pypi.org and installs it, so that run needs network access. pre-commit runs the hook on groups of files in parallel; one of them installs the wheel and the others wait for it.

Install the tool with the hook

We recommend listing the exact wheel in additional_dependencies. pre-commit then installs the clang tool once, when it creates the hook environment, the same way mirrors-clang-format does:

      - id: clang-format
        args: [--style=file, --version=21]
        additional_dependencies: [clang-format==21.1.8]
      - id: clang-tidy
        args: [--version=21]
        additional_dependencies: [clang-tidy==21.1.6]

pre-commit installs additional_dependencies with the hook (for example on pre-commit install-hooks), through the same package index it uses for the hook itself. The hooks then never install anything or contact pypi.org when they run, so this works:

  • on pre-commit.ci, which runs hooks without network access,
  • behind a firewall or with a company PyPI mirror, where pypi.org may be blocked or slow,
  • for teams and CI, where every machine gets exactly the same wheel.

The pinned wheel must match --version; otherwise the hook installs the --version one at run time. Without --version, the hook uses the pinned wheel.

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

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 commit and pre-commit run: the staged changes.
  • With pre-commit run --from-ref A --to-ref B and in the pre-push stage: 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.0  # 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.0
    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 -v or verbose: true on the hook.
  • fails in CI, when the CI environment 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.

Troubleshooting

Performance optimization

- repo: https://github.com/cpp-linter/cpp-linter-hooks
  rev: v1.8.0
  hooks:
    - id: clang-format
      args: [--style=file, --version=21]
      files: ^(src|include)/.*\.(cpp|cc|cxx|h|hpp)$ # Limits to specific dirs and file types
    - id: clang-tidy
      args: [--version=21]
      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.0
  hooks:
    - id: clang-tidy
      args: [--version=21, --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.0
    hooks:
      - id: clang-format
        args: [--style=file, --version=21, --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 via --version arg (e.g. --version=21) 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.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for cpp-linter-hooks 1.8.0
File Interpreter ABI Platform
cpp_linter_hooks-1.8.0-py3-none-any.whl Python 3 none any Details

Release files / cpp_linter_hooks-1.8.0-py3-none-any.whl

Download URL cpp_linter_hooks-1.8.0-py3-none-any.whl
Size 24.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aeeb9a05e3c7a488fce6a25de736370ccf49722bf0fecb08dce45e97367342b8
BLAKE2b-256 checksum
How to use checksums
409a23bd86bb22a807bd4fd51f0c4f8c182fafcbbbfee5b50d78bdcb5604be08
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 23, 2026.

Transparency log

Security for cpp-linter-hooks 1.8.0

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.0-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 23, 2026.

Transparency log

A 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.

Release history Release notifications | RSS feed

This release

1.8.0 This release

1 release file Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page