Skip to content

Repository files navigation

pre-commit hooks

Ubuntu 24.04 Build Ubuntu 26.04 Build Macos Build Windows Build

This is a pre-commit hooks repo that integrates two C/C++ code formatters:

clang-format, uncrustify,

and five C/C++ static code analyzers:

clang-tidy, oclint, cppcheck, cpplint, include-what-you-use

This repo's hooks do more than passthrough arguments to provide these features:

  • Relay correct pass/fail to pre-commit, even when some commands exit 0 when they should not. Some versions of oclint, clang-tidy, and cppcheck have this behavior.
  • Honor -- arguments, which pre-commit has problems with
  • Optionally enforce a command version so your team gets code formatted/analyzed the same way
  • Formatters clang-format and uncrustify will error with diffs of what has changed

Example Usage

With this err.c

#include <stdio.h>
int main(){int i;return;}

and using this .pre-commit-config.yaml:

fail_fast: false
repos:
  - repo: https://github.com/pocc/pre-commit-hooks
    rev: master
    hooks:
      - id: clang-format
        args: [--style=Google]
      - id: clang-tidy
      - id: oclint
      - id: uncrustify
      - id: cppcheck
      - id: cpplint
      - id: include-what-you-use

All seven linters should fail on commit with these messages. Full text is at media/all_failed.txt.

clang-format error (indentation)
clang-format.............................................................Failed
- hook id: clang-format
- exit code: 1

err.c
====================
--- original

+++ formatted

@@ -1,3 +1,6 @@

 #include <stdio.h>
-int main(){int i;return;}
+int main() {
+  int i;
+  return;
+}

clang-tidy error (non-void main should return a value)
clang-tidy...............................................................Failed
- hook id: clang-tidy
- exit code: 1

/tmp/temp/err.c:2:18: error: non-void function 'main' should return a value [clang-diagnostic-return-type]
int main(){int i;return;}
                 ^
1 error generated.
Error while processing /tmp/temp/err.c.
Found compiler error(s).

oclint error (non-void main should return a value)
oclint...................................................................Failed
- hook id: oclint
- exit code: 6

Compiler Errors:
(please be aware that these errors will prevent OCLint from analyzing this source code)

/tmp/temp/err.c:2:18: non-void function 'main' should return a value

Clang Static Analyzer Results:

/tmp/temp/err.c:2:18: non-void function 'main' should return a value


OCLint Report

Summary: TotalFiles=0 FilesWithViolations=0 P1=0 P2=0 P3=0


