diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..429ecdab --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,36 @@ +# Shared clang-tidy baseline for dd-trace-cpp, httpd-datadog, and nginx-datadog. +# Keep this file in sync across those repositories. +Checks: > + -*, + bugprone-assert-side-effect, + bugprone-bool-pointer-implicit-conversion, + bugprone-copy-constructor-init, + bugprone-dangling-handle, + bugprone-forwarding-reference-overload, + bugprone-inaccurate-erase, + bugprone-infinite-loop, + bugprone-macro-repeated-side-effects, + bugprone-move-forwarding-reference, + bugprone-parent-virtual-call, + bugprone-posix-return, + bugprone-string-constructor, + bugprone-undelegated-constructor, + bugprone-unused-raii, + bugprone-virtual-near-miss, + misc-unused-using-decls, + readability-delete-null-pointer, + readability-redundant-control-flow, + readability-redundant-string-cstr +# Deferred until existing findings are cleaned up: +# bugprone-argument-comment +# bugprone-use-after-move +# misc-redundant-expression +# misc-static-assert +# modernize-redundant-void-arg +# performance-move-const-arg +# performance-noexcept-move-constructor +# readability-redundant-member-init +# readability-simplify-boolean-expr +WarningsAsErrors: '*' +HeaderFilterRegex: '^src/' +FormatStyle: file diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 50115cec..4ac58c47 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -18,6 +18,8 @@ jobs: run: bin/check-environment-variables - name: Configure run: bin/with-toolchain llvm cmake . -B ${BUILD_DIR} --preset ci-clang + - name: clang-tidy + run: bin/check-tidy - name: Build run: cmake --build ${BUILD_DIR} -j --target config-inversion -v - name: Verify supported configurations metadata diff --git a/.gitignore b/.gitignore index cb7439c6..fd03fcd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .build/ +.clang-tidy-build/ .cache/ .coverage/ .cursor/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 787f91fb..87fb8fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) option(DD_TRACE_BUILD_BENCHMARK "Build benchmark binaries" OFF) option(DD_TRACE_ENABLE_COVERAGE "Build code with code coverage profiling instrumentation" OFF) option(DD_TRACE_ENABLE_SANITIZE "Build with address sanitizer and undefined behavior sanitizer" OFF) + option(DD_TRACE_ENABLE_CLANG_TIDY "Run clang-tidy on first-party sources during build" OFF) + set(DD_TRACE_CLANG_TIDY "clang-tidy-14" CACHE STRING "clang-tidy executable when DD_TRACE_ENABLE_CLANG_TIDY is ON") endif() # Include mandatory files @@ -258,6 +260,11 @@ set_target_properties(dd-trace-cpp-objects POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) +if(DD_TRACE_ENABLE_CLANG_TIDY) + set_property(TARGET dd-trace-cpp-objects PROPERTY CXX_CLANG_TIDY + "${DD_TRACE_CLANG_TIDY};--quiet;--use-color") +endif() + install( TARGETS dd-trace-cpp-objects dd-trace-cpp-specs EXPORT dd-trace-cpp-targets diff --git a/CMakePresets.json b/CMakePresets.json index abf0d98a..bf772c4f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -39,6 +39,7 @@ "displayName": "CI Clang", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", "DD_TRACE_ENABLE_SANITIZE": "ON", "DD_TRACE_BUILD_TOOLS": "ON", "DD_TRACE_BUILD_TESTING": "ON" diff --git a/Dockerfile b/Dockerfile index ef3c9a23..73b9dcdc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ run apt-get update && apt-get install --yes software-properties-common && \ add-apt-repository ppa:git-core/ppa --yes && \ apt-get update && apt-get upgrade --yes && \ apt-get install --yes \ - wget build-essential clang sed gdb clang-format git ssh shellcheck \ + wget build-essential clang sed gdb clang-format clang-tidy git ssh shellcheck \ libc++-dev libc++abi-dev python3 pip coreutils curl gnupg nodejs # bazelisk, a launcher for bazel. `bazelisk --help` will cause the latest diff --git a/bin/README.md b/bin/README.md index 5a5126ec..d03a9644 100644 --- a/bin/README.md +++ b/bin/README.md @@ -11,6 +11,9 @@ This directory contains scripts that are useful during development. before pushing changes. - [check-format](check-format) verifies that the source code is formatted as [format](format) prefers. +- [check-tidy](check-tidy) runs **clang-tidy-14** (pinned, same major as the + CI Clang) through CMake's `CXX_CLANG_TIDY` on `dd-trace-cpp-objects`. Do + not point it at a host `compile_commands.json`. - [check-version](check-version) accepts a version string as a command line argument (e.g. "v1.2.3") and checks whether the version within the source code matches. This is a good check to perform before publishing a source release. diff --git a/bin/check-tidy b/bin/check-tidy new file mode 100755 index 00000000..187a0e87 --- /dev/null +++ b/bin/check-tidy @@ -0,0 +1,95 @@ +#!/bin/sh +# Run the pinned clang-tidy through CMake in the CI container. +# +# clang-tidy must use the same compiler, flags, and stdlib as the real +# build (ci-clang + libc++). CMake's CXX_CLANG_TIDY on +# dd-trace-cpp-objects does that: it invokes tidy with the exact compile +# line after `--`. Do not run tidy against a host compilation database. +# +# Usage: bin/check-tidy + +set -e + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) +cd "$REPO_ROOT" + +# Keep this in sync with .github/workflows/dev.yml and cmake/compiler/clang.cmake. +CLANG_TIDY_VERSION=14 +CI_IMAGE_BASE=datadog/docker-library:dd-trace-cpp-ci-23768e9 + +in_container() { + [ -f /.dockerenv ] || [ -n "${KUBERNETES_SERVICE_HOST:-}" ] || \ + [ "${DD_TRACE_CPP_TIDY_IN_CONTAINER:-}" = "1" ] +} + +docker_arch() { + case "$(uname -m)" in + arm64|aarch64) echo arm64 ;; + *) echo amd64 ;; + esac +} + +if ! in_container; then + if ! command -v docker >/dev/null 2>&1; then + >&2 echo "docker is required to run clang-tidy-$CLANG_TIDY_VERSION." + exit 1 + fi + if ! docker info >/dev/null 2>&1; then + >&2 echo "Docker is not running. Please start Docker." + exit 1 + fi + arch=$(docker_arch) + image="${CI_IMAGE_BASE}-${arch}" + exec docker run --rm -t \ + --platform "linux/${arch}" \ + -e DD_TRACE_CPP_TIDY_IN_CONTAINER=1 \ + -e BUILD_DIR=.clang-tidy-build \ + -v "$REPO_ROOT:$REPO_ROOT" \ + -w "$REPO_ROOT" \ + "$image" \ + bin/check-tidy +fi + +build_dir=${BUILD_DIR:-.clang-tidy-build} +tidy=clang-tidy-$CLANG_TIDY_VERSION + +if ! command -v "$tidy" >/dev/null 2>&1; then + if [ -f /etc/debian_version ]; then + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install --yes "$tidy" + else + >&2 echo "$tidy is required (pinned). Install it in this container." + exit 1 + fi +fi + +if ! command -v "$tidy" >/dev/null 2>&1; then + >&2 echo "$tidy is not installed." + exit 1 +fi + +compiler=${CXX:-clang++} +if ! command -v "$compiler" >/dev/null 2>&1; then + >&2 echo "$compiler is required (same toolchain as CMake)." + exit 1 +fi + +compiler_major=$("$compiler" -dumpversion | cut -d. -f1) +tidy_major=$("$tidy" --version | sed -n 's/.*version \([0-9][0-9]*\).*/\1/p' | head -n 1) +if [ "$compiler_major" != "$CLANG_TIDY_VERSION" ] || [ "$tidy_major" != "$CLANG_TIDY_VERSION" ]; then + >&2 echo "clang-tidy and the CMake compiler must both be LLVM $CLANG_TIDY_VERSION." + >&2 echo " $compiler: $compiler_major" + >&2 echo " $tidy: $tidy_major" + exit 1 +fi + +# Reconfigure so CXX_CLANG_TIDY is attached to dd-trace-cpp-objects, then +# compile that target. CMake passes the exact ci-clang compile line +# (including -stdlib=libc++) to tidy. First-party src/ only; no extra-args. +bin/with-toolchain llvm cmake . -B "$build_dir" --preset ci-clang \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DDD_TRACE_ENABLE_CLANG_TIDY=ON \ + -DDD_TRACE_CLANG_TIDY="$tidy" + +cmake --build "$build_dir" --target dd-trace-cpp-objects -j diff --git a/docs/development.md b/docs/development.md index 17191469..75f0b148 100644 --- a/docs/development.md +++ b/docs/development.md @@ -22,3 +22,27 @@ command: ```shell bin/format ``` + +To check formatting without writing files: + +```shell +bin/check-format +``` + +## Static Analysis + +C++ is analyzed with **clang-tidy-14** (pinned; same major as `clang-format-14`) +using the shared `.clang-tidy` baseline. Warnings are errors. + +Do not run clang-tidy on the host. Tidy must use the same compiler, flags, and +stdlib as the real build (`ci-clang` + libc++). `bin/check-tidy` re-execs in +`datadog/docker-library:dd-trace-cpp-ci-23768e9-*`, configures CMake there with +`DD_TRACE_ENABLE_CLANG_TIDY`, and builds `dd-trace-cpp-objects` so CMake +invokes `clang-tidy-14` with the exact compile line (first-party `src/` only): + +```shell +bin/check-tidy +``` + +CI runs the same script in that image. A finding fails the pull request. +