From d5350c35a82ac72f944b47abeb4c9b9a8a21d7a8 Mon Sep 17 00:00:00 2001 From: MH Hung Date: Tue, 9 Sep 2025 11:11:53 +0800 Subject: [PATCH] feat(clang): add claude suggestion --- .clang-format | 16 +++++++ .clang-tidy | 26 +++++++++++ CMakeLists.txt | 39 ++++++++++++++++ README.md | 75 +++++++++++++++++++++++++++++- azure-pipelines.yml | 106 ++++++++++++++++++++++++++++++++++++++++++ scripts/run-linter.sh | 29 ++++++++++++ src/main.cpp | 33 +++++++++++++ src/utils.cpp | 22 +++++++++ src/utils.h | 15 ++++++ 9 files changed, 360 insertions(+), 1 deletion(-) mode change 100644 => 100755 scripts/run-linter.sh diff --git a/.clang-format b/.clang-format index e69de29..68fb1b3 100644 --- a/.clang-format +++ b/.clang-format @@ -0,0 +1,16 @@ +# 基於 Google 風格但較寬鬆,適合舊代碼 +BasedOnStyle: Google +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 100 +AccessModifierOffset: -2 +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BreakBeforeBraces: Attach +IndentCaseLabels: true +SpacesBeforeTrailingComments: 2 +Standard: Cpp03 # 針對舊代碼 \ No newline at end of file diff --git a/.clang-tidy b/.clang-tidy index e69de29..cff2d44 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -0,0 +1,26 @@ +# 階段一:只檢查最關鍵的錯誤,適合舊代碼 +Checks: '-*, + clang-analyzer-core.NullDereference, + clang-analyzer-core.DivideZero, + clang-analyzer-core.UndefinedBinaryOperatorResult, + bugprone-argument-comment, + bugprone-bool-pointer-implicit-conversion, + bugprone-dangling-handle, + bugprone-use-after-move, + readability-braces-around-statements, + readability-container-size-empty' + +# 不將警告視為錯誤(階段一) +WarningsAsErrors: '' + +# 包含所有標頭檔 +HeaderFilterRegex: '.*' + +# 分析設定 +AnalyzeTemporaryDtors: false +FormatStyle: none + +# 檢查參數 +CheckOptions: + - key: readability-braces-around-statements.ShortStatementLines + value: '2' \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29..80a6451 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.10) +project(CppLinterTemplate) + +# 設定 C++ 標準(針對舊代碼) +set(CMAKE_CXX_STANDARD 98) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 編譯選項 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + +# 尋找 clang-tidy +find_program(CLANG_TIDY_EXE NAMES "clang-tidy") +if(CLANG_TIDY_EXE) + message(STATUS "Found clang-tidy: ${CLANG_TIDY_EXE}") + option(ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) +endif() + +# 原始碼 +add_executable(main + src/main.cpp + src/utils.cpp +) + +target_include_directories(main PRIVATE src) + +# 如果啟用 clang-tidy +if(CLANG_TIDY_EXE AND ENABLE_CLANG_TIDY) + set_target_properties(main PROPERTIES + CXX_CLANG_TIDY "${CLANG_TIDY_EXE};--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy" + ) +endif() + +# 測試執行檔 +add_executable(tests + tests/test_main.cpp + src/utils.cpp +) + +target_include_directories(tests PRIVATE src) \ No newline at end of file diff --git a/README.md b/README.md index 281dbe0..43b66ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,77 @@ -# cpp-linter-template +# C++ Linter Template + +這是一個展示如何在 C++ 專案中整合 linter 工具的模板專案。 + +## 目的 +- 展示 clang-tidy 在舊 C++ 代碼中的應用 +- 提供漸進式導入 linter 的範例 +- Azure DevOps CI/CD 整合範例 + +## 快速開始 + +### 1. 本地開發環境設定 +```bash +# 安裝相依套件 (Ubuntu/Debian) +sudo apt install clang-tidy clang-format cmake build-essential + +# 安裝相依套件 (macOS) +brew install llvm cmake + +# 克隆專案 +git clone +cd cpp-linter-template +``` + +### 2. 執行 Linter +```bash +# 使用提供的腳本 +./scripts/run-linter.sh + +# 或手動執行 +clang-tidy src/*.cpp src/*.h tests/*.cpp --config-file=.clang-tidy +``` + +### 3. 建構專案 +```bash +mkdir build && cd build +cmake .. +make +./main +``` + +## Linter 配置說明 +### 當前採用階段一設定: +- 只檢查最關鍵的錯誤 +- 不會因 linter 警告而中斷 CI +- 適合舊代碼逐步改進 + +### 後續階段計劃 +- 階段二:增加記憶體和資源管理檢查 +- 階段三:加入可讀性和現代化建議 +- 階段四:強制執行,警告視為錯誤 + +## 在 Azure DevOps 中使用 +1. 將此專案推送到 Azure DevOps +2. 設定 Pipeline 使用 azure-pipelines.yml +3. 建立 Pull Request 時會自動執行 linter + +## 自定義配置 +編輯 .clang-tidy 檔案來調整檢查規則: +```yaml +# 增加更多檢查 +Checks: '-*,clang-analyzer-*,bugprone-*,readability-*' +``` + +## 檔案說明 +`.clang-tidy:` Linter 配置檔 +`.clang-format`: 程式碼格式化配置 +`azure-pipelines.yml`: Azure DevOps CI/CD 配置 +`scripts/run-linter.sh`: 本地執行 linter 腳本 + +## 注意事項 +- 此模板使用 C++98 標準,適合舊代碼 +- Linter 設定較為寬鬆,適合逐步導入 +- 可以根據團隊需求調整檢查規則 ## 專案結構 cpp-linter-template/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e69de29..bdefb1b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -0,0 +1,106 @@ +# Azure DevOps Pipeline for C++ Linter Template +trigger: + branches: + include: + - main + - develop + +pr: + branches: + include: + - main + - develop + +pool: + vmImage: 'ubuntu-latest' + +variables: + buildConfiguration: 'Debug' + +stages: +- stage: LintAndBuild + displayName: 'Lint and Build' + jobs: + - job: StaticAnalysis + displayName: 'Static Analysis with clang-tidy' + steps: + - task: Bash@3 + displayName: 'Install Dependencies' + inputs: + targetType: 'inline' + script: | + sudo apt-get update + sudo apt-get install -y clang-tidy clang-format cmake build-essential + clang-tidy --version + + - task: Bash@3 + displayName: 'Check Code Format' + inputs: + targetType: 'inline' + script: | + echo "Checking code format..." + find src tests -name "*.cpp" -o -name "*.h" | xargs clang-format --dry-run --Werror + + - task: Bash@3 + displayName: 'Run clang-tidy on all files' + inputs: + targetType: 'inline' + script: | + echo "Running clang-tidy..." + find src tests -name "*.cpp" -o -name "*.h" | xargs clang-tidy --config-file=.clang-tidy + + # 階段一:只顯示警告,不中斷 CI + echo "Linter completed. Review warnings above." + + - task: Bash@3 + displayName: 'Build Project' + inputs: + targetType: 'inline' + script: | + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE=${{ variables.buildConfiguration }} + make -j$(nproc) + + - task: Bash@3 + displayName: 'Run Tests' + inputs: + targetType: 'inline' + script: | + cd build + ./tests + ./main + + - job: PRAnalysis + displayName: 'PR Changed Files Analysis' + condition: eq(variables['Build.Reason'], 'PullRequest') + steps: + - task: Bash@3 + displayName: 'Install Dependencies' + inputs: + targetType: 'inline' + script: | + sudo apt-get update + sudo apt-get install -y clang-tidy + + - task: Bash@3 + displayName: 'Analyze Changed Files Only' + inputs: + targetType: 'inline' + script: | + echo "Analyzing only changed files in PR..." + + # 取得變更的檔案 + git fetch origin $(System.PullRequest.TargetBranch) + CHANGED_FILES=$(git diff origin/$(System.PullRequest.TargetBranch)...HEAD --name-only | grep -E '\.(cpp|cxx|cc|c\+\+|h|hpp|hxx)$' || true) + + if [ -z "$CHANGED_FILES" ]; then + echo "No C++ files changed in this PR" + exit 0 + fi + + echo "Changed C++ files:" + echo "$CHANGED_FILES" + + echo "Running clang-tidy on changed files..." + echo "$CHANGED_FILES" | xargs clang-tidy --config-file=.clang-tidy -- -std=c++98 \ No newline at end of file diff --git a/scripts/run-linter.sh b/scripts/run-linter.sh old mode 100644 new mode 100755 index e69de29..7094f1e --- a/scripts/run-linter.sh +++ b/scripts/run-linter.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# 本地執行 linter 的腳本 + +echo "Running clang-tidy on C++ files..." + +# 檢查 clang-tidy 是否存在 +if ! command -v clang-tidy &> /dev/null; then + echo "clang-tidy not found. Please install it first." + echo "Ubuntu/Debian: sudo apt install clang-tidy" + echo "macOS: brew install llvm" + exit 1 +fi + +# 尋找所有 C++ 檔案 +CPP_FILES=$(find src tests -name "*.cpp" -o -name "*.h" 2>/dev/null) + +if [ -z "$CPP_FILES" ]; then + echo "No C++ files found." + exit 0 +fi + +echo "Found files:" +echo "$CPP_FILES" +echo + +# 執行 clang-tidy +echo "$CPP_FILES" | xargs clang-tidy --config-file=.clang-tidy + +echo "Linter check completed." \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index e69de29..bc3fc08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -0,0 +1,33 @@ +// 這個檔案故意包含一些會被 linter 檢查的情況 +#include +#include +#include "utils.h" + +int main() { + std::cout << "C++ Linter Template Demo" << std::endl; + + // 測試案例 1: 正常的代碼 + std::vector numbers; + numbers.push_back(1); + numbers.push_back(2); + numbers.push_back(3); + + // 測試案例 2: 可能被 linter 警告的代碼 + int* ptr = new int(42); + if (ptr) { + std::cout << "Value: " << *ptr << std::endl; + delete ptr; + ptr = NULL; // 好習慣,避免 dangling pointer + } + + // 測試案例 3: 容器大小檢查 + if (!numbers.empty()) { // 比 numbers.size() > 0 好 + std::cout << "Numbers count: " << numbers.size() << std::endl; + } + + // 測試案例 4: 呼叫工具函數 + int result = calculateSum(numbers); + std::cout << "Sum: " << result << std::endl; + + return 0; +} \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp index e69de29..c51cb9b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -0,0 +1,22 @@ +#include "utils.h" +#include + +int calculateSum(const std::vector& vec) { + int sum = 0; + // 使用舊式的 iterator(模擬舊代碼) + for (std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { + sum += *it; + } + return sum; +} + +bool isPositive(int number) { + return number > 0; +} + +void safeCopy(char* dest, const char* src, int maxLen) { + if (dest && src && maxLen > 0) { + strncpy(dest, src, maxLen - 1); + dest[maxLen - 1] = '\0'; // 確保字串結尾 + } +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index e69de29..19a2f19 100644 --- a/src/utils.h +++ b/src/utils.h @@ -0,0 +1,15 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +// 計算向量元素總和 +int calculateSum(const std::vector& vec); + +// 檢查數字是否為正數 +bool isPositive(int number); + +// 安全的字串複製(展示舊式 C++ 代碼) +void safeCopy(char* dest, const char* src, int maxLen); + +#endif // UTILS_H \ No newline at end of file