Skip to content

oliverlee/rules_clang_tidy

Repository files navigation

rules_clang_tidy

Run clang-tidy on Bazel C++ targets. This project is heavily inspired by bazel_clang_tidy but has changes made to better fit my workflow.

usage

# //:WORKSPACE.bazel
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_repository")

RULES_CLANG_TIDY_COMMIT = ...

http_repository(
    name = "rules_clang_tidy",
    integrity = ...,
    strip_prefix = "rules_clang_tidy-{commit}".format(
        commit = RULES_CLANG_TIDY_COMMIT,
    ),
    url = "https://github.com/oliverlee/rules_clang_tidy/archive/{commit}.tar.gz".format(
        commit = RULES_CLANG_TIDY_COMMIT,
    ),
)

load("@rules_clang_tidy//:dependencies.bzl", "rules_clang_tidy_dependencies")

rules_clang_tidy_dependencies()
# //:.bazelrc
build:clang-tidy --aspects=@rules_clang_tidy//:aspects.bzl%check
build:clang-tidy --output_groups=report
build:clang-tidy --remote_download_outputs=toplevel
build:clang-tidy --keep_going

Perform static analysis with

bazel build //... --config=clang-tidy

This will use clang-tidy in your PATH and .clang-tidy defined in this repository.

using a hermetic toolchain

To specify a specific binary (e.g. clang-tidy is specified by a hermetic toolchain like this), update the build setting in .bazelrc.

# //:.bazelrc

build --@rules_clang_tidy//:clang-tidy=@llvm18//:clang-tidy

build:clang-tidy --aspects=@rules_clang_tidy//:aspects.bzl%check
build:clang-tidy --output_groups=report
build:clang-tidy --remote_download_outputs=toplevel
build:clang-tidy --keep_going

specifying .clang-tidy

To override the default .clang-tidy, define a filegroup containing the replacement config and update build setting in .bazelrc.

# //:BUILD.bazel

filegroup(
    name = "clang-tidy-config",
    srcs = [".clang-tidy"],
    visibility = ["//visibility:public"],
)
# //:.bazelrc

build --@rules_clang_tidy//:config=//:clang-tidy-config

build:clang-tidy --aspects=@rules_clang_tidy//:aspects.bzl%check
build:clang-tidy --output_groups=report
build:clang-tidy --remote_download_outputs=toplevel
build:clang-tidy --keep_going

applying fixes

To apply fixes, run @rules_clang_tidy//:apply-fixes. This uses the exported fixes generated by the check aspect in the output_path.

bazel build //... --config=clang-tidy
bazel run @rules_clang_tidy//:apply-fixes -- $(bazel info output_path)

If only a subset of checks needs to be run, those can be specified with extra-options. This flag can be specified multiples times.

bazel build //... --config=clang-tidy \
  --@rules_clang_tidy//:extra-options="--checks=-*,misc-unused-alias-decls"
bazel run @rules_clang_tidy//:apply-fixes -- $(bazel info output_path)

Alternatively, use rule apply_fixes and specify the dependencies for the target.

load("@rules_clang_tidy//:defs.bzl", "apply_fixes")

apply_fixes(
    name = "apply-fixes",
    deps = [
        ...
    ],
    desired_deps = "//...", # requires Bazel 7.1.0
    testonly = True, # if deps includes cc_test targets
)

and run the apply_fixes target

bazel run //:apply-fixes

# or generate and apply fixes for only a single check
bazel run //:apply-fixes \
  --@rules_clang_tidy//:extra-options="--checks=-*,misc-unused-alias-decls*"

Both the apply-fixes executable target and the apply_fixes rule use the binary specified with --@rules_clang_tidy//:clang-apply-replacements. If not set, clang-apply-replacements must be in PATH. Similarly to --@rules_clang_tidy//:clang-tidy, it's convenient to define the value in .bazelrc.

# //:.bazelrc

build --@rules_clang_tidy//:clang-apply-replacements=@llvm18//:clang-apply-replacements

Requirements

  • Bazel 5.x
  • ClangTidy ?? (at least 14?)