Clang-Tidy Rules and Checks

Android C/C++ source files can be checked by clang-tidy for issues like coding style, error-prone/performance patterns, and static flow analysis. See the official clang-tidy document and list of clang-tidy checks.

Global defaults for Android builds

The simplest way to enable clang-tidy checks is to set environment variable WITH_TIDY.

$ WITH_TIDY=1 make

This will turn on the global default to run clang-tidy for every required C/C++ source file compilation. The global default clang-tidy checks do not include time-consuming static analyzer checks. To enable those checks, set the CLANG_ANALYZER_CHECKS variable.

$ WITH_TIDY=1 CLANG_ANALYZER_CHECKS=1 make

The default global clang-tidy checks and flags are defined in build/soong/cc/config/tidy.go.

Module clang-tidy properties

The global default can be overwritten by module properties in Android.bp.

tidy and tidy_checks

For example, in system/bpf/Android.bp, clang-tidy is enabled explicitly and with a different check list:

cc_defaults {
    name: "bpf_defaults",
    // snipped
    tidy: true,
    tidy_checks: [
        "android-*",
        "cert-*",
        "-cert-err34-c",
        "clang-analyzer-security*",
        // Disabling due to many unavoidable warnings from POSIX API usage.
        "-google-runtime-int",
    ],
}

That means in normal builds, even without WITH_TIDY=1, the modules that use bpf_defaults will run clang-tidy over C/C++ source files with the given tidy_checks. Note that clang-analyzer-security* is included in tidy_checks but not all clang-analyzer-* checks. Check cert-err34-c is disabled, although cert-* is selected.

Some modules might want to disable clang-tidy even when environment variable WITH_TIDY=1 is set. Examples can be found in system/netd/tests/Android.bp

cc_test {
    name: "netd_integration_test",
    // snipped
    defaults: ["netd_defaults"],
    tidy: false,  // cuts test build time by almost 1 minute

and in bionic/tests/Android.bp.

cc_test_library {
    name: "fortify_disabled_for_tidy",
    // snipped
    srcs: ["clang_fortify_tests.cpp"],
    tidy: false,
}

tidy_checks_as_errors

The global tidy checks are enabled as warnings. If a C/C++ module wants to be free of certain clang-tidy warnings, it can chose those checks to be treated as errors. For example system/core/libsysutils/Android.bp has enabled clang-tidy explicitly, selected its own tidy checks, and set three groups of tidy checks as errors:

cc_library {
    name: "libsysutils",
    // snipped
    tidy: true,
    tidy_checks: [
        "-*",
        "cert-*",
        "clang-analyzer-security*",
        "android-*",
    ],
    tidy_checks_as_errors: [
        "cert-*",
        "clang-analyzer-security*",
        "android-*",
    ],
    // snipped
}

tidy_flags and tidy_disabled_srcs

Extra clang-tidy flags can be passed with the tidy_flags property.

Some Android modules use the tidy_flags to pass “-warnings-as-errors=” to clang-tidy. This usage should now be replaced with the tidy_checks_as_errors property.

Some other tidy flags examples are -format-style= and -header-filter= For example, in art/odrefresh/Android.bp, we found

cc_defaults {
    name: "odrefresh-defaults",
    srcs: [
        "odrefresh.cc",
        "odr_common.cc",
        "odr_compilation_log.cc",
        "odr_fs_utils.cc",
        "odr_metrics.cc",
        "odr_metrics_record.cc",
    ],
    // snipped
    generated_sources: [
        "apex-info-list-tinyxml",
        "art-apex-cache-info",
        "art-odrefresh-operator-srcs",
    ],
    // snipped
    tidy: true,
    tidy_disabled_srcs: [":art-apex-cache-info"],
    tidy_flags: [
        "-format-style=file",
        "-header-filter=(art/odrefresh/|system/apex/)",
    ],
}

That means all modules with the odrefresh-defaults will have clang-tidy enabled, but not for generated source files in art-apex-cache-info. The clang-tidy is called with extra flags to specify the format-style and header-filter.

Note that the globally set default for header-filter is to include only the module directory. So, the default clang-tidy warnings for art/odrefresh modules will include source files under that directory. Now odrefresh-defaults is interested in seeing warnings from both art/odrefresh/ and system/apex/ and it redefines -header-filter in its tidy_flags.

Phony tidy-* targets

The tidy-directory targets

Setting WITH_TIDY=1 is easy to enable clang-tidy globally for any build. However, it adds extra compilation time.

For developers focusing on just one directory, they only want to compile their files with clang-tidy and wish to build other Android components as fast as possible. Changing the WITH_TIDY=1 variable setting is also expensive since the build.ninja file will be regenerated due to any such variable change.

To manually select only some directories or modules to compile with clang-tidy, do not set the WITH_TIDY=1 variable, but use the special tidy-<directory> phony target. For example, a person working on system/libbase can build Android quickly with

unset WITH_TIDY # Optional, not if you haven't set WITH_TIDY
make droid tidy-system-libbase

For any directory d1/d2/d3, a phony target tidy-d1-d2-d3 is generated if there is any C/C++ source file under d1/d2/d3.

Note that with make droid tidy-system-libbase, some C/C++ files that are not needed by the droid target will be passed to clang-tidy if they are under system/libbase. This is like a checkbuild under system/libbase to include all modules, but only C/C++ files of those modules are compiled with clang-tidy.

The tidy-soong target

A special tidy-soong target is defined to include all C/C++ source files in all directories. This phony target is sometimes used to test if all source files compile with a new clang-tidy release.

The tidy-*_subset targets

A subset of each tidy-* phony target is defined to reduce test time. Since any Android module, a C/C++ library or binary, can be built for many different variants, one C/C++ source file is usually compiled multiple times with different compilation flags. Many of such variant flags have little or no effect on clang-tidy checks. To reduce clang-tidy check time, a subset target like tidy-soong_subset or tidy-system-libbase_subset is generated to include only a subset, the first variant, of each module in the directory.

Hence, for C/C++ source code quality, instead of a long “make checkbuild”, we can use “make tidy-soong_subset”.

Limit clang-tidy runtime

Some Android modules have large files that take a long time to compile with clang-tidy, with or without the clang-analyzer checks. To limit clang-tidy time, an environment variable can be set as

WITH_TIDY=1 TIDY_TIMEOUT=90 make

This 90-second limit is actually the default time limit in several Android continuous builds where WITH_TIDY=1 and CLANG_ANALYZER_CHECKS=1 are set.

Capabilities for Android.bp and Android.mk

Some of the previously mentioned features are defined only for modules in Android.bp files, not for Android.mk modules yet.

  • The global WITH_TIDY=1 variable will enable clang-tidy for all C/C++ modules in Android.bp or Android.mk files.

  • The global TIDY_TIMEOUT variable is recognized by Android prebuilt clang-tidy, so it should work for any clang-tidy invocation.

  • The clang-tidy module level properties are defined for Android.bp modules. For Android.mk modules, old LOCAL_TIDY, LOCAL_TIDY_CHECKS, LOCAL_TIDY_FLAGS work similarly, but it would be better to convert those modules to use Android.bp files.

  • The tidy-* phony targets are only generated for Android.bp modules.