You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用LLVM-15的clang-tidy强制使用MyClassSet替代特定集合类型

正确编写clang-tidy匹配器:用MyClassSet替代std::unordered_setmy::namespace1::Class

需求说明

需要通过LLVM-15的clang-tidy工具,强制在以下场景中用MyClassSet替代直接编写的std::unordered_set<my::namespace1::Class>:

  • 变量声明的类型
  • 函数的返回值类型
  • using/typedef定义的类型

其中MyClassSet的定义为:

using MyClassSet = std::unordered_set<my::namespace1::Class>;

原匹配器的问题

你编写的匹配器存在以下核心问题:

  1. 结构逻辑错误:varDecl直接嵌套cxxRecordDecl和classTemplateSpecializationDecl无效,变量声明的类型需要通过hasType()关联类型节点。
  2. 场景覆盖不全:仅匹配了变量声明,未覆盖函数返回值、using/typedef的场景。
  3. 模板参数匹配冗余:重复使用templateArgument(),正确写法是直接通过refersToType()关联目标类型。

正确匹配器写法

1. 定义目标类型匹配器

先单独定义匹配std::unordered_set<my::namespace1::Class>的模板特化类型:

const auto TargetUnorderedSetType = classTemplateSpecializationDecl(
    hasTemplateName(templateName(matchesName("std::unordered_set"))),
    hasTemplateArgument(
        0,
        templateArgument(refersToType(
            hasDeclaration(cxxRecordDecl(matchesName("my::namespace1::Class")))
        ))
    )
);

2. 覆盖所有目标场景并排除误匹配

分别匹配三类场景,同时通过条件排除已使用MyClassSet的代码,合并为总匹配器:

const auto matcher = anyOf(
    // 匹配变量声明
    varDecl(
        hasType(TargetUnorderedSetType),
        unless(hasType(typedefNameDecl(matchesName("MyClassSet"))))
    ).bind("uset_var"),

    // 匹配函数返回值
    functionDecl(
        returns(TargetUnorderedSetType),
        unless(returns(typedefNameDecl(matchesName("MyClassSet"))))
    ).bind("uset_func"),

    // 匹配using/typedef定义
    typedefDecl(
        hasType(TargetUnorderedSetType),
        unless(hasDeclaration(matchesName("MyClassSet")))
    ).bind("uset_typedef")
);

3. 关键细节说明

  • 避免误匹配:通过unless()条件过滤掉已经使用MyClassSet的代码,防止对正确写法的误修改。
  • 节点绑定:给不同场景的匹配节点绑定不同名称,方便后续在检查逻辑中针对变量、函数、typedef生成对应的修复提示或替换代码。

修复逻辑示例(参考)

在clang-tidy检查的check()方法中,可根据绑定节点类型生成修复:

void check(const MatchFinder::MatchResult &Result) {
    if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("uset_var")) {
        diag(Var->getLocation(), "use MyClassSet instead of std::unordered_set<my::namespace1::Class>")
            << FixItHint::CreateReplacement(Var->getTypeSourceInfo()->getTypeLoc().getSourceRange(), "MyClassSet");
    } else if (const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("uset_func")) {
        diag(Func->getLocation(), "use MyClassSet instead of std::unordered_set<my::namespace1::Class> as return type")
            << FixItHint::CreateReplacement(Func->getReturnTypeSourceRange(), "MyClassSet");
    } else if (const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("uset_typedef")) {
        diag(Typedef->getLocation(), "use MyClassSet instead of std::unordered_set<my::namespace1::Class> in typedef/using")
            << FixItHint::CreateReplacement(Typedef->getTypeSourceInfo()->getTypeLoc().getSourceRange(), "MyClassSet");
    }
}

内容的提问来源于stack exchange,提问作者Richard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:47:44