如何用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>;
原匹配器的问题
你编写的匹配器存在以下核心问题:
- 结构逻辑错误:
varDecl直接嵌套cxxRecordDecl和classTemplateSpecializationDecl无效,变量声明的类型需要通过hasType()关联类型节点。 - 场景覆盖不全:仅匹配了变量声明,未覆盖函数返回值、
using/typedef的场景。 - 模板参数匹配冗余:重复使用
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
相关产品推荐
相关产品推荐

