Automatically formats and lints C/C++ code using clang-format and clang-tidy
Project description
cpp-linter-hooks
A pre-commit hook that automatically formats and lints your C/C++ code using clang-format and clang-tidy.
Table of Contents
Quick Start
Add this configuration to your .pre-commit-config.yaml file:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0 # Use the tag or commit you want
hooks:
- id: clang-format
args: [--style=Google] # Other coding style: LLVM, GNU, Chromium, Microsoft, Mozilla, WebKit.
- id: clang-tidy
args: [--checks='boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*']
Custom Configuration Files
To use custom configurations like .clang-format and .clang-tidy:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0
hooks:
- id: clang-format
args: [--style=file] # Loads style from .clang-format file
- id: clang-tidy
args: [--checks=.clang-tidy] # Loads checks from .clang-tidy file
[!TIP] The
revtag (e.g.v1.4.0) is the project version, not the clang tool version. Each release bundles a default version ofclang-formatandclang-tidy— check the release notes to see which tool version a givenrevships with. To pin an exact tool version independently of the project release, use--versionas shown below.
Custom Clang Tool Version
To use specific versions of clang-format and clang-tidy (using Python wheel packages):
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0
hooks:
- id: clang-format
args: [--style=file, --version=21] # Specifies version
- id: clang-tidy
args: [--checks=.clang-tidy, --version=21] # Specifies version
[!TIP] For production use, always pin the tool version explicitly with
--version(e.g.--version=21) so upgrades tocpp-linter-hooksnever silently change your linter version.
Compilation Database (CMake/Meson Projects)
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 (build/,
out/, cmake-build-debug/, _build/) and passes -p <dir> to clang-tidy
automatically — no configuration needed for most projects:
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0
hooks:
- id: clang-tidy
args: [--checks=.clang-tidy]
# Auto-detects ./build/compile_commands.json if present
To specify the build directory explicitly:
- id: clang-tidy
args: [--compile-commands=build, --checks=.clang-tidy]
To disable auto-detection (e.g. in a monorepo where auto-detect might pick the wrong database):
- id: clang-tidy
args: [--no-compile-commands, --checks=.clang-tidy]
To see which compile_commands.json the hook is using, add -v:
- id: clang-tidy
args: [--compile-commands=build, -v, --checks=.clang-tidy]
[!NOTE] Generate
compile_commands.jsonwith CMake usingcmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -Bbuild .or addset(CMAKE_EXPORT_COMPILE_COMMANDS ON)to yourCMakeLists.txt.--compile-commandstakes the directory containingcompile_commands.json, not the file path itself.
Output
clang-format Output
clang-format.............................................................Failed
- hook id: clang-format
- files were modified by this hook
Here’s a sample diff showing the formatting applied:
--- 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;
+}
[!NOTE] Use
--dry-runinargsofclang-formatto print instead of changing the format, e.g.:
clang-format.............................................................Failed
- hook id: clang-format
- exit code: 255
main.c:2:11: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:13: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:21: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:28: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:54: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
main.c:2:63: warning: code should be clang-formatted [-Wclang-format-violations]
int main() {for (;;) break; printf("Hello world!\n");return 0;}
^
clang-tidy Output
clang-tidy...............................................................Failed
- hook id: clang-tidy
- exit code: 1
522 warnings generated.
Suppressed 521 warnings (521 in non-user code).
Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
/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 (;;)
^
{
[!NOTE] Add
--fixtoargsto automatically apply clang-tidy fixes in place (equivalent to passing-fixto clang-tidy directly). This is opt-in and not the default because auto-fixing can modify source files in unexpected ways. A validcompile_commands.jsonis strongly recommended when using--fix.For cases where compiler errors exist alongside style issues, pass
-fix-errorsdirectly inargsinstead (clang-tidy native flag).
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0 # requires the version that introduced --fix
hooks:
- id: clang-tidy
args: [--checks=.clang-tidy, --fix]
[!WARNING] When
--fix(or-fix-errors) is active, parallel execution via--jobs/-jis automatically disabled to prevent concurrent writes to the same header file.
Troubleshooting
Performance Optimization
[!TIP] For large codebases, if your
pre-commitruns longer than expected, it is highly recommended to addfilesin.pre-commit-config.yamlto limit the scope of the hook. This helps improve performance by reducing the number of files being checked and avoids unnecessary processing. Here's an example configuration:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.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: [--checks=.clang-tidy, --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.4.0
hooks:
- id: clang-tidy
args: [--checks=.clang-tidy, --version=21, --jobs=4]
[!WARNING] When using
--jobs/-j, avoid sharing options that write to a single output file (for example--export-fixes=fixes.yaml) across parallelclang-tidyinvocations. If you need--export-fixes, ensure each job writes to a unique file path to avoid corrupted or overwritten outputs. 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
[!NOTE] Use
-vor--verboseinargsto enable verbose output. Forclang-format, it shows the list of processed files. Forclang-tidy, it prints whichcompile_commands.jsonis being used (when auto-detected or explicitly set).
repos:
- repo: https://github.com/cpp-linter/cpp-linter-hooks
rev: v1.4.0
hooks:
- id: clang-format
args: [--style=file, --version=21, --verbose] # Shows processed files
- id: clang-tidy
args: [--checks=.clang-tidy, --verbose] # Shows which compile_commands.json is used
FAQ
What's the difference between cpp-linter-hooks and mirrors-clang-format?
| Feature | cpp-linter-hooks |
mirrors-clang-format |
|---|---|---|
Supports clang-format and clang-tidy |
✅ (clang-format & clang-tidy) |
✅ (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 — see release notes for bundled tool version | Equals the clang-format version directly |
| Supports passing format style string | ✅ via --style |
❌ |
| Verbose output | ✅ via --verbose |
❌ |
| Dry-run mode | ✅ via --dry-run |
❌ |
| Auto-fix mode | ✅ via --fix (clang-tidy only) |
❌ |
| Compilation database support | ✅ auto-detect or --compile-commands |
❌ |
Contributing
We welcome contributions! Whether it's fixing issues, suggesting improvements, or submitting pull requests, your support is greatly appreciated.
License
This project is licensed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cpp_linter_hooks-1.4.0-py3-none-any.whl.
File metadata
- Download URL: cpp_linter_hooks-1.4.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
888620492dc944cd0c95f4f221faf417e4306800a8c52c1283a448b0c4c759f4
|
|
| MD5 |
b8e49d53975b8936eef6c1687715d552
|
|
| BLAKE2b-256 |
3dc81aaff4d326f88492f861d027c59984e57223905fbc3a068395c9c48f1277
|