[OCLint (https://oclint.org) v21.05]

uncrustify error (indentation)
uncrustify...............................................................Failed
- hook id: uncrustify
- exit code: 1

err.c
====================
--- original

+++ formatted

@@ -1,3 +1,5 @@

 #include <stdio.h>
-int main(){int i;return;}
+int main(){
+  int i; return;
+}

cppcheck error (unused variable i)
cppcheck.................................................................Failed
- hook id: cppcheck
- exit code: 1

err.c:2:16: style: Unused variable: i [unusedVariable]
int main(){int i;return;}
               ^

cpplint error (no copyright message, bad whitespace)
cpplint..................................................................Failed
- hook id: cpplint
- exit code: 1

Done processing err.c
Total errors found: 4
err.c:0:  No copyright message found.  You should have a line: "Copyright [year] <Copyright Owner>"  [legal/copyright] [5]
err.c:2:  More than one command on the same line  [whitespace/newline] [0]
err.c:2:  Missing space after ;  [whitespace/semicolon] [3]
err.c:2:  Missing space before {  [whitespace/braces] [5]

include-what-you-use error (remove unused #include )
include-what-you-use.....................................................Failed
- hook id: include-what-you-use
- exit code: 3

err.c:2:18: error: non-void function 'main' should return a value [-Wreturn-type]
int main(){int i;return;}
                 ^

err.c should add these lines:

err.c should remove these lines:
- #include <stdio.h>  // lines 1-1

The full include-list for err.c:
---

Note that for your config yaml, you can supply your own args or remove the args line entirely, depending on your use case.

You can also clone this repo and then run the test_repo to see all of the linters at work to produce this output:

git clone https://github.com/pocc/pre-commit-hooks
cp -r pre-commit-hooks/tests/test_repo .
cd test_repo
git init
git add .
pre-commit run

Note that we are copying the test_repo outside of the pre-commit-hooks repo so that pre-commit doesn't get confused by nested github repositories.

Using this repo

Special flags in this repo

The --version, --no-diff, and --no-cppcheck-cache flags can be added to args: for a pre-commit hook. They will be removed and not be passed on to the command.

Some linters change behavior between versions. To enforce a linter version 8.0.0, for example, add --version=8.0.0 to args: for that linter. Note that this is a pre-commit hook arg and will be filtered before args are passed to the linter.

You can add --no-diff to the args: for clang-format and uncrustify if you would like there to be no diff output for these commands.

Default Options

These options are automatically added to enable all errors or are required.

  • oclint: ["-enable-global-analysis", "-enable-clang-static-analyzer", "-max-priority-3", "0"]
  • uncrustify: ["-c", "defaults.cfg", "-q"] (options added, and a defaults.cfg generated, if -c is missing)
  • cppcheck: ["-q" , "--error-exitcode=1", "--enable=all", "--suppress=unmatchedSuppression", "--suppress=missingIncludeSystem", "--suppress=unusedFunction"] (See #30)
  • cpplint: ["--verbose=0"]

If you supply any of these options in args:, your options will override the above defaults (use -<flag>=<option> if possible when overriding).

Persistent cppcheck caching

cppcheck caches analysis by default and checks all selected files in one invocation. Its cache is outside the repository:

  • macOS: ~/Library/Caches/pre-commit-hooks/cppcheck/
  • Linux: $XDG_CACHE_HOME/pre-commit-hooks/cppcheck/ (or ~/.cache/...)
  • Windows: %LOCALAPPDATA%\pre-commit-hooks\cppcheck\

The cache is keyed by cppcheck version and repository path. It is safe to delete; cppcheck recreates it. The hook supplies -j with the available CPU count unless args: already supplies -j.

Disable caching for a run with the hook-only --no-cppcheck-cache flag:

- id: cppcheck
  args: [--no-cppcheck-cache]

For CI, override the cache location with --cppcheck-build-dir and cache that directory with the CI provider. Include the cppcheck version in the CI cache key so cache formats are not mixed.

- id: cppcheck
  args:
    - --cppcheck-build-dir=.cache/cppcheck
    - -j
    - "8"

-j is entirely user-selected. Use the ordinary cppcheck hook when caching is not needed.

With --project, disable filename passing because the project supplies the complete file set:

- id: cppcheck
  pass_filenames: false
  args: [--project=compile_commands.json]

Choosing a specific executable

Each hook runs the tool of the same name from your PATH. To point a hook at a specific binary instead, set an environment variable named after the command, upper-cased with non-alphanumeric characters replaced by _:

Command Environment variable
clang-format CLANG_FORMAT
clang-tidy CLANG_TIDY
cppcheck CPPCHECK
cpplint CPPLINT
oclint OCLINT
uncrustify UNCRUSTIFY
include-what-you-use INCLUDE_WHAT_YOU_USE

For example, to use a non-default uncrustify without touching PATH:

UNCRUSTIFY=/home/me/.local/bin/uncrustify pre-commit run uncrustify

The value may be an absolute path or another name to resolve on PATH.

Vala

uncrustify can format Vala, but pre-commit's identify library does not yet tag .vala files (see identify#258), so the hook cannot list vala in types_or. Until that lands, match Vala files yourself and tell uncrustify the language explicitly:

- id: uncrustify
  types_or: [text]
  files: \.vala$
  args: [-l, VALA]

The other hooks target C/C++/Objective-C and do not apply to Vala.

Compilation Database

clang-tidy and oclint both expect a compilation database. Both of the hooks for them will ignore the error for not having one.

You can generate with one cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON <dir> if you have a cmake-based project.

Information about the Commands

Python3.8+ is required to use these hooks as all hook scripts are written in it. This aligns with modern pre-commit requirements and ensures compatibility with current tools.

Installation

Use these commands to install some or all of the linters used in this project:

  • Linux
    • brew install llvm uncrustify cppcheck include-what-you-use [1] [2]
    • oclint: See note below for installation [3]
  • MacOS
    • brew install llvm uncrustify cppcheck include-what-you-use oclint
    • Note: After installing llvm, you may need to add it to your PATH:
      echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
      # or for bash: echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.bash_profile
  • Windows
    • choco install llvm uncrustify cppcheck inlcude-what-you-use
  • Cross-platform
    • pip install cpplint
    • pipx install clang-format [4]

[1]: llvm includes tools like clang-format and clang-tidy, but they may not be automatically added to your PATH (see MacOS note above).

[2]: While it's possible to use other package managers to install these utilities on Linux, I recommend using brew to avoid dependency issues between llvm and linters that use it, such as include-what-you-use.

[3]: You can install on MacOS with brew install oclint. Oclint's github page also provides compiled binary packages (and zip of source code to compile) for Macos/Linux: releases. oclint is not available on windows.

[4]: This will download the latest version. Versions from 10.0.1 and up are supported. To pin to a specific version like 13.0.0, use pipx install clang-format==13.0.0. Check out the clang-format-wheel repository for information on how to download the clang-format binary as part of a CI pre-commit build.

Hook Info

Hook Info Type Languages
clang-format Formatter C, C++, ObjC, ObjC++, Java
clang-tidy Static code analyzer C, C++, ObjC
oclint Static code analyzer C, C++, ObjC
uncrustify Formatter C, C++, C#, ObjC, D, Java, Pawn, Vala
cppcheck Static code analyzer C, C++
cpplint Style checker C, C++
include-what-you-use Static code analyzer C, C++

Hook Option Comparison

Hook Options Fix In Place Enable all Checks Set key/value
clang-format -i
clang-tidy --fix-errors [1] -checks=* -warnings-as-errors=* [2]
oclint -enable-global-analysis -enable-clang-static-analyzer -max-priority-3 0 [3] -rc=<key>=<value>
uncrustify --replace --no-backup [4] --set key=value
cppcheck -enable=all
cpplint --verbose=0
include-what-you-use --verbose=3

[1]: -fix will fail if there are compiler errors. -fix-errors will -fix and fix compiler errors if it can, like missing semicolons.

[2]: Be careful with -checks=*. can have self-contradictory rules in newer versions of llvm (9+): modernize wants to use trailing return type but Fuchsia disallows it. Thanks to @rambo.

[3]: The oclint pre-commit hook does the equivalent of -max-priority-3 0 by default, which returns an error code when any check fails. See oclint error codes for more info on partially catching failed checks.

[4]: By definition, if you are using pre-commit, you are using version control. Therefore, it is recommended to avoid needless backup creation by using --no-backup.

Development

See README_dev.md

Additional Resources

clang-format

clang-tidy

oclint

On Linux, use a release no newer than 22.02. The published 24.11 binary cannot load its own rules (undefined symbol: _ZN4llvm24DisableABIBreakingChecksE) and reports no rule loaded instead of analyzing. Releases 21.05 through 22.02 work. macOS is unaffected. Building from source needs ENABLE_EXPORTS on the driver target so the dlopened rules can resolve LLVM symbols against it.

uncrustify

cppcheck

cpplint

include-what-you-use

License

Apache 2.0

About

C/C++ hooks to integrate with pre-commit

Topics

Resources

Contributing

Stars

399 stars

Watchers

9 watching

Forks

Releases

Packages

Used by

Contributors

Languages