开源框架LLVM
- LLVM 官方文档:LLVM Documentation
- github源码:https://github.com/llvm/llvm-project
Demo搭建
创建自定义检查器目录结构
- Visual Studio Build Tools:
- 安装步骤:
- 运行安装程序,选择“使用C++的桌面开发”工作负载。
- 确保选中以下组件:
- MSVC 编译器(最新版本)
- Windows 10 SDK 或更高版本
- CMake 工具
- 可选:Ninja(可以单独安装,见下文)
- CMake:
- 下载地址:CMake 官方网站
- 安装提示:
- 在安装过程中,选择“将 CMake 添加到系统 PATH”选项,方便在命令行中使用 cmake
- Ninja(推荐使用高效的构建系统):
- 下载地址:Ninja Releases
- 安装方法:
- 下载 ninja.exe。
- 将 ninja.exe 放置在一个已添加到系统 PATH 的目录中(例如 C:\Program Files\CMake\bin)或将其路径手动添加到环境变量中。
创建构建目录
#在“x64 Native Tools Command Prompt for VS 2022”中,运行以下命令:
mkdir D:\prj_files\llvm\llvm-project\build
cd D:\prj_files\llvm\llvm-project\build
运行CMake配置
- -G “Visual Studio 17 2022”:指定使用Visual Studio 2022生成器。
- -DLLVM_ENABLE_PROJECTS=“clang;clang-tools-extra”:指定要构建的LLVM子项目。
- -DCMAKE_BUILD_TYPE=Release:指定构建类型为Release(在Visual Studio生成器下可选,可以忽略)。
- …\llvm:相对于build目录,指向llvm源码目录。
#在“x64 Native Tools Command Prompt for VS 2022”中,运行以下命令:
cmake -G "Visual Studio 17 2022" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release ..\llvm
- 成功信息:
-- Compiling and running to test HAVE_STEADY_CLOCK
-- Performing Test HAVE_STEADY_CLOCK -- success
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - no
--
Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - not found
-- Found Threads: TRUE
-- Compiling and running to test HAVE_PTHREAD_AFFINITY
-- Performing Test HAVE_PTHREAD_AFFINITY -- failed to compiile
-- Configuring done (312.9s)
-- Generating done (19.7s)
-- Build files have been written to: D:/prj_files/llvm/llvm-project/build
- 报错:
flang project is disabled
CMake Error at cmake/modules/CheckCompilerversion.cmake:37(message):
Host GCC versionmust be at least 7.4, your version is 6.3.0.
Call Stack (most recent call first):
cmake/modules/CheckCompilerVersion.cmake:47 (check_compiler_version)
cmake/config-ix.cmake:16(include)
CMakeLists.txt:973 (include)
Configuringincomplete,errors occurred!
确保MinGW的路径不干扰:
- 为避免CMake误用MinGW的GCC,请暂时从环境变量PATH中移除MinGW的路径,或者确保在“x64 本机工具命令提示符”中MinGW路径未被包含。
编译LLVM/Clang
#在“x64 Native Tools Command Prompt for VS 2022”中,运行以下命令:
ninja
- 报错1:
fatal error C1083: 无法打开包括文件: “atlbase.h”: No such file or directory
- 解决1:
#在“x64 Native Tools Command Prompt for VS 2022”中,运行以下命令:
echo %INCLUDE%
#预期输出:包含atlmfc\include,如不存在需要set
set INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\atlmfc\include;%INCLUDE%
- 报错2:LINK : fatal error LNK1104: 无法打开文件“atls.lib”
其实是问题1连带的问题。 - 解决2:
#在“x64 Native Tools Command Prompt for VS 2022”中,运行以下命令:
echo %LIB%
#预期输出:包含atlmfc\lib,如不存在需要set
set LIB=C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\atlmfc\lib\x64;%LIB%
创建自定义Clang-Tidy检查器
创建自定义检查器目录结构
cd D:\prj_files\llvm\llvm-project\clang-tools-extra\clang-tidy
mkdir custom_checks
cd custom_checks
在clang-tools-extra\clang-tidy\custom_checks\目录中,应该包含以下文件
custom_checks/
├── CMakeLists.txt
├── StaticVarPrefixCheck.cpp
├── StaticVarPrefixCheck.h
└── StaticVarPrefixCheck.td
编写以上文件 (StaticVarPrefixCheck.h、StaticVarPrefixCheck.cpp、StaticVarPrefixCheck.td)
// StaticVarPrefixCheck.h
#ifndef CLANG_TIDY_CUSTOM_CHECKS_STATICVARPREFIXCHECK_H
#define CLANG_TIDY_CUSTOM_CHECKS_STATICVARPREFIXCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace custom_checks {
class StaticVarPrefixCheck : public ClangTidyCheck {
public:
StaticVarPrefixCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Prefix(getOptions().get("custom_checks-static-var-prefix.Prefix", "s_")) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchResult &Result) override;
private:
std::string Prefix;
};
} // namespace custom_checks
} // namespace tidy
} // namespace clang
#endif // CLANG_TIDY_CUSTOM_CHECKS_STATICVARPREFIXCHECK_H
// StaticVarPrefixCheck.cpp
#include "StaticVarPrefixCheck.h"
#include "../ClangTidy.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Core/Replacement.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace custom_checks {
void StaticVarPrefixCheck::registerMatchers(MatchFinder *Finder) {
// 匹配所有具有`static`存储类的变量声明
Finder->addMatcher(varDecl(isStaticStorageClass()).bind("staticVar"), this);
}
void StaticVarPrefixCheck::check(const MatchFinder::MatchResult &Result) {
const auto *VD = Result.Nodes.getNodeAs<VarDecl>("staticVar");
if (!VD)
return;
std::string VarName = VD->getNameAsString();
if (VarName.find(Prefix) != 0) {
std::string FixedName = Prefix + VarName;
SourceManager &SM = *Result.SourceManager;
auto VarLoc = VD->getLocation();
auto Range = CharSourceRange::getTokenRange(VD->getLocation(),
VD->getLocation().getLocWithOffset(VarName.size()));
// 添加修复建议
auto FixIt = FixItHint::CreateReplacement(Range, FixedName);
diag(VD->getLocation(),
"静态变量 '%0' 应以前缀 '%1' 开头,建议使用 '%2'")
<< VarName << Prefix << FixedName
<< FixIt;
}
}
} // namespace custom_checks
} // namespace tidy
} // namespace clang
// StaticVarPrefixCheck.td
def StaticVarPrefixCheck : ClangTidyCheck {
let PassClassName = "clang::tidy::custom_checks::StaticVarPrefixCheck";
let Group = "CustomChecks";
let DisplayName = "Static Variable Prefix Check";
let ShortDescription = "Ensures static variables are prefixed with a specified prefix.";
let Documentation = [
{
Summary = "Checks that all static variables are prefixed with a specific string.";
Description = "This check ensures that static variables start with the specified prefix (default is 's_') to enhance code readability and maintainability.";
Link = "https://clang.llvm.org/extra/clang-tidy/custom_checks.html#static-variable-prefix-check";
}
];
let Options = [{
Key = "custom_checks-static-var-prefix.Prefix";
Value = "s_";
Desc = "Prefix to use for static variables.";
}];
}
确认CMakeLists.txt配置正确
在 custom_checks 目录中,创建 CMakeLists.txt 文件:
# clang-tools-extra/clang-tidy/custom_checks/CMakeLists.txt
# 引入Clang-Tidy的CMake脚本
include("${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy/cmake/ClangTidyUtils.cmake")
# 添加Clang-Tidy自定义检查器库
add_clang_tidy_library(
ClangTidyCustomChecks
StaticVarPrefixCheck.cpp
StaticVarPrefixCheck.h
LINK_LIBS
clangTidy
)
# 添加TableGen定义
set(LLVM_TARGET_DEFINITIONS ${CMAKE_CURRENT_SOURCE_DIR}/StaticVarPrefixCheck.td)
# 生成Clang-Tidy检查器的头文件和源文件
tablegen(ClangTidyChecks.h.inc -gen=ClangTidyCheck)
tablegen(ClangTidyChecks.cpp.inc -gen=ClangTidyCheckImpl)
# 包含TableGen生成的文件目录
include_directories(${CMAKE_CURRENT_BINARY_DIR})
- 在 clang-tools-extra/clang-tidy/CMakeLists.txt 中,应包含对子目录的添加
add_subdirectory(custom_checks)
重新配置并编译Clang-Tidy
执行:
#在“x64 Native Tools Command Prompt”中,重新运行CMake配置和编译命令:
#返回到构建目录:
cd D:\prj_files\llvm\llvm-project\build
#重新运行CMake配置命令:
cmake -G "Ninja" ^
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" ^
-DCMAKE_BUILD_TYPE=Release ^
-DLLVM_TARGETS_TO_BUILD="X86" ^
-DLLVM_USE_LINKER=lld ^
../llvm
结果:
--Configuring done<12.6s>
--Generating done<6.0s>
--Build files have been written to: D:/prj_fiiles/llvn/110m-proj/build
执行:
#编译Clang-Tidy及其自定义检查器:
ninja clang-tidy
#检查check是否已经被编译
clang-tidy.exe --list-checks
结果:
D:\prj_files\llvm\llvm-project\build>ninja clang-tidy
[136/136] Linking CXX executable bin\clang-tidy.exe
通过add_new_check.py脚本进行添加规则- 一些参考记录:
- Clang Static Analyzer (4) Clang-Tidy-鸿蒙开发者社区-51CTO.COM
- 23 - 自定义Clang
- 使用 Clang-Tidy 进行静态代码分析:完整的配置与 CMake 集成实例_clang-tidy cmake-CSDN博客
- clang-tidy入门简介_哔哩哔哩_bilibili
1. 在clang-tools-extra\clang-tidy\misc下自动创建MyFirstCheckCheck.cpp、MyFirstCheckCheck.h
python add_new_check.py misc MyFirstCheckCheck.cpp
2. clang-tools-extra\clang-tidy\misc\CMakeLists.txt自动添加
add_clang_library(clangTidyMiscModule STATIC
ConstCorrectnessCheck.cpp
CoroutineHostileRAIICheck.cpp
DefinitionsInHeadersCheck.cpp
ConfusableIdentifierCheck.cpp
Header IncludeCycleCheck.cpp
IncludeCleanerCheck.cpp
MiscTidyModule.cpp
MisleadingBidirectional.cpp
MisleadingIdentifier.cpp
MisplacedConstCheck.cpp
MyFirstCheckCheck.cpp //添加的代码cpp
NewDeleteOverloadsCheck.cpp
NoRecursionCheck.cpp
NonCopyableObjects.cpp
NonPrivateMemberVariablesInClassesCheck.cpp
RedundantExpressionCheck.cpp
StaticAssertCheck.cpp
ThrowByValueCatchByReferenceCheck.cpp
UnconventionalAssignOperatorCheck.cpp
UniqueptrResetReleaseCheck.cpp
UnusedAliasDeclsCheck.cpp
UnusedParametersCheck.cpp
UnusedUsingDeclsCheck.cpp
UseAnonymousNamespaceCheck.cpp
UseInternal inkageCheck.cpp
LINK LIBS
clangTidy
clangTidyUtils
3. clang-tools-extra\clang-tidy\misc\MiscTidyModule.cpp自动添加
#include "MyFirstCheckCheck.h"
CheckFactories.registerCheck<MyFirstCheckCheckcheck>(
"misc-my-first-check");
4. 重新配置并编译Clang-Tidy
#在“x64 Native Tools Command Prompt”中,重新运行CMake配置和编译命令:
#返回到构建目录:
cd D:\prj_files\llvm\llvm-project\build
#重新运行CMake配置命令:
cmake -G "Ninja" ^
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" ^
-DCMAKE_BUILD_TYPE=Release ^
-DLLVM_TARGETS_TO_BUILD="X86" ^
-DLLVM_USE_LINKER=lld ^
../llvm
#编译Clang-Tidy及其自定义检查器:
ninja clang-tidy
#检查check是否已经被编译
clang-tidy.exe --list-checks
结果:
D:\prj_files\llvm\llum-project\build>ninja cllang-tidy
[136/136] Linking CXX executable bin\clang-tidy.exe
测试自定义checks
准备测试代码
创建一个简单的C++文件,例如test.c,用于测试自定义检查器。
// test.c
static int initValue; // 不符合规范
static int s_correctValue; // 符合规范
运行Clang-Tidy检查
/path/to/clang-tidy.exe -checks=misc-my-first-check,-* -std=c99 test.c --
规则开发
- 执行add脚本
默认选择misc
llvm-project\clang-tools-extra\clang-tidy>python add_new_check.py misc NameRuleNameRule123
- 修改生成的文件,大小写如NameRuleNameRule324表示。
- 规则
静态变量加前缀 s_(表示 static)。
例如:
void Init(…)
{
static int s_initValue; // 静态变量
…
}
结果则为上面的截图。
结论
- 只能使用gcc,MSVC编译的工程,如解析其他商业编译器的工程不能正常解析,则会报出一堆错误。
- 如解决以上问题,则则需要进行mock或者适配gcc代码