Обход всего CXXMemberCallExpr в NamespaceDecl - PullRequest
0 голосов
/ 25 октября 2018

Идея состоит в том, чтобы обойти все экземпляры CXXMemberCallExpr внутри NamespaceDecl.

У меня есть RecursiveASTVisitor, который вызывается из ASTConsumer.

У RecursiveASTVisitor есть перегруженный экземпляр VisitNamespaceDecl, в котором я вызываю TraverseDecl в каждом объявлении, которое я получаю, используя другой RecursiveASTVisitor, у которого перегруженный экземпляр VisitCXXMethodDecl.

Вызов clang -Xclang -ast-dump myclass.cc показывает правильную иерархию, поэтому я знаю, что она доступна.К сожалению, я думаю, что TraverseDecl не проходит через CompoundStmt или CallExpr:

`-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 *'

Есть мысли?

1 Ответ

0 голосов
/ 15 ноября 2018

Более простой способ решить эту проблему - использовать AST Matchers.Они позволяют формировать сложные предикаты, относящиеся к узлам AST, например, «CXXMemberCallExpr в определенном пространстве имен».Это позволяет избежать рекурсивного RecursiveASTVisitors;MatchFinder будет обрабатывать все это для вас.

В этом подходе вы определяете два элемента: средство сопоставления и обратный вызов, который действует, когда найдено совпадение.Сопоставитель может выглядеть так:

// 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();это дает доступ к узлу AST CXXMemberCallExpr в обратном вызове.Также обратите внимание на совпадение 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_).Оттуда у нас есть вся информация, касающаяся звонка.

Я добавил полный рабочий пример в файл apps / ListCXXMemberCalls.cc в этом демонстрационном проекте «Анализ кода и рефакторинг с помощью Clang Tools».(https://github.com/lanl/coarct) Включает в себя весь шаблон создания MatchFinder, регистрации средства сопоставления и обратного вызова и запуска его через инструмент Clang.

...