AST-сопоставитель на указанном c узле - PullRequest
1 голос
/ 27 февраля 2020

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

1 Ответ

3 голосов
/ 28 февраля 2020

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

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

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'

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

...