diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..fd89a7f9 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,11 @@ +Checks: > + -*, + clang-analyzer-*, + bugprone-*, + performance-*, + modernize-use-nullptr, + modernize-use-override +WarningsAsErrors: "*" +HeaderFilterRegex: "^(src|tests|tools)/" +FormatStyle: none +SystemHeaders: false diff --git a/.github/workflows/clang_tidy.yml b/.github/workflows/clang_tidy.yml new file mode 100644 index 00000000..21a6b923 --- /dev/null +++ b/.github/workflows/clang_tidy.yml @@ -0,0 +1,146 @@ +name: Clang-Tidy + +on: + push: + branches: ["main"] + paths: + - "**/*.c" + - "**/*.cc" + - "**/*.cpp" + - "**/*.cxx" + - "**/*.h" + - "**/*.hpp" + - "CMakeLists.txt" + - "**/CMakeLists.txt" + - "cmake/**" + - ".clang-tidy" + pull_request: + branches: ["main"] + paths: + - "**/*.c" + - "**/*.cc" + - "**/*.cpp" + - "**/*.cxx" + - "**/*.h" + - "**/*.hpp" + - "CMakeLists.txt" + - "**/CMakeLists.txt" + - "cmake/**" + - ".clang-tidy" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + clang_tidy: + name: Clang-Tidy Checks + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang-tidy cmake ninja-build + + - name: Configure CMake and export compile commands + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DENABLE_HASWELL=ON \ + -DBUILD_TOOLS=OFF + + - name: Collect changed C/C++ files + id: changed_files + uses: tj-actions/changed-files@v46 + with: + files: | + **/*.c + **/*.cc + **/*.cpp + **/*.cxx + !thirdparty/** + !build/** + separator: "\n" + + - name: Filter changed files to compile database entries + id: tidy_files + if: steps.changed_files.outputs.any_changed == 'true' + run: | + python3 - <<'PY' + import json + import os + from pathlib import Path + + changed = [line.strip() for line in """${{ steps.changed_files.outputs.all_changed_files }}""".splitlines() if line.strip()] + compile_db_path = Path("build/compile_commands.json") + with compile_db_path.open("r", encoding="utf-8") as f: + compile_db = json.load(f) + + compile_entries = set() + for entry in compile_db: + file_path = entry.get("file") + if not file_path: + continue + normalized = os.path.normpath(file_path).replace("\\", "/") + compile_entries.add(normalized) + + selected = [] + skipped = [] + cwd = Path.cwd() + for file_path in changed: + if not Path(file_path).is_file(): + skipped.append(f"{file_path} (missing)") + continue + + abs_normalized = os.path.normpath(str((cwd / file_path).resolve())).replace("\\", "/") + rel_normalized = os.path.normpath(file_path).replace("\\", "/") + + if abs_normalized in compile_entries or rel_normalized in compile_entries: + selected.append(file_path) + else: + skipped.append(f"{file_path} (no compile_commands entry)") + + output_path = os.environ["GITHUB_OUTPUT"] + with open(output_path, "a", encoding="utf-8") as out: + out.write(f"any_tidy_files={'true' if selected else 'false'}\n") + out.write("all_tidy_files<