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

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.0-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for cpp_linter_hooks-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ab2c9ee0070b4a66f27d99c014b59f7747393a4b9d5fb2809945b92d88fb8b2d
MD5 09f2dd7210e8ed9f0175b539ac6ebfd0
BLAKE2b-256 c0d11970edb48c92fbe5eb0fca28c65bbe9f5b35daf3848c13b78bd9516fd6d9

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