Сопоставитель AST на определенном узле

Я написал сопоставитель AST для поиска утверждений определенного типа. В совпадающих узлах я вычислил соседние братья и сестры этого узла. Теперь мне нужно запустить сопоставитель на соседних узлах, чтобы убедиться, что они удовлетворяют моему условию или нет. Сопоставитель clang AST сопоставляет все узлы дерева один за другим. Я хочу запустить сопоставитель для определенного узла и вернуть true, если узел соответствует моему требуемому условию. Это возможно?


person Sanjay Malakar    schedule 27.02.2020    source источник


Ответы (1)


Я предлагаю реализовать свой собственный сопоставитель, который будет инкапсулировать логику поиска соседних узлов и сопоставления их с другими сопоставителями.

Я собрал следующий сопоставитель в качестве примера того, как это можно сделать:

using clang::ast_matchers::internal::Matcher;
constexpr auto AVERAGE_NUMBER_OF_NESTED_MATCHERS = 3;
using Matchers =
    llvm::SmallVector<Matcher<clang::Stmt>, AVERAGE_NUMBER_OF_NESTED_MATCHERS>;


clang::Stmt *getNeighbor(const clang::Stmt &Node, clang::ASTContext &Context) {
  // It is my naive implementation of this method, you can easily put your own
  auto Parents = Context.getParents(Node);
  if (Parents.size() != 1) {
    return nullptr;
  }

  // As we deal with statements, let's assume that neighbor - is the next
  // statement in the enclosing compound statement.
  if (auto *Parent = Parents[0].get<clang::CompoundStmt>()) {
    auto Neighbor = std::adjacent_find(
        Parent->body_begin(), Parent->body_end(),
        [&Node](const auto *Top, const auto *Bottom) { return Top == &Node; });

    if (Neighbor != Parent->body_end()) {
      return *std::next(Neighbor);
    }
  }

  return nullptr;
}

AST_MATCHER_P(clang::Stmt, neighbors, Matchers, NestedMatchers) {
  // Node is the current tested node
  const clang::Stmt *CurrentNode = &Node;

  // Our goal is to iterate over the given matchers and match the current node
  // with the first matcher.
  //
  // Further on, we plan on checking whether the next
  // matcher matches the neighbor/sibling of the previous node.
  for (auto NestedMatcher : NestedMatchers) {
    // This is how one can call a matcher to test one node.
    //
    // NOTE: it uses Finder and Builder, so it's better to do it from
    //       inside of a matcher and get those for free
    if (CurrentNode == nullptr or
        not NestedMatcher.matches(*CurrentNode, Finder, Builder)) {
      return false;
    }

    // Here you can put your own implementation of finding neighbor/sibling
    CurrentNode = getNeighbor(*CurrentNode, Finder->getASTContext());
  }

  return true;
}

Я надеюсь, что комментарии внутри фрагмента охватывают основные идеи, лежащие в основе этого сопоставителя.

Демо

Сопоставитель:

neighbors({declStmt().bind("first"), forStmt().bind("second"),
           returnStmt().bind("third")})

Фрагмент кода:

int foo() {
  int x = 42;
  int y = 10;
  for (; x > y; --x) {
  }
  return x;
}

Выход:

first:
DeclStmt 0x4c683e0
`-VarDecl 0x4c68360  used y 'int' cinit
  `-IntegerLiteral 0x4c683c0 'int' 10

second:
ForStmt 0x4c684d0
|-<<<NULL>>>
|-<<<NULL>>>
|-BinaryOperator 0x4c68468 '_Bool' '>'
| |-ImplicitCastExpr 0x4c68438 'int' <LValueToRValue>
| | `-DeclRefExpr 0x4c683f8 'int' lvalue Var 0x4c682b0 'x' 'int'
| `-ImplicitCastExpr 0x4c68450 'int' <LValueToRValue>
|   `-DeclRefExpr 0x4c68418 'int' lvalue Var 0x4c68360 'y' 'int'
|-UnaryOperator 0x4c684a8 'int' lvalue prefix '--'
| `-DeclRefExpr 0x4c68488 'int' lvalue Var 0x4c682b0 'x' 'int'
`-CompoundStmt 0x4c684c0

third:
ReturnStmt 0x4c68540
`-ImplicitCastExpr 0x4c68528 'int' <LValueToRValue>
  `-DeclRefExpr 0x4c68508 'int' lvalue Var 0x4c682b0 'x' 'int'

Я надеюсь, что это отвечает на ваш вопрос!

person Valeriy Savchenko    schedule 28.02.2020
comment
Как вы получили этот результат? Или, в более общем плане, как вы интегрировали свой сопоставитель в LLVM? - person Richard; 23.01.2021