#!/bin/bash
set -e
SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
PROJECT_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
COMPILE_DB=$(find "./" -name "compile_commands.json" -type f 2>/dev/null | head -n 1)
BASE_BRANCH="origin/master"
FORMAT_WARNING_COUNT=0
TIDY_ERROR_COUNT=0
TIDY_WARNING_COUNT=0
MANUAL_COMPILE_FLAGS="-std=c++17 -I${PROJECT_ROOT}/include"
setup_compile_flags() {
if [[ -n "$LLVM_INSTALL_PREFIX" && -d "$LLVM_INSTALL_PREFIX/include" ]]; then
MANUAL_COMPILE_FLAGS="$MANUAL_COMPILE_FLAGS -I${LLVM_INSTALL_PREFIX}/include"
fi
local gcc_version=$(gcc -dumpversion 2>/dev/null || echo "9")
local arch=$(uname -m)
local triple="${arch}-linux-gnu"
for dir in \
"/usr/lib/gcc/${triple}/${gcc_version}/include" \
"/usr/include/c++/${gcc_version}" \
"/usr/include/c++/${gcc_version}/backward" \
"/usr/include/${triple}/c++/${gcc_version}" \
"/usr/include/${triple}/c++/${gcc_version}/bits"; do
[[ -d "$dir" ]] && MANUAL_COMPILE_FLAGS="$MANUAL_COMPILE_FLAGS -I$dir"
done
}
log() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >&2
}
check_and_install_tools() {
log "[INFO] Installing clang tools..."
apt-get update -qq 2>&1 || true
apt-get install -y clang clang-format clang-tidy clang-tools 2>&1
local tools="clang-format clang-tidy clang-format-diff clang-tidy-diff"
for tool in $tools; do
command -v "$tool" &> /dev/null || { log "[ERROR] $tool installation failed"; exit 1; }
done
log "[INFO] clang tools installed successfully"
}
run_clang_format_diff_check() {
log "[INFO] Running clang-format-diff check (base: $BASE_BRANCH)..."
local format_output=$(git diff -U0 "$BASE_BRANCH" HEAD | clang-format-diff -p1 2>&1) || true
if [[ -n "$format_output" ]]; then
FORMAT_WARNING_COUNT=$(echo "$format_output" | grep "^-" | grep -v "^---" | wc -l | xargs)
log "[FORMAT ISSUE] Found formatting issues in changed lines"
echo "$format_output"
log "[WARNING] $FORMAT_WARNING_COUNT lines need formatting"
log "[HINT] Fix: git diff -U0 $BASE_BRANCH HEAD | clang-format-diff -p1 -i"
fi
}
run_clang_tidy_diff_check() {
log "[INFO] Running clang-tidy-diff check (base: $BASE_BRANCH)..."
[[ -n "$COMPILE_DB" ]] && log "[INFO] Using compile_commands.json: $COMPILE_DB"
[[ -z "$COMPILE_DB" ]] && log "[WARNING] compile_commands.json not found, using manual flags"
local tidy_output=""
if [[ -n "$COMPILE_DB" ]]; then
tidy_output=$(git diff -U0 "$BASE_BRANCH" HEAD | clang-tidy-diff -p1 -clang-tidy-binary clang-tidy -path "$(dirname $COMPILE_DB)" 2>&1) || true
else
local tidy_args=(-p1 -clang-tidy-binary clang-tidy -extra-arg-before="-x" -extra-arg-before="c++")
for flag in $MANUAL_COMPILE_FLAGS; do
tidy_args+=(-extra-arg-before="$flag")
done
tidy_output=$(git diff -U0 "$BASE_BRANCH" HEAD | clang-tidy-diff "${tidy_args[@]}" 2>&1) || true
fi
if [[ -n "$tidy_output" ]]; then
set +e
local filtered_output=$(echo "$tidy_output" | awk '
BEGIN { in_diagnostic = 0 }
/^[^ ]+:[0-9]+:[0-9]+:.*(warning:|error:|note:)/ {
if ($0 ~ /mlir\// || $0 ~ /llvm\// || $0 ~ /\.inc/) { in_diagnostic = 0; next }
in_diagnostic = 1; print; next
}
/^[[:space:]]/ && in_diagnostic { print; next }
{ in_diagnostic = 0 }')
set -e
if [[ -n "$filtered_output" ]]; then
TIDY_ERROR_COUNT=$(echo "$filtered_output" | grep -c "error:" | xargs || echo 0)
TIDY_WARNING_COUNT=$(echo "$filtered_output" | grep -c "warning:" | xargs || echo 0)
log "[TIDY ISSUE] Found static analysis issues"
echo "$filtered_output"
log "[HINT] Reproduce: git diff -U0 $BASE_BRANCH HEAD | clang-tidy-diff -p1 -clang-tidy-binary clang-tidy -extra-arg-before='-x' -extra-arg-before='c++' ${MANUAL_COMPILE_FLAGS//-I/-extra-arg-before=-I}"
fi
fi
[[ "$TIDY_ERROR_COUNT" -gt 0 ]] && log "[ERROR] $TIDY_ERROR_COUNT errors"
[[ "$TIDY_WARNING_COUNT" -gt 0 ]] && log "[WARNING] $TIDY_WARNING_COUNT warnings"
}
print_summary() {
echo ""
echo "----------------------------------------------------------------"
echo "Static Check Summary"
echo "----------------------------------------------------------------"
echo "clang-format-diff: $FORMAT_WARNING_COUNT lines need formatting"
echo "clang-tidy-diff: $TIDY_ERROR_COUNT error(s), $TIDY_WARNING_COUNT warning(s)"
echo "----------------------------------------------------------------"
if [[ $FORMAT_WARNING_COUNT -gt 0 || $TIDY_ERROR_COUNT -gt 0 || $TIDY_WARNING_COUNT -gt 0 ]]; then
echo "Status: FAILED"
return 1
fi
echo "Status: PASSED"
}
main() {
log "[INFO] Checking changed code between $BASE_BRANCH and HEAD"
local cpp_files=$(git diff --name-only "$BASE_BRANCH" HEAD | grep -E '\.cpp$|\.h$')
if [[ -z "$cpp_files" ]]; then
log "[INFO] No C/C++ files changed"
print_summary
exit 0
fi
log "[INFO] Changed C/C++ files: $cpp_files"
check_and_install_tools
setup_compile_flags
run_clang_format_diff_check
run_clang_tidy_diff_check
print_summary
exit $?
}
main