【问题标题】:Traversing all CXXMemberCallExpr in a NamespaceDecl遍历 NamespaceDecl 中的所有 CXXMemberCallExpr
【发布时间】:2019-03-30 07:18:34
【问题描述】:

想法是遍历CXXMemberCallExpr 内的所有NamespaceDecl 实例。

我有一个从ASTConsumer 调用的RecursiveASTVisitor

RecursiveASTVisitor 有一个 VisitNamespaceDecl 的重载实例,在我使用另一个 RecursiveASTVisitor 的每个声明时调用 TraverseDecl,该实例具有 VisitCXXMethodDecl 的重载实例。

调用clang -Xclang -ast-dump myclass.cc 显示了正确的层次结构,所以我知道它是可访问的。不幸的是,我认为TraverseDecl 没有经过CompoundStmtCallExpr

`-NamespaceDecl 0x555ce9e8b508 prev 0x555ce9e88d38 </class.cc:3:1, line:277:1> line:3:11 my_namespace
    |-original Namespace 0x555ce9e7c268 'my_namespace'
    |-CXXMethodDecl 0x555ce9e8b970 parent 0x555ce9e88da0 prev 0x555ce9e89480 <line:4:1, line:18:1> line:4:16 Init 'void (std::MyOtherClass *, std::my_namespace::paramstruct_t *, const std::object *, std::double, std::int, std::string, std::string, std::ob
f_namespace::MyClass *)'
    | |-ParmVarDecl 0x555ce9e8b5a0 <col:21, col:29> col:29 used env 'std::MyOtherClass *'
    | |-ParmVarDecl 0x555ce9e8b610 <col:34, col:49> col:49 used params 'std::my_namespace::paramstruct_t *'
    | |-ParmVarDecl 0x555ce9e8b680 <line:5:21, col:36> col:36 used j_dd 'const std::object *'
    | |-ParmVarDecl 0x555ce9e8b6f0 <col:47, col:55> col:55 used j_cc 'std::double':'double'
    | |-ParmVarDecl 0x555ce9e8b760 <col:65, col:70> col:70 used j_bb 'std::int':'int'
    | |-ParmVarDecl 0x555ce9e8b7d0 <line:6:21, col:29> col:29 used js_aa 'std::string':'std::_string *'
    | |-ParmVarDecl 0x555ce9e8b840 <col:46, col:54> col:54 used js_ee 'std::string':'std::_string *'
    | |-ParmVarDecl 0x555ce9e8b8b0 <line:7:21, col:32> col:32 used my_class 'std::my_namespace::MyClass *'
    | `-CompoundStmt 0x555ce9e8c3b8 <col:44, line:18:1>
    |   `-CXXMemberCallExpr 0x555ce9e8c350 <line:17:5, col:36> 'void'
    |     |-MemberExpr 0x555ce9e8c2c8 <col:5, col:17> '<bound member function type>' ->Init 0x555ce9e854b0
    |     | `-ImplicitCastExpr 0x555ce9e8c2b0 <col:5> 'std::my_namespace::MyClass *' <LValueToRValue>
    |     |   `-DeclRefExpr 0x555ce9e8c288 <col:5> 'std::my_namespace::MyClass *' lvalue ParmVar 0x555ce9e8b8b0 'my_class' 'std::my_namespace::MyClass *'

有什么想法吗?

【问题讨论】:

  • 实现 TraverseStmt() 来通过 CompoundStmt 怎么样?
  • 您对不使用 RecursiveASTVisitor 的方法感兴趣吗?

标签: c++ clang abstract-syntax-tree static-analysis


【解决方案1】:

解决此问题的更简单方法是使用 AST 匹配器。它们使您可以形成与 AST 节点相关的复杂谓词,例如“特定命名空间中的 CXXMemberCallExpr”。这避免了递归RecursiveASTVisitorsMatchFinder 将为您处理所有这些。

在这种方法中,您将定义两个元素:一个匹配器和一个在找到匹配项时执行的回调。匹配器可能如下所示:

// makes a matcher for C++ member call expressions in namespace <ns_name>
auto mk_call_expr_matcher(std::string const & ns_name)
{
  return namespaceDecl(hasName(ns_name),
                       forEachDescendant(cxxMemberCallExpr().bind("call")));
}

注意对bind()的调用;允许访问回调中的 CXXMemberCallExpr AST 节点。另外,请注意 forEachDescendant 匹配器:这是在命名空间中查找每个调用表达式的内容。

要创建一个回调,为每个匹配打印出一条消息,例如“方法 'foo' 由 'bar' 类型的对象调用”,可以执行以下操作:

struct CallPrinter : public MatchFinder::MatchCallback {
  void run(MatchFinder::MatchResult const & result) override
  {
    using namespace clang;
    SourceManager & sm(result.Context->getSourceManager());
    CXXMemberCallExpr const * call =
        result.Nodes.getNodeAs<CXXMemberCallExpr>(ns_name_);
    if(call) {
      auto const method_name(call->getMethodDecl()->getNameAsString());
      auto const callee_name(call->getRecordDecl()->getNameAsString());
      std::cout << "Method '" << method_name << "' invoked by object of type '"
                << callee_name << "\n";
    }
    else{ // error reporting... }
    return;
  }

  std::string ns_name_;
}; // struct CallPrinter

回调的参数result 链接到CXXMemberDecl 对象(注意ns_name_)。从那里我们可以掌握与通话相关的所有信息。

我在这个 Code Analysis and Refactoring with Clang Tools 演示项目的文件 apps/ListCXXMemberCalls.cc 中添加了一个完整的工作示例。 (https://github.com/lanl/coarct) 它包括实例化 MatchFinder、注册匹配器和回调以及通过 Clang 工具运行它的所有样板。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-29
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 2015-07-12
    • 1970-01-01
    • 2012-09-17
    • 2014-03-18
    相关资源
    最近更新 更多