Skip to main content

Automatically formats and lints C/C++ code using clang-format and clang-tidy

Project description

cpp-linter-hooks

PyPI PyPI - Python Version codecov Test CodeQL

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.2.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.2.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 rev tag (e.g. v1.2.0) is the project version, not the clang tool version. Each release bundles a default version of clang-format and clang-tidy — check the release notes to see which tool version a given rev ships with. To pin an exact tool version independently of the project release, use --version as 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.2.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 to cpp-linter-hooks never 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.2.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.json with CMake using cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -Bbuild . or add set(CMAKE_EXPORT_COMPILE_COMMANDS ON) to your CMakeLists.txt. --compile-commands takes the directory containing compile_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-run in args of clang-format to 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 (;;)
            ^
             {

Troubleshooting

Performance Optimization

[!TIP] For large codebases, if your pre-commit runs longer than expected, it is highly recommended to add files in .pre-commit-config.yaml to 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.2.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.2.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 parallel clang-tidy invocations. 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 -v or --verbose in args to enable verbose output. For clang-format, it shows the list of processed files. For clang-tidy, it prints which compile_commands.json is being used (when auto-detected or explicitly set).

repos:
  - repo: https://github.com/cpp-linter/cpp-linter-hooks
    rev: v1.2.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
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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cpp_linter_hooks-1.3.1-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

Details for the file cpp_linter_hooks-1.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for cpp_linter_hooks-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ca4861f6d169b0c9cab8a21a6f22621c192fa4c77499f8b9f7692b85e991f83b
MD5 c662118e5ca60b234acc1a04d5dc56a3
BLAKE2b-256 4204b6c9eab2fe46c65e5a74de7a3a4f6989b2ab1eb7a52e6a74a95be211e10a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page