C之(16)scan-build與clang-tidy使用
Author: Once Day Date: 2025年3月29日
一位熱衷于Linux學習和開發的菜鳥,試圖譜寫一場冒險之旅,也許終點只是一場白日夢…
漫漫長路,有人對你微笑過嘛…
全系列文章可參考專欄: Linux實踐記錄_Once_day的博客-CSDN博客
參考文章:
- Clang C Language Family Frontend for LLVM
- 2.2. Command Line Usage: scan-build and CodeChecker — Clang 21.0.0git documentation
- Clang Static Analyzer - 靜態代碼分析工具 - 煊奕 - 博客園
- 2.2. 命令行使用:scan-build 和 CodeChecker — Clang 20.0.0git 文檔 - Clang 編譯器
- Clang-Tidy — Extra Clang Tools 21.0.0git documentation
- c++靜態代碼掃描工具clang-tidy詳細介紹-CSDN博客
- Clang-Tidy — Extra Clang Tools 20.0.0git 文檔 - Clang 編譯器
文章目錄
- C之(16)scan-build與clang-tidy使用
- 1. Clang項目介紹
- 1.1 Clang概述
- 1.2 LLVM與Clang
- 1.3 常用工具
- 2. Scan-build介紹
- 2.1 基礎使用
- 2.2 常見參數
- 2.3 Gitlab流水線實踐
- 3. Clang-tidy介紹
- 3.1 基礎使用
- 3.2 常見參數
- 3.3 Vscode集成示例
1. Clang項目介紹
1.1 Clang概述
Clang是一個由Apple主導開發的開源編譯器前端項目,旨在提供一個現代化、高效、易于使用和可擴展的編譯器工具鏈。Clang項目的主要目標是為C、C++、Objective-C等編程語言提供更好的編譯支持,同時與底層的LLVM編譯器基礎設施緊密集成。
Clang采用模塊化的架構設計,將編譯過程分為詞法分析、語法分析、語義分析、代碼生成等多個階段,每個階段都有相應的庫和工具來處理。這種模塊化的設計使得Clang易于理解、維護和擴展,開發者可以方便地為Clang添加新的功能或優化現有功能。
Clang還注重提供友好的用戶體驗,它的錯誤診斷信息比傳統編譯器更加清晰、準確,幫助開發者快速定位和解決代碼中的問題。此外,Clang還提供了一些額外的工具,如代碼格式化、靜態分析等,進一步提高了開發效率。
1.2 LLVM與Clang
LLVM和Clang是密切相關但又有所區別的項目,它們共同構成了一個完整的編譯器工具鏈。
Clang是LLVM項目的一個子項目,主要負責編譯器前端的工作,如詞法分析、語法分析、語義分析等,將源代碼轉換為LLVM中間表示(IR)。
LLVM則主要負責編譯器后端的工作,對Clang生成的LLVM IR進行優化、轉換并生成目標平臺的機器碼。
Clang和LLVM的設計遵循模塊化和可重用性的原則,它們之間通過標準的LLVM IR進行交互和銜接,形成了一個靈活、可擴展的編譯器架構。
兩者之間的區別:
- 功能定位:Clang主要是編譯器前端,負責處理源代碼并生成LLVM IR;LLVM則是編譯器后端,負責對LLVM IR進行優化和目標代碼生成。
- 適用語言:Clang主要支持C、C++、Objective-C等編程語言;LLVM則是語言無關的,可以支持多種編程語言。
- 工具集:Clang提供了一些額外的工具,如代碼格式化、靜態分析等;LLVM則提供了各種優化passes、JIT編譯器、調試器等工具。
- 項目起源:Clang項目是由Apple主導開發的,旨在替代GCC;LLVM項目則起源于伊利諾伊大學厄巴納-香檳分校,最初是一個研究項目。
1.3 常用工具
Clang項目除了提供核心的編譯器功能外,還包含了一些非常實用的輔助工具,如Scan-build、Codechecker和Clang-tidy,這些工具可以幫助開發者提高代碼質量、發現潛在缺陷并遵循最佳編程實踐。
Scan-build是一個靜態分析工具,它利用Clang的靜態分析器對代碼進行深入的分析,以發現潛在的缺陷和漏洞,如空指針解引用、內存泄漏、死鎖等。Scan-build可以與現有的構建系統(如Make、CMake)無縫集成,并生成易于理解的HTML報告,幫助開發者快速定位和修復代碼中的問題。Scan-build的分析過程是全面的,可以跨越不同的函數和文件,提供了比傳統編譯器警告更深入、更準確的缺陷檢測能力。
Clang-tidy是一個基于Clang的代碼檢查和重構工具,它可以幫助開發者發現和修復代碼中的風格問題、潛在缺陷以及現代C++的最佳實踐偏離。Clang-tidy提供了一系列的檢查規則(如命名規范、異常安全、性能優化等),開發者可以根據項目需求選擇和配置這些規則。除了發現問題,Clang-tidy還可以自動應用修復,幫助開發者快速重構代碼。與Scan-build不同,Clang-tidy主要關注代碼風格和最佳實踐,而不是深入的靜態分析。
2. Scan-build介紹
2.1 基礎使用
在ubuntu上安裝Scan-build非常簡單,如下所示:
sudo apt-get install clang
sudo apt-get install llvm
sudo apt-get install clang-tools #包含工具集
scan-build 通過攔截編譯命令來使用獲取編譯命令行,當在構建命令前加上 scan-build 時,scan-build 會將編譯命令中的編譯器(如 gcc、clang)替換為一個叫 ccc-analyzer
的 wrapper 程序。
ccc-analyzer
會先調用正常的編譯器編譯源文件,然后再調用 Clang 靜態分析器分析源文件。靜態分析器會模擬代碼的執行過程,嘗試找出各種潛在的 bug,如空指針解引用、內存泄露、死鎖等。
分析器發現的問題會記錄下來,生成一份 HTML 格式的報告。報告中列出了所有發現的問題,并提供了問題的代碼位置、執行路徑等詳細信息。用戶可以用瀏覽器查看這些 HTML 報告,了解代碼中存在的各種潛在問題。
整個過程如下所示:
項目源文件 ==> [scan-build] ==> [ccc-analyzer] ==> [編譯器]+[靜態分析器] ==> [HTML報告] ==> 輸出目錄
可見,scan-build 本質上是通過"偷梁換柱"替換編譯器,從而將靜態分析器集成到項目的構建過程中,使代碼分析變得透明和自動化。分析過程完全是靜態的,不需要實際運行程序,因此比動態分析更快更輕量級。這種方式使得在大型項目中使用靜態分析檢查代碼問題變得非常方便。
在構建命令前加上"scan-build"前綴即可對項目進行靜態分析。支持make、xcodebuild等常見構建命令,例如:
$ scan-build [scan-build options] <command> [command options]
$ scan?build make
scan-build會將構建命令后面的選項都傳遞給實際的構建命令,因此可以使用構建系統支持的各種選項,如指定并行構建的-j
選項:
$ scan-build make -j4
scan-build的輸出是一組HTML文件,代表分析過程發現的各種問題。默認存放在/tmp
目錄下的一個子目錄中。可以用-o
選項指定輸出目錄:
$ scan-build -o ./analyze_results make
分析結果中包含一個index.html文件,在瀏覽器中打開它即可查看各種問題的報告。如果在scan-build時使用了-V
選項,構建結束后會自動打開該頁面。
下面用一個簡單的C源文件來演示操作:
#include <stdio.h>int main()
{int i;printf("Hello, World!\n");return i; // 返回未初始化的值
}
使用scan-build攔截編譯命令并進行靜態分析,通常情況下默認參數即可:
onceday->output:$ scan-build gcc -c test.c
scan-build: Using '/usr/lib/llvm-18/bin/clang' for static analysis
test.c:7:5: warning: Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]7 | return i;| ^~~~~~~~
1 warning generated.
scan-build: Analysis run complete.
scan-build: 1 bug found.
scan-build: Run 'scan-view /tmp/scan-build-2025-03-30-165354-2811989-1' to examine bug reports.
然后按照提示使用scan-view
查看結果,需要額外補充--host 10.52.25.98 --allow-all-hosts
參數,也就Web服務器監聽的地址和允許訪問的地址,根據實際情況填寫即可。
onceday->output:$ scan-view --host 10.52.25.98 --allow-all-hosts /tmp/scan-build-2025-03-30-165354-2811989-1
Starting scan-view at: http://10.52.25.98:8181Use Ctrl-C to exit.
在瀏覽器訪問對應網址,可以看到如下信息:
點擊View Report可以查看詳細的錯誤信息:
2.2 常見參數
參數名稱 | 描述 |
---|---|
-o | 指定分析結果的輸出目錄。如果未指定,默認在 /tmp (Mac OS X 上為 TMPDIR)中創建一個目錄存儲報告 |
-h, --help | 顯示幫助信息 |
-k, --keep-going | 在指定的構建命令中添加 “keep on going” 選項。目前支持 make 和 xcodebuild |
-v | 啟用 scan-build 的詳細輸出。使用第二個和第三個 -v 會進一步增加詳細程度 |
-V, --view | 在構建完成時,在 Web 瀏覽器中查看分析結果 |
--use-cc | 指定要用于編譯的編譯器路徑。scan-build 會攔截編譯命令,但它可能無法猜測項目使用的具體編譯器 |
--use-c++ | 與 --use-cc 類似,但用于指定 C++ 編譯器路徑 |
--exclude | 指定靜態分析器不分析此目錄中的文件。適用于項目包含第三方庫的情況 |
-enable-checker | 啟用指定的檢查器(Checker) |
-disable-checker | 禁用指定的檢查器(Checker) |
-load-plugin | 使用 Clang 插件接口加載外部檢查器 |
除此之外,scan-build 還支持所有傳給構建命令的選項,如 make -j4
指定并行構建的線程數。控制分析過程輸出格式的選項有 -plist
、-plist-html
、-sarif
等,可輸出 HTML、plist 或 SARIF 格式的分析報告。
2.3 Gitlab流水線實踐
在Gitlab流水線里面,可以將scan-build的靜態分析自動化,并且根據檢查的結果反饋流水線的成功狀態。
編寫一個輔助腳本,如下:
#!/bin/bash# 開啟調試
# set -x# 當前目錄路徑
export SOURCE_DIR=${SOURCE_DIR:-$(pwd)}# 導入基本ANMK shell工具函數
source $SOURCE_DIR/scripts/utils.sh# 定義參數
CHECK_ARGS="--status-bugs"# 指定輸出目錄
OUTPUT_DIR="$SOURCE_DIR/clang-check"
rm -rf $OUTPUT_DIR
# 創建輸出目錄
mkdir -p $OUTPUT_DIRINFO "OUTPUT_DIR: [$OUTPUT_DIR]"# 運行檢查命令
scan-build -o $OUTPUT_DIR $CHECK_ARGS make rebuild
if [ $? -ne 0 ]; thenERROR "clang-check failed"exit 1
fi# 未檢測到問題
INFO "clang-check success"
exit 0
gitlab-ci.yaml流水線配置如下:
develop-scan-build:stage: local-buildtags:- anmk-buildneeds: []script:- echo "Check Netfpc Project - Develop Version - Scan Build"- ./clang-check.sh- echo "Check finished"
流水線運行效果如下:
3. Clang-tidy介紹
3.1 基礎使用
ubuntu 24.04按照clang-tidy很簡單:
sudo apt install clang-tidy
Clang-Tidy支持不同場景下的使用:
- 集成于編譯器:Clang-Tidy可以作為編譯器的一部分運行,在編譯過程中自動檢測代碼問題。這種方式使用簡單,適合持續集成和自動化檢查。
- 命令行工具:Clang-Tidy提供了靈活的命令行接口,可以對單個文件、目錄或整個項目進行分析。通過指定不同的檢查項和配置文件,可以自定義分析過程。
- 集成于IDE:許多流行的集成開發環境如Visual Studio、CLion等都內置了Clang-Tidy支持。開發者可以在編寫代碼時實時獲得Clang-Tidy的反饋,并快速定位和修復問題。
Clang-Tidy不僅能發現問題,還可以自動修復其中的一部分。通過-fix
選項,Clang-Tidy可以直接修改源代碼,減輕開發者的工作量。Clang-Tidy采用模塊化設計,支持自定義檢查規則。開發者可以根據項目需求,編寫自己的檢查模塊,擴展Clang-Tidy的功能。
當作為命令行工具使用時,Clang-Tidy提供了靈活的選項和參數,以滿足不同的分析需求。以下是一些常用的使用方式:
(1)分析單個文件:
clang-tidy file.cpp -- -std=c++11 -Iinclude/
這個命令會對file.cpp進行靜態分析,并使用C++11標準和include/目錄作為頭文件搜索路徑。
(2)分析目錄下的所有文件:
clang-tidy src/*.cpp -- -std=c++14 -Iinclude/
這個命令會分析src/目錄下的所有.cpp文件,使用C++14標準和include/目錄作為頭文件搜索路徑。
(3)指定檢查項:
clang-tidy file.cpp -checks="-*,modernize-*,readability-*"
通過-checks
選項,可以指定啟用或禁用特定的檢查項。上述命令啟用了modernize-
*和readability-*
兩組檢查,并禁用了其他所有檢查。
(4)使用配置文件:
clang-tidy file.cpp -config-file=.clang-tidy
通過-config-file
選項,可以指定一個配置文件,其中包含了自定義的檢查項和編譯器選項。這種方式適合于項目級別的統一配置管理。
(5)自動修復:
clang-tidy file.cpp -fix -format-style=file
加上-fix
選項后,Clang-Tidy會自動修復能夠確定的問題,并將修改后的代碼寫回源文件。-format-style
選項可以指定代碼格式化風格。
(6)輸出到文件:
clang-tidy file.cpp -export-fixes=report.yaml
-export-fixes
選項可以將分析結果導出到一個文件中,方便后續處理和集成。
下面是命令行操作的一個簡單演示:
onceday->output:$ clang-tidy test.c -- -std=c11
1 warning generated.
/home/onceday/ease/libnetfpc/output/test.c:7:5: warning: Undefined or garbage value returned to caller [clang-analyzer-core.uninitialized.UndefReturn]7 | return i;| ^ ~
/home/onceday/ease/libnetfpc/output/test.c:5:5: note: 'i' declared without an initial value5 | int i;| ^~~~~
/home/onceday/ease/libnetfpc/output/test.c:7:5: note: Undefined or garbage value returned to caller7 | return i;| ^ ~
3.2 常見參數
Clang-Tidy 提供了許多參數,用于控制分析過程和自定義行為。以下是一些常見的參數:
名稱 | 描述 |
---|---|
-checks= | 指定要啟用或禁用的檢查項。可以使用逗號分隔的列表,以"-"前綴表示禁用,無前綴表示啟用。 如 -checks=-*,modernize-* 表示禁用所有檢查,只啟用 modernize- 相關的檢查。 |
-config-file= | 指定一個配置文件,其中包含了自定義的檢查項和編譯器選項。 這樣可以在項目級別上統一配置,方便管理。 |
-header-filter= | 指定一個正則表達式,用于過濾要分析的頭文件。 只有匹配該表達式的頭文件才會被分析。 |
-line-filter= | 指定一個正則表達式,用于過濾要分析的代碼行。 只有匹配該表達式的代碼行才會被分析。 |
-fix | 自動修復代碼中的問題。 Clang-Tidy 會嘗試自動修復能夠安全修復的問題,并將修改后的代碼寫回源文件。 |
-format-style= | 指定代碼格式化風格。 可以是預定義的風格名稱(如 LLVM、Google),也可以是一個配置文件的路徑。 |
-export-fixes= | 將分析結果導出到一個文件中,以便后續處理或集成到其他工具中。 導出的文件格式可以是 YAML 或 JSON。 |
-p= | 指定編譯數據庫的路徑。 編譯數據庫包含了編譯器選項和頭文件搜索路徑等信息,Clang-Tidy 需要這些信息來正確分析代碼。 |
-extra-arg= | 向 Clang-Tidy 傳遞額外的編譯器參數。 這對于一些特殊的編譯選項或宏定義很有用。 |
-quiet | 抑制所有診斷信息的輸出。 這在將 Clang-Tidy 集成到自動化流程中時很有用,以避免產生過多的噪音信息。 |
3.3 Vscode集成示例
在C/C++插件配置里面使能clang-tidy,并且關閉格式化功能,如下所示:
重點是使能C_CPP > Code Analysis > Clang Tidy: Enabled
。clang-tidy的配置不需要在這里填寫,而是使用.clang-tidy
配置文件,這樣每個項目都具有各自獨立的配置。
Clang Tidy: Use Build Path
用于指定是否使用編譯數據庫(Compilation Database,一個 JSON 格式的文件),通常命名為 compile_commands.json
,其中包含了項目中每個源文件的編譯命令和相關的編譯選項。
Clang 工具鏈(如 Clang-Tidy、Clang-Format 等)可以利用編譯數據庫來獲取正確的編譯選項,如頭文件搜索路徑、宏定義等。這樣,Clang 工具就能夠準確地分析和處理源代碼,而無需手動指定復雜的編譯選項。
如果使用 CMake 構建項目,可以在 CMake 命令中添加 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
選項,這會讓 CMake 在構建目錄下生成 compile_commands.json
文件。
如果沒有顯式指定 -p
選項,Clang 工具會嘗試在輸入文件的所有父目錄中搜索 compile_commands.json
文件。這個行為稱為"編譯數據庫的自動發現",可以簡化工具的使用。
如果項目沒有使用 CMake,可以使用其他工具(如 Bear)來生成編譯數據庫。一旦生成了 compile_commands.json
,就可以將其路徑傳遞給 -p
選項。
在VScode代碼編輯界面,就可以很明顯看到錯誤提示信息了(黃色波浪線):
Clang-tidy與VScode等IDE集成非常方便,由于Clang-tidy的報錯很多與語法和編程風格有關,因此在編寫代碼階段,實時反饋幫助很大。當然,是否采用建議要由對應的編碼工程師來判斷,畢竟容易出現誤報或者"個人風格"強相關的問題。