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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:57:34