We’ll use the LLVM project’s clang-tidy
tool to find possible programming mistakes, and the clang-format
tool to reformat the whitespace in our code.
Linting
We already have a .clang-tidy
file in the repository. This is used by the clangd
editor extension we installed earlier, and you may have noticed warning underlines like the following:
This is already great for any developers who have their editor setup this way. They can see warnings or errors based on the .clang-tidy
configuration, and accept any auto-fixes it provides.
However developers might choose to ignore the warnings. We can run clang-tidy with Bazel as well. Our approach doesn’t require any changes to BUILD
files, as the cc_library
targets already supply the “bare facts” about the code, and shouldn’t have to specify what to do with it.
Look in the tools/lint
folder. You’ll see a clang_tidy
target in tools/lint/BUILD
that runs the tool. In order to run it over existing targets, we use a Bazel feature called “aspects”. The clang_tidy
aspect is declared in tools/lint/linters.bzl
.
We could invoke this aspect from the command line with a Bazel command:
% bazel build --aspects=//tools/lint:linters.bzl%clang_tidy //src:all
The aspect simply adds additional build actions to the cc_library targets it visits. Like other actions, these are cached and can be run remotely. However the build
command just leaves outputs in bazel-bin
which is a bit hard to discover.
% cat $(bazel info bazel-bin)/src/magic.AspectRulesLintClangTidy.out
6796 warnings generated.
warning: redefining builtin macro [clang-diagnostic-builtin-macro-redefined]
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:17:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Failed to initialize libmagic\n");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:17:9: note: cast the expression to void to silence this warning
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:23:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Cannot load magic database: %s\n", magic_error(magic));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:23:9: note: cast the expression to void to silence this warning
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:31:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Error determining MIME type: %s\n", magic_error(magic));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/tmp/_bazel_alexeagle/4477e74205cb05f1e66aea9fd72f89b1/sandbox/darwin-sandbox/6/execroot/_main/src/magic.c:31:9: note: cast the expression to void to silence this warning
Suppressed 6790 warnings (6790 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.
You could improve the developer experience with a bit of scripting. An example of such a script is included in rules_lint: https://github.com/aspect-build/rules_lint/blob/main/example/lint.sh
However it’s even better to use the Aspect CLI which adds a lint
command to Bazel:
% bazel lint :all
INFO: Analyzed target //src:magic (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:magic up-to-date (nothing to build)
Aspect //tools/lint:linters.bzl%clang_tidy of //src:magic up-to-date:
bazel-bin/src/magic.AspectRulesLintClangTidy.out
bazel-bin/src/magic.AspectRulesLintClangTidy.out.exit_code
bazel-bin/src/magic.AspectRulesLintClangTidy.patch
INFO: Elapsed time: 0.113s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
Lint results for //src:magic:
6796 warnings generated.
warning: redefining builtin macro [clang-diagnostic-builtin-macro-redefined]
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:17:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Failed to initialize libmagic\n");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:17:9: note: cast the expression to void to silence this warning
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:23:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Cannot load magic database: %s\n", magic_error(magic));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:23:9: note: cast the expression to void to silence this warning
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:31:9: warning: the value returned by this function should be used [cert-err33-c]
fprintf(stderr, "Error determining MIME type: %s\n", magic_error(magic));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8n/7cfngjb106sgwb8mr1tytx700000gn/T/rules_lint_patcher-oheSnn/_main/src/magic.c:31:9: note: cast the expression to void to silence this warning
Suppressed 6790 warnings (6790 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.