如何在Clang-Tidy中匹配含vector声明的循环?解决重载歧义
自定义Clang-Tidy检查:匹配循环体中声明的std::vector
问题背景
需要实现一个Clang-Tidy检查,找出所有在for/while/do-while循环体内部声明std::vector的代码。参考AST匹配器文档编写的匹配器在clang-query中可以正常工作,但移植到Clang-Tidy检查代码时,编译出现hasType()重载歧义错误。
示例代码
#include <vector> int main() { for (int i = 0; i < 10; i++) { std::vector<int> foo; } return 0; }
对应AST结构
`-FunctionDecl 0x55c1d33c8bc8 <main.cpp:3:1, line:9:1> line:3:5 main 'int ()' `-CompoundStmt 0x55c1d3402a48 <col:12, line:9:1> |-ForStmt 0x55c1d34029e0 <line:4:3, line:6:3> | |-DeclStmt 0x55c1d33c8d40 <line:4:8, col:17> | | `-VarDecl 0x55c1d33c8cb8 <col:8, col:16> col:12 used i 'int' cinit | | `-IntegerLiteral 0x55c1d33c8d20 <col:16> 'int' 0 | |-<<<NULL>>> | |-BinaryOperator 0x55c1d33c8db0 <col:19, col:23> 'bool' '<' | | |-ImplicitCastExpr 0x55c1d33c8d98 <col:19> 'int' <LValueToRValue> | | | `-DeclRefExpr 0x55c1d33c8d58 <col:19> 'int' lvalue Var 0x55c1d33c8cb8 'i' 'int' | | `-IntegerLiteral 0x55c1d33c8d78 <col:23> 'int' 10 | |-UnaryOperator 0x55c1d33c8df0 <col:27, col:28> 'int' postfix '++' | | `-DeclRefExpr 0x55c1d33c8dd0 <col:27> 'int' lvalue Var 0x55c1d33c8cb8 'i' 'int' | `-CompoundStmt 0x55c1d34029c8 <col:32, line:6:3> | `-DeclStmt 0x55c1d34029b0 <line:5:5, col:25> | `-VarDecl 0x55c1d33cd388 <col:5, col:22> col:22 foo 'std::vector<int>' callinit destroyed | `-CXXConstructExpr 0x55c1d3402988 <col:22> 'std::vector<int>' 'void () noexcept' `-ReturnStmt 0x55c1d3402a38 <line:8:3, col:10> `-IntegerLiteral 0x55c1d3402a18 <col:10> 'int' 0
移植的Clang-Tidy代码(报错版本)
void LowPerfLoopCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( forStmt(hasDescendant(varDecl(hasType(hasDeclaration(cxxRecordDecl( matchesName("^::std::vector$"))))) .bind("vector-in-for"))), this); }
编译报错信息
llvm-project/clang-tools-extra/clang-tidy/misc/LowPerfLoopCheck.cpp: In member function ‘virtual void clang::tidy::misc::LowPerfLoopCheck::registerMatchers(clang::ast_matchers::MatchFinder*)’: /home/wentao/Desktop/llvm-project/clang-tools-extra/clang-tidy/misc/LowPerfLoopCheck.cpp:19:44: error: call of overloaded ‘hasType(clang::ast_matchers::internal::PolymorphicMatcher<clang::ast_matchers::internal::HasDeclarationMatcher, void(clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::CXXNewExpr, clang::DeclRefExpr, clang::EnumType, clang::ElaboratedType, clang::InjectedClassNameType, clang::LabelStmt, clang::AddrLabelExpr, clang::MemberExpr, clang::QualType, clang::RecordType, clang::TagType, clang::TemplateSpecializationType, clang::TemplateTypeParmType, clang::TypedefType, clang::UnresolvedUsingType, clang::ObjCIvarRefExpr>), clang::ast_matchers::internal::Matcher<clang::Decl> >)’ is ambiguous 19 | forStmt(hasDescendant(varDecl(hasType(hasDeclaration(cxxRecordDecl( | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 20 | matchesName("^::std::vector$"))))) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
问题原因与修复方案
歧义原因
hasType有两个重载版本:一个接受Matcher<QualType>,另一个接受Matcher<Type>。hasDeclaration返回的是多态匹配器,编译器无法自动推断应该转换为哪个重载的参数类型。而clang-query的执行环境中有额外的上下文隐式转换,所以不会触发歧义错误。
修复代码
通过recordType()明确指定要匹配的是RecordType节点,让hasType的参数类型明确为Matcher<Type>,消除重载歧义。同时扩展匹配逻辑,覆盖while和do-while循环:
void LowPerfLoopCheck::registerMatchers(MatchFinder *Finder) { // 定义匹配循环体中std::vector变量的匹配器 auto VectorVarInLoop = varDecl( hasType(recordType( hasDeclaration(cxxRecordDecl(matchesName("^::std::vector$"))) )) ).bind("vector-in-loop"); // 匹配所有三种循环语句,包含目标变量声明 Finder->addMatcher( stmt(anyOf( forStmt(hasDescendant(VectorVarInLoop)), whileStmt(hasDescendant(VectorVarInLoop)), doStmt(hasDescendant(VectorVarInLoop)) )), this); }
说明
recordType()限定了类型节点的类型,让编译器能确定使用hasType(Matcher<Type>)的重载,解决歧义问题。- 使用
anyOf()组合forStmt/whileStmt/doStmt,一次性覆盖所有循环类型,满足需求。
内容的提问来源于stack exchange,提问作者Rahn
相关产品推荐
相关产品推荐

