From 2cfeebf7089e85ea87e3a2f823bb0939f5d912db Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 10:51:27 -0400 Subject: [PATCH 1/6] Enable clang-tidy tooling Co-authored-by: Cursor --- .clang-tidy | 35 +++++++++++++++++++++++++ .github/workflows/dev.yml | 2 ++ CMakePresets.json | 1 + bin/README.md | 3 +++ bin/check-tidy | 54 +++++++++++++++++++++++++++++++++++++++ docs/development.md | 22 ++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 .clang-tidy create mode 100755 bin/check-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..417996f92 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,35 @@ +# Shared clang-tidy baseline for dd-trace-cpp, httpd-datadog, and nginx-datadog. +# Keep this file in sync across those repositories. +Checks: > + -*, + bugprone-argument-comment, + 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-use-after-move, + bugprone-virtual-near-miss, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + modernize-redundant-void-arg, + performance-move-const-arg, + performance-noexcept-move-constructor, + readability-delete-null-pointer, + readability-redundant-control-flow, + readability-redundant-member-init, + readability-redundant-string-cstr, + readability-simplify-boolean-expr +WarningsAsErrors: '*' +HeaderFilterRegex: '(src|include|binding|test|fuzz|examples)/' +FormatStyle: file diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 50115cecf..4ac58c479 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/CMakePresets.json b/CMakePresets.json index abf0d98ab..bf772c4f7 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/bin/README.md b/bin/README.md index 5a5126ec0..aa1ce7694 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 against first-party sources using + the compilation database in `.build` (or `$BUILD_DIR`). Findings fail the + check. - [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 000000000..d4001d63a --- /dev/null +++ b/bin/check-tidy @@ -0,0 +1,54 @@ +#!/bin/sh +# Run clang-tidy on first-party C++ sources. Findings fail the check. +# +# Requires compile_commands.json. Generate it with: +# cmake . -B .build --preset dev +# or, in CI: +# bin/with-toolchain llvm cmake . -B .build --preset ci-clang + +set -e + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) +cd "$REPO_ROOT" + +build_dir=${BUILD_DIR:-.build} +compile_commands="$build_dir/compile_commands.json" + +find_tidy() { + if command -v clang-tidy-14 >/dev/null 2>&1; then + echo clang-tidy-14 + return + fi + if command -v clang-tidy >/dev/null 2>&1; then + echo clang-tidy + return + fi + return 1 +} + +if ! [ -f "$compile_commands" ]; then + >&2 echo "Missing $compile_commands." + >&2 echo "Configure CMake first so clang-tidy can use the compilation database:" + >&2 echo " cmake . -B ${build_dir} --preset dev" + >&2 echo " bin/check-tidy" + exit 1 +fi + +if ! tidy=$(find_tidy); then + >&2 echo "clang-tidy-14 (or clang-tidy) is not installed." + exit 1 +fi + +# Collect first-party translation units. Headers are analyzed through them. +# Compiler flags from the database may include -Werror. Tidy findings are the +# gate; unknown-to-this-tidy compiler flags should not fail the job. +find binding/ examples/ fuzz/ include/ src/ test/ \ + -type f \( -name '*.cpp' -o -name '*.c' \) \ + ! -path '*/google-benchmark/*' \ + ! -path '*/tinycc/*' \ + -print0 \ + | xargs -0 "$tidy" -p "$build_dir" --quiet --use-color \ + -extra-arg=-Wno-error \ + -extra-arg=-Wno-unknown-warning-option \ + -extra-arg=-Wno-unused-command-line-argument diff --git a/docs/development.md b/docs/development.md index 17191469d..e9ce84017 100644 --- a/docs/development.md +++ b/docs/development.md @@ -22,3 +22,25 @@ command: ```shell bin/format ``` + +To check formatting without writing files: + +```shell +bin/check-format +``` + +## Static Analysis + +C++ is analyzed with clang-tidy using the shared `.clang-tidy` baseline (kept in +sync with `httpd-datadog` and `nginx-datadog`). Warnings are errors. + +Configure CMake so a compilation database exists, then run tidy: + +```shell +cmake . -B .build --preset dev +bin/check-tidy +``` + +`BUILD_DIR` defaults to `.build`. CI runs the same check in the Development +workflow after `ci-clang` configure; a finding fails the pull request. + From 4b02832f5065250e508a230c30fd03f638067dd7 Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 11:00:33 -0400 Subject: [PATCH 2/6] Install clang-tidy-14 in the CI verify job Co-authored-by: Cursor --- .github/workflows/dev.yml | 2 ++ Dockerfile | 2 +- bin/check-tidy | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 4ac58c479..fd3c7a3fa 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: Install clang-tidy + run: apt-get update && apt-get install --yes clang-tidy-14 - name: clang-tidy run: bin/check-tidy - name: Build diff --git a/Dockerfile b/Dockerfile index ef3c9a23c..73b9dcdc2 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/check-tidy b/bin/check-tidy index d4001d63a..ec0eac4d2 100755 --- a/bin/check-tidy +++ b/bin/check-tidy @@ -24,6 +24,13 @@ find_tidy() { echo clang-tidy return fi + for candidate in clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 \ + clang-tidy-17 clang-tidy-16 clang-tidy-15; do + if command -v "$candidate" >/dev/null 2>&1; then + echo "$candidate" + return + fi + done return 1 } @@ -36,7 +43,7 @@ if ! [ -f "$compile_commands" ]; then fi if ! tidy=$(find_tidy); then - >&2 echo "clang-tidy-14 (or clang-tidy) is not installed." + >&2 echo "clang-tidy-14 (or clang-tidy) is not installed. On Ubuntu: apt-get install --yes clang-tidy-14" exit 1 fi From 17cb3f5d87e5640848ff4fbdd963f1386592c02a Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 11:46:49 -0400 Subject: [PATCH 3/6] Pin clang-tidy-14 and run it in the CI container Co-authored-by: Cursor --- .github/workflows/dev.yml | 2 - .gitignore | 1 + bin/README.md | 6 +- bin/check-tidy | 114 ++++++++++++++++++++++++-------------- docs/development.md | 13 +++-- 5 files changed, 84 insertions(+), 52 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index fd3c7a3fa..4ac58c479 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -18,8 +18,6 @@ jobs: run: bin/check-environment-variables - name: Configure run: bin/with-toolchain llvm cmake . -B ${BUILD_DIR} --preset ci-clang - - name: Install clang-tidy - run: apt-get update && apt-get install --yes clang-tidy-14 - name: clang-tidy run: bin/check-tidy - name: Build diff --git a/.gitignore b/.gitignore index cb7439c6c..fd03fcd23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .build/ +.clang-tidy-build/ .cache/ .coverage/ .cursor/ diff --git a/bin/README.md b/bin/README.md index aa1ce7694..a3b596d33 100644 --- a/bin/README.md +++ b/bin/README.md @@ -11,9 +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 against first-party sources using - the compilation database in `.build` (or `$BUILD_DIR`). Findings fail the - check. +- [check-tidy](check-tidy) runs **clang-tidy-14** (pinned) in the CI container. + It configures CMake inside the container and fails on findings. 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 index ec0eac4d2..0bdc56c4d 100755 --- a/bin/check-tidy +++ b/bin/check-tidy @@ -1,10 +1,12 @@ #!/bin/sh -# Run clang-tidy on first-party C++ sources. Findings fail the check. +# Run the pinned clang-tidy in the CI container. # -# Requires compile_commands.json. Generate it with: -# cmake . -B .build --preset dev -# or, in CI: -# bin/with-toolchain llvm cmake . -B .build --preset ci-clang +# clang-tidy is not reproducible across versions, and compile_commands.json +# points at the container sysroot. Do not run tidy against a host CMake +# database (especially on macOS). This script always configures and analyzes +# inside the same image CI uses. +# +# Usage: bin/check-tidy set -e @@ -12,50 +14,80 @@ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) cd "$REPO_ROOT" -build_dir=${BUILD_DIR:-.build} -compile_commands="$build_dir/compile_commands.json" +# Keep this in sync with .github/workflows/dev.yml. +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" ] +} -find_tidy() { - if command -v clang-tidy-14 >/dev/null 2>&1; then - echo clang-tidy-14 - return +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 command -v clang-tidy >/dev/null 2>&1; then - echo clang-tidy - return + if ! docker info >/dev/null 2>&1; then + >&2 echo "Docker is not running. Please start Docker." + exit 1 fi - for candidate in clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 \ - clang-tidy-17 clang-tidy-16 clang-tidy-15; do - if command -v "$candidate" >/dev/null 2>&1; then - echo "$candidate" - return - fi - done - return 1 -} + 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 -if ! [ -f "$compile_commands" ]; then - >&2 echo "Missing $compile_commands." - >&2 echo "Configure CMake first so clang-tidy can use the compilation database:" - >&2 echo " cmake . -B ${build_dir} --preset dev" - >&2 echo " bin/check-tidy" - exit 1 +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 ! tidy=$(find_tidy); then - >&2 echo "clang-tidy-14 (or clang-tidy) is not installed. On Ubuntu: apt-get install --yes clang-tidy-14" +if ! command -v "$tidy" >/dev/null 2>&1; then + >&2 echo "$tidy is not installed." exit 1 fi -# Collect first-party translation units. Headers are analyzed through them. -# Compiler flags from the database may include -Werror. Tidy findings are the -# gate; unknown-to-this-tidy compiler flags should not fail the job. -find binding/ examples/ fuzz/ include/ src/ test/ \ - -type f \( -name '*.cpp' -o -name '*.c' \) \ - ! -path '*/google-benchmark/*' \ - ! -path '*/tinycc/*' \ - -print0 \ - | xargs -0 "$tidy" -p "$build_dir" --quiet --use-color \ +if ! [ -f "$build_dir/compile_commands.json" ]; then + bin/with-toolchain llvm cmake . -B "$build_dir" --preset ci-clang \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +fi + +if command -v "run-clang-tidy-$CLANG_TIDY_VERSION" >/dev/null 2>&1; then + "run-clang-tidy-$CLANG_TIDY_VERSION" -p "$build_dir" -quiet \ -extra-arg=-Wno-error \ -extra-arg=-Wno-unknown-warning-option \ - -extra-arg=-Wno-unused-command-line-argument + -extra-arg=-Wno-unused-command-line-argument \ + binding/ examples/ fuzz/ include/ src/ test/ +else + find binding/ examples/ fuzz/ include/ src/ test/ \ + -type f \( -name '*.cpp' -o -name '*.c' \) \ + ! -path '*/google-benchmark/*' \ + ! -path '*/tinycc/*' \ + -print0 \ + | xargs -0 "$tidy" -p "$build_dir" --quiet --use-color \ + -extra-arg=-Wno-error \ + -extra-arg=-Wno-unknown-warning-option \ + -extra-arg=-Wno-unused-command-line-argument +fi diff --git a/docs/development.md b/docs/development.md index e9ce84017..cbe643dfa 100644 --- a/docs/development.md +++ b/docs/development.md @@ -31,16 +31,17 @@ bin/check-format ## Static Analysis -C++ is analyzed with clang-tidy using the shared `.clang-tidy` baseline (kept in -sync with `httpd-datadog` and `nginx-datadog`). Warnings are errors. +C++ is analyzed with **clang-tidy-14** (pinned; same major as `clang-format-14`) +using the shared `.clang-tidy` baseline. Warnings are errors. -Configure CMake so a compilation database exists, then run tidy: +Do not run clang-tidy on the host. `compile_commands.json` must be produced by +the same container that runs tidy (CMake, compiler, and sysroot). `bin/check-tidy` +re-execs in `datadog/docker-library:dd-trace-cpp-ci-23768e9-*`, configures CMake +there, and runs `clang-tidy-14`: ```shell -cmake . -B .build --preset dev bin/check-tidy ``` -`BUILD_DIR` defaults to `.build`. CI runs the same check in the Development -workflow after `ci-clang` configure; a finding fails the pull request. +CI runs the same script in that image. A finding fails the pull request. From bb72b52a58b3afac887254717af195b9c8326612 Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 12:29:07 -0400 Subject: [PATCH 4/6] Limit clang-tidy to configured src/ sources Co-authored-by: Cursor --- .clang-tidy | 2 +- bin/check-tidy | 55 ++++++++++++++++++++++++++++++++------------- docs/development.md | 3 ++- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 417996f92..69feca593 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -31,5 +31,5 @@ Checks: > readability-redundant-string-cstr, readability-simplify-boolean-expr WarningsAsErrors: '*' -HeaderFilterRegex: '(src|include|binding|test|fuzz|examples)/' +HeaderFilterRegex: '^src/' FormatStyle: file diff --git a/bin/check-tidy b/bin/check-tidy index 0bdc56c4d..09d5aa490 100755 --- a/bin/check-tidy +++ b/bin/check-tidy @@ -74,20 +74,43 @@ if ! [ -f "$build_dir/compile_commands.json" ]; then -DCMAKE_EXPORT_COMPILE_COMMANDS=ON fi -if command -v "run-clang-tidy-$CLANG_TIDY_VERSION" >/dev/null 2>&1; then - "run-clang-tidy-$CLANG_TIDY_VERSION" -p "$build_dir" -quiet \ - -extra-arg=-Wno-error \ - -extra-arg=-Wno-unknown-warning-option \ - -extra-arg=-Wno-unused-command-line-argument \ - binding/ examples/ fuzz/ include/ src/ test/ -else - find binding/ examples/ fuzz/ include/ src/ test/ \ - -type f \( -name '*.cpp' -o -name '*.c' \) \ - ! -path '*/google-benchmark/*' \ - ! -path '*/tinycc/*' \ - -print0 \ - | xargs -0 "$tidy" -p "$build_dir" --quiet --use-color \ - -extra-arg=-Wno-error \ - -extra-arg=-Wno-unknown-warning-option \ - -extra-arg=-Wno-unused-command-line-argument +# Only first-party library sources that CMake actually configured. +# Skip binding/, examples/, fuzz/, test/, and vendored trees. +files=$(python3 - "$build_dir/compile_commands.json" "$REPO_ROOT" <<'PY' +import json, os, sys +db_path, root = sys.argv[1], sys.argv[2] +includes = ("src/",) +excludes = () +seen = [] +for ent in json.load(open(db_path)): + path = ent.get("file") or "" + if not os.path.isabs(path): + path = os.path.normpath(os.path.join(ent.get("directory", root), path)) + try: + rel = os.path.relpath(path, root) + except ValueError: + continue + if rel.startswith("..") or not rel.endswith((".c", ".cc", ".cpp", ".cxx")): + continue + if any(rel == e.rstrip("/") or rel.startswith(e) for e in excludes): + continue + if not any(rel.startswith(i) for i in includes): + continue + if path not in seen: + seen.append(path) +for path in seen: + print(path) +PY +) + +if [ -z "$files" ]; then + >&2 echo "No configured first-party sources (src/) in $build_dir/compile_commands.json." + exit 1 fi + +# shellcheck disable=SC2086 +"$tidy" -p "$build_dir" --quiet --use-color \ + -extra-arg=-Wno-error \ + -extra-arg=-Wno-unknown-warning-option \ + -extra-arg=-Wno-unused-command-line-argument \ + $files diff --git a/docs/development.md b/docs/development.md index cbe643dfa..0d956b53a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -37,7 +37,8 @@ using the shared `.clang-tidy` baseline. Warnings are errors. Do not run clang-tidy on the host. `compile_commands.json` must be produced by the same container that runs tidy (CMake, compiler, and sysroot). `bin/check-tidy` re-execs in `datadog/docker-library:dd-trace-cpp-ci-23768e9-*`, configures CMake -there, and runs `clang-tidy-14`: +there, and runs `clang-tidy-14` on configured `src/` only (no examples, tests, +bindings, or vendored code): ```shell bin/check-tidy From 7f2eedda1a3633c5fd7a77f8189bb97509439475 Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 13:07:04 -0400 Subject: [PATCH 5/6] Run clang-tidy via CMake CXX_CLANG_TIDY Co-authored-by: Cursor --- CMakeLists.txt | 7 +++++ bin/README.md | 6 ++-- bin/check-tidy | 71 ++++++++++++++++----------------------------- docs/development.md | 10 +++---- 4 files changed, 40 insertions(+), 54 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 787f91fb6..87fb8fdc4 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/bin/README.md b/bin/README.md index a3b596d33..d03a96449 100644 --- a/bin/README.md +++ b/bin/README.md @@ -11,9 +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) in the CI container. - It configures CMake inside the container and fails on findings. Do not point - it at a host `compile_commands.json`. +- [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 index 09d5aa490..187a0e87a 100755 --- a/bin/check-tidy +++ b/bin/check-tidy @@ -1,10 +1,10 @@ #!/bin/sh -# Run the pinned clang-tidy in the CI container. +# Run the pinned clang-tidy through CMake in the CI container. # -# clang-tidy is not reproducible across versions, and compile_commands.json -# points at the container sysroot. Do not run tidy against a host CMake -# database (especially on macOS). This script always configures and analyzes -# inside the same image CI uses. +# 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 @@ -14,7 +14,7 @@ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) cd "$REPO_ROOT" -# Keep this in sync with .github/workflows/dev.yml. +# 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 @@ -69,48 +69,27 @@ if ! command -v "$tidy" >/dev/null 2>&1; then exit 1 fi -if ! [ -f "$build_dir/compile_commands.json" ]; then - bin/with-toolchain llvm cmake . -B "$build_dir" --preset ci-clang \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +compiler=${CXX:-clang++} +if ! command -v "$compiler" >/dev/null 2>&1; then + >&2 echo "$compiler is required (same toolchain as CMake)." + exit 1 fi -# Only first-party library sources that CMake actually configured. -# Skip binding/, examples/, fuzz/, test/, and vendored trees. -files=$(python3 - "$build_dir/compile_commands.json" "$REPO_ROOT" <<'PY' -import json, os, sys -db_path, root = sys.argv[1], sys.argv[2] -includes = ("src/",) -excludes = () -seen = [] -for ent in json.load(open(db_path)): - path = ent.get("file") or "" - if not os.path.isabs(path): - path = os.path.normpath(os.path.join(ent.get("directory", root), path)) - try: - rel = os.path.relpath(path, root) - except ValueError: - continue - if rel.startswith("..") or not rel.endswith((".c", ".cc", ".cpp", ".cxx")): - continue - if any(rel == e.rstrip("/") or rel.startswith(e) for e in excludes): - continue - if not any(rel.startswith(i) for i in includes): - continue - if path not in seen: - seen.append(path) -for path in seen: - print(path) -PY -) - -if [ -z "$files" ]; then - >&2 echo "No configured first-party sources (src/) in $build_dir/compile_commands.json." +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 -# shellcheck disable=SC2086 -"$tidy" -p "$build_dir" --quiet --use-color \ - -extra-arg=-Wno-error \ - -extra-arg=-Wno-unknown-warning-option \ - -extra-arg=-Wno-unused-command-line-argument \ - $files +# 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 0d956b53a..75f0b1488 100644 --- a/docs/development.md +++ b/docs/development.md @@ -34,11 +34,11 @@ bin/check-format 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. `compile_commands.json` must be produced by -the same container that runs tidy (CMake, compiler, and sysroot). `bin/check-tidy` -re-execs in `datadog/docker-library:dd-trace-cpp-ci-23768e9-*`, configures CMake -there, and runs `clang-tidy-14` on configured `src/` only (no examples, tests, -bindings, or vendored code): +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 From 70b56c4e1fcdbda332f21c53250b15aef64aedcf Mon Sep 17 00:00:00 2001 From: Colin Higgins Date: Thu, 24 Sep 2026 13:26:08 -0400 Subject: [PATCH 6/6] Defer clang-tidy checks with existing findings Co-authored-by: Cursor --- .clang-tidy | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 69feca593..429ecdab0 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,7 +2,6 @@ # Keep this file in sync across those repositories. Checks: > -*, - bugprone-argument-comment, bugprone-assert-side-effect, bugprone-bool-pointer-implicit-conversion, bugprone-copy-constructor-init, @@ -17,19 +16,21 @@ Checks: > bugprone-string-constructor, bugprone-undelegated-constructor, bugprone-unused-raii, - bugprone-use-after-move, bugprone-virtual-near-miss, - misc-redundant-expression, - misc-static-assert, misc-unused-using-decls, - modernize-redundant-void-arg, - performance-move-const-arg, - performance-noexcept-move-constructor, readability-delete-null-pointer, readability-redundant-control-flow, - readability-redundant-member-init, - readability-redundant-string-cstr, - readability-simplify-boolean-expr + 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