【自定义规则检测】Clang-Tidy检查器

开源框架LLVM

  1. LLVM 官方文档:LLVM Documentation
  2. github源码:https://github.com/llvm/llvm-project

Demo搭建

创建自定义检查器目录结构

  1. Visual Studio Build Tools:
  • 安装步骤:
    • 运行安装程序,选择“使用C++的桌面开发”工作负载。
    • 确保选中以下组件:
      • MSVC 编译器(最新版本)
      • Windows 10 SDK 或更高版本
      • CMake 工具
      • 可选:Ninja(可以单独安装,见下文)
  1. CMake:
  • 下载地址:CMake 官方网站
  • 安装提示:
    • 在安装过程中,选择“将 CMake 添加到系统 PATH”选项,方便在命令行中使用 cmake
  1. 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脚本进行添加规则- 一些参考记录:

  1. Clang Static Analyzer (4) Clang-Tidy-鸿蒙开发者社区-51CTO.COM
  2. 23 - 自定义Clang
  3. 使用 Clang-Tidy 进行静态代码分析:完整的配置与 CMake 集成实例_clang-tidy cmake-CSDN博客
  4. 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 --

在这里插入图片描述在这里插入图片描述

规则开发

  1. 执行add脚本
    默认选择misc
llvm-project\clang-tools-extra\clang-tidy>python add_new_check.py misc NameRuleNameRule123
  1. 修改生成的文件,大小写如NameRuleNameRule324表示。
  2. 规则
静态变量加前缀 s_(表示 static)。
  例如:
  void Init()
  {
    static int s_initValue; // 静态变量}

结果则为上面的截图。

结论

  1. 只能使用gcc,MSVC编译的工程,如解析其他商业编译器的工程不能正常解析,则会报出一堆错误。
  2. 如解决以上问题,则则需要进行mock或者适配gcc代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R3us

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值