From fca0c19207c9d81f69e6447faf76157f9404ab13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20K=C3=BChne?= Date: Wed, 8 Apr 2026 20:23:22 +0200 Subject: [PATCH] Add clang-tidy configuration and watch script for improved code quality checks --- .clang-tidy | 28 +++++++++++++++ .vscode/settings.json | 3 ++ .vscode/tasks.json | 64 ++++++++++++++++++++++++++++++++++ CMakeLists.txt | 29 ++++++++++++++++ tools/clang_tidy_watch.sh | 73 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 .clang-tidy create mode 100644 tools/clang_tidy_watch.sh diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..2e66b1a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,28 @@ +--- +Checks: > + -*, + clang-analyzer-*, + bugprone-*, + cert-*, + cppcoreguidelines-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, + -modernize-use-trailing-return-type, + -cppcoreguidelines-owning-memory +# WarningsAsErrors: "*" +HeaderFilterRegex: "^(include|source)/" +FormatStyle: none +SystemHeaders: false +CheckOptions: + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.FunctionCase + value: lower_case + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: cppcoreguidelines-pro-bounds-pointer-arithmetic.AllowIncrementDecrementOperators + value: "true" \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c059d3c..0a45505 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,12 @@ { + "C_Cpp.intelliSenseEngine": "disabled", "files.associations": { "array": "cpp", "*.c": "c" }, "clangd.arguments": [ + "--background-index", + "--clang-tidy", "--query-driver=${env:DEVKITPRO}/devkitARM/bin/arm-none-eabi-*", "--compile-commands-dir=." ] diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bc94738..a9855bf 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -69,6 +69,70 @@ "command": "cd ${workspaceFolder} && if [ -d build ]; then cmake --build build --target clean; fi && if [ -d build-debug ]; then cmake --build build-debug --target clean; fi", "problemMatcher": [], }, + { + "label": "clang-tidy (all)", + "type": "shell", + "command": "cd ${workspaceFolder} && mkdir -p .clang-tidy-db && sed 's/-mthumb-interwork//g' compile_commands.json > .clang-tidy-db/compile_commands.json && run-clang-tidy -p .clang-tidy-db '.*\\.(c|cc|cpp|cxx)$' -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0 -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0/arm-none-eabi -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0/backward -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/lib/gcc/arm-none-eabi/15.2.0/include -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/lib/gcc/arm-none-eabi/15.2.0/include-fixed -extra-arg=-isystem -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include", + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error|note):\\s+(.*?)(?:\\s+\\[.*\\])?$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ], + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "clear": true + } + }, + { + "label": "clang-tidy (watch changed)", + "type": "shell", + "isBackground": true, + "command": "cd ${workspaceFolder} && bash tools/clang_tidy_watch.sh", + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error|note):\\s+(.*?)(?:\\s+\\[.*\\])?$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "^TIDY-WATCH-BEGIN$", + "endsPattern": "^TIDY-WATCH-END$" + } + } + ], + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "clear": true + } + }, { "label": "run", "type": "shell", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c0f757..807014a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,8 @@ include("${DEVKITPRO_CMAKE}") project(snakePlus LANGUAGES C CXX ASM) +option(ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) + # Export compile_commands.json for tooling (clangd, IntelliSense, static analysis). set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -30,6 +32,24 @@ set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) +if(ENABLE_CLANG_TIDY) + find_program(CLANG_TIDY_EXE NAMES clang-tidy) + if(NOT CLANG_TIDY_EXE) + message(FATAL_ERROR "ENABLE_CLANG_TIDY is ON, but clang-tidy was not found") + endif() + + set(CMAKE_C_CLANG_TIDY + ${CLANG_TIDY_EXE} + --header-filter=^(include|source)/ + --extra-arg=--target=arm-none-eabi + ) + set(CMAKE_CXX_CLANG_TIDY + ${CLANG_TIDY_EXE} + --header-filter=^(include|source)/ + --extra-arg=--target=arm-none-eabi + ) +endif() + set(TARGET_NAME ${PROJECT_NAME}) set(GAME_TITLE "Snake Plus") @@ -122,6 +142,15 @@ target_compile_definitions(${TARGET_NAME} PRIVATE target_compile_options(${TARGET_NAME} PRIVATE -g -Wall + -Wextra + -Wpedantic + -Wconversion + -Wsign-conversion + -Wshadow + -Wformat=2 + -Wundef + -Wnull-dereference + -Wimplicit-fallthrough -O3 -marm -mthumb-interwork diff --git a/tools/clang_tidy_watch.sh b/tools/clang_tidy_watch.sh new file mode 100644 index 0000000..4e39611 --- /dev/null +++ b/tools/clang_tidy_watch.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -u + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT_DIR" || exit 1 + +EXTRA_ARGS=( + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0 + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0/arm-none-eabi + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include/c++/15.2.0/backward + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/lib/gcc/arm-none-eabi/15.2.0/include + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/lib/gcc/arm-none-eabi/15.2.0/include-fixed + -extra-arg=-isystem + -extra-arg=/opt/devkitpro/devkitARM/arm-none-eabi/include +) + +rebuild_tidy_db() { + mkdir -p .clang-tidy-db + sed 's/-mthumb-interwork//g' compile_commands.json > .clang-tidy-db/compile_commands.json +} + +collect_sources() { + find source include -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' \) 2>/dev/null | sort +} + +declare -A mtimes +last_cc_mtime="" + +while true; do + if [[ ! -f compile_commands.json ]]; then + echo "Waiting for compile_commands.json (run CMake configure/build once)..." + sleep 2 + continue + fi + + cc_mtime="$(stat -c %Y compile_commands.json 2>/dev/null || echo 0)" + db_regenerated=0 + if [[ "$cc_mtime" != "$last_cc_mtime" ]]; then + rebuild_tidy_db + last_cc_mtime="$cc_mtime" + db_regenerated=1 + fi + + mapfile -t files < <(collect_sources) + changed=() + + for f in "${files[@]}"; do + m="$(stat -c %Y "$f" 2>/dev/null || echo 0)" + if [[ "${mtimes[$f]:-}" != "$m" ]]; then + changed+=("$f") + mtimes["$f"]="$m" + fi + done + + if [[ $db_regenerated -eq 1 && ${#files[@]} -gt 0 ]]; then + changed=("${files[@]}") + fi + + if [[ ${#changed[@]} -gt 0 ]]; then + echo "TIDY-WATCH-BEGIN" + for f in "${changed[@]}"; do + clang-tidy -quiet -p .clang-tidy-db "$f" "${EXTRA_ARGS[@]}" || true + done + echo "TIDY-WATCH-END" + fi + + sleep 1 +done