How to implement a linter and code formatter in a C/C++ project (clang-format and clang-tidy)
Guide explaining how to implement a linter and a code formatter on a C project.
In this article you will know how to use a linter in a C project, rather for a Linux or Windows environment:
- Install the linter and the formatter
- clang-tidy is a clang-based C/C++ linter tool
- clang-format is both a library and a stand-alone tool with the goal of automatically reformatting C/C++ sources files according to configurable style guides
- Learn how to use them
- Integrate them on a IDE (Visual Studio Code)
Table of contents
- Table of contents
- Installing utilities
- Learning how to use the utilities
- How to integrate them in VS Code
- References
Installing utilities
To install only clang-format
and clang-tidy
tools without having to install the entire clang compiler, do the following:
On linux:
1
sudo apt install clang-format clang-tidy
On windows:
Looking for how to get these two tools I have found these ways (more or less simple) to install them on windows:
- Go to clang official repository and install Windows binaries exe:
LLVM-<version>-win64.exe
. Once installed, in the route/installation/path/LLVM/bin
you will have the 2 tools. - “Emulate” linux environment on Windows (MinGW, Cygwin, WSL…) and install like a linux machine.
- On cpp-linter github repository make a python pip package to install only
clang-format
andclang-tidy
, among other clang compiler’s tools. You need to have correct version of python installed on your PC. Usepip install clang-tools
to install it.
In my case I have used the first installation option, so from now on the way you call the tools will only be validated for that option
Learning how to use the utilities
clang-tidy: Start linting your code
Create a config file to set checking rules
First, to use clang-tidy
you need to implement all the checks that you want the utility to review.
The checks can be listed in two ways:
- Creating a configuration file named
.clang-tidy
in your project’s directory --checks=<string>
comma-separated list of globs. Whose value is added to the value of the Checks option of the.clang-tidy
file, if it exists.
.clang-tidy configuration file example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Checks: 'clang-diagnostic-*,
clang-analyzer-*,
cppcoreguidelines-*,
modernize-*,
-modernize-use-trailing-return-type'
# clang-analyzer- --> Clang Static Analyzer checks.
# cppcoreguidelines- --> Checks related to C++ Core Guidelines.
# modernize- --> Checks that advocate usage of modern (currently “modern” means “C++11”) language constructs.
WarningsAsErrors: true
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: 'file' # Uses .clang-format file in the closest parent directory
CheckOptions:
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: '0'
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
value: '1'
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: '1'
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
clang-tidy basic use
As an example, the following code (with namefile “test.c”) is used:
1
2
3
4
5
6
7
8
#include <stdio.h>
int main(int argc, char const *argv[])
{
unsigned int var = 10;
printf("The default value of \"var\" is: %i\n", var);
return 0;
}
With this .clang-tidy
file, that will apply all the possible check on the given file(s):
1
2
3
4
Checks: '*'
WarningsAsErrors: ''
AnalyzeTemporaryDtors: false
FormatStyle: 'file'
Although the code compiles and works, it has several good practice errors. And using the following command:
1
2
3
4
clang-tidy test.c --
└─┬──┘ ├┘
│ └─────╴> To avoid "Error while trying to load a compilation database"
└────╴> File to make the checks of ".clang-tidy" file
On How To Setup Tooling For LLVM you can learn how to configure a compilation database
Its output will show you the correct way to code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1374 warnings generated.
/file/absolute/path/test.c:1:1: warning: system include stdio.h not allowed [llvmlibc-restrict-system-libc-headers]
#include <stdio.h>
^~~~~~~~~~~~~~~~~~
/file/absolute/path/test.c:3:14: warning: parameter 'argc' is unused [misc-unused-parameters]
int main(int argc, char const *argv[])
^
/file/absolute/path/test.c:3:32: warning: parameter 'argv' is unused [misc-unused-parameters]
int main(int argc, char const *argv[])
^
/file/absolute/path/test.c:5:21: warning: 10 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
unsigned int var = 10;
^
Suppressed 1369 warnings (1369 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.
As an example, focusing on the magic number warning cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers
, the proper way to fix it is creating a constant variable (or a #define
…) with a descriptive name and then give the value. Like this:
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
const int DEFAULT_INITIAL_VALUE = 10;
int main(int argc, char const *argv[])
{
unsigned int var = DEFAULT_INITIAL_VALUE;
printf("The default value of \"var\" is: %i\n", var);
return 0;
}
But if you know what are you doing you can ignore that error in the .clang-tidy
file adding this:
1
2
3
4
5
6
7
8
9
Checks: '*'
WarningsAsErrors: ''
AnalyzeTemporaryDtors: false
CheckOptions:
- key: 'cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues'
value: '10'
- key: 'readability-magic-numbers.IgnoredIntegerValues'
value: '10'
FormatStyle: 'file'
How clang-format is use
Make a config file to set a coding style
First, to use clang-format
you need to stablish a coding style. You can use one of the predefined styles (GNU, Google, Chromium…) or a custom one.
To make easier a custom style or to preview any style, you can use one of these tools:
- clang-format-configurator based on web
- clang-format-editor the oficial application (only for windows)
The style can be added in two ways:
- With
--style=llvm
(for predefined options) and/or--style="{key: value, ...}"
(for custom options) - Creating a style configuration file named
.clang-format
in your project’s directory and using--style=file
Shell command | .clang-format file |
---|---|
clang-format --style="{BasedOnStyle: Chromium, IndentWidth: 4, ColumnLimit: 120, AccessModifierOffset: -2}" file_path | ---
BasedOnStyle: Chromium
IndentWidth: 4
ColumnLimit: 120
AccessModifierOffset: -2 |
clang-format basic use
As an example, the following code (with namefile “test.c”) is used:
1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(int argc, char const *argv[])
{
unsigned int var = 10;
printf("The default value of \"var\" is: %i\n", var);
return
0;
}
Although it compiles and works, it has several formatting bugs if you rely on the GNU standard. And using clang-format --style=gnu test.c
, its output will show you the correct way to format the code:
1
2
3
4
5
6
7
8
9
10
root@62b80bb26435:/# clang-format --style=gnu test.c
#include <stdio.h>
int
main (int argc, char const *argv[])
{
unsigned int var = 10;
printf ("The default value of \"var\" is: %i\n", var);
return 0;
}
Other way to see which parts of the code need to be formatted you can add -dry-run
(or -n
) option:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@62b80bb26435:/# clang-format -n --style=gnu test.c
test.c:3:4: warning: code should be clang-formatted [-Wclang-format-violations]
int main(int argc, char const *argv[])
^^^^^
test.c:3:9: warning: code should be clang-formatted [-Wclang-format-violations]
int main(int argc, char const *argv[])
^
test.c:4:2: warning: code should be clang-formatted [-Wclang-format-violations]
{
^
test.c:5:25: warning: code should be clang-formatted [-Wclang-format-violations]
unsigned int var = 10;
^
test.c:6:8: warning: code should be clang-formatted [-Wclang-format-violations]
printf("The default value of \"var\" is: %i\n", var);
^
test.c:6:55: warning: code should be clang-formatted [-Wclang-format-violations]
printf("The default value of \"var\" is: %i\n", var);
^
test.c:7:8: warning: code should be clang-formatted [-Wclang-format-violations]
return
^
test.c:9:2: warning: code should be clang-formatted [-Wclang-format-violations]
}
This commands only shows you, in console, the correct way to format the code, but does not modify it. To automatically format the code you need to add the -i
option:
1
root@62b80bb26435:/# clang-format -i --style=gnu test.c
How to integrate them in VS Code
Apart of clang-format
and clang-tidy
programs, you only need the C/C++ extension.
These are the setting that you need to know to setup both utilities:
- For
clang-format
you have “C_Cpp -> formatting” subsettings - For
clang-tidy
you have “C_Cpp -> Code Analysis” subsettings
As setting.json:
1
2
3
4
5
6
7
8
"C_Cpp.clang_format_style": "file"
"C_Cpp.clang_format_fallbackStyle": "Visual Studio"
"C_Cpp.clang_format_path": "/absolute/path/clang-format-14"
"C_Cpp.codeAnalysis.clangTidy.enabled": true
"C_Cpp.codeAnalysis.clangTidy.config": ""
"C_Cpp.codeAnalysis.clangTidy.fallbackConfig": ""
"C_Cpp.codeAnalysis.clangTidy.path": "/absolute/path/clang-tidy-14.exe"
On Linux normally the configuration is very staghforward once the tools are installed plug-and-play, because VS Code search the tools on the standard path. But on windows as you installed them as show on Installing utilities part, and you need to set the path of tools on extension settings.
References
- https://clang.llvm.org/docs/ClangTools.html
- https://www.clangpowertools.com/blog/getting-started-with-clang-format-style-options.html
- https://developers.redhat.com/blog/2021/04/06/get-started-with-clang-tidy-in-red-hat-enterprise-linux#using_clang_tidy_in_red_hat_enterprise_linux
- https://github.com/cpp-linter
- https://www.labri.fr/perso/fleury/posts/programming/using-clang-tidy-and-clang-format.html
Useful tools
- Windows application to create
.clang-format
file -> https://clangpowertools.com/ - Web based application to create
.clang-format
file -> https://zed0.co.uk/clang-format-configurator/ - Utility to implement CI formatter -> https://github.com/Sarcasm/run-clang-format
Useful extensions
Visual Studio Code:
- https://www.youtube.com/watch?v=8RSxQ8sluG0
- https://devblogs.microsoft.com/cppblog/visual-studio-code-c-december-2021-update-clang-tidy/
Some implementations on real projects
- LLVM project the maintainer of
clang-format
andclang-tidy
. - Flight Software & Embedded Systems Framework by NASA
- GTKWave a fully featured GTK+ based wave viewer