Commit 7d382dcd authored by Marcel Hlopko's avatar Marcel Hlopko Committed by Dmitri Gribenko
Browse files

[Syntax] Build declarator nodes

Summary:
Copy of https://reviews.llvm.org/D72089 with Ilya's permission. See
https://reviews.llvm.org/D72089 for the first batch of comments.

Reviewers: gribozavr2

Reviewed By: gribozavr2

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D76220
parent 9e484220
Loading
Loading
Loading
Loading
+131 −5
Original line number Diff line number Diff line
@@ -38,10 +38,10 @@ enum class NodeKind : uint16_t {
  Leaf,
  TranslationUnit,

  // Expressions
  // Expressions.
  UnknownExpression,

  // Statements
  // Statements.
  UnknownStatement,
  DeclarationStatement,
  EmptyStatement,
@@ -58,7 +58,7 @@ enum class NodeKind : uint16_t {
  ExpressionStatement,
  CompoundStatement,

  // Declarations
  // Declarations.
  UnknownDeclaration,
  EmptyDeclaration,
  StaticAssertDeclaration,
@@ -68,7 +68,16 @@ enum class NodeKind : uint16_t {
  NamespaceAliasDefinition,
  UsingNamespaceDirective,
  UsingDeclaration,
  TypeAliasDeclaration
  TypeAliasDeclaration,

  // Declarators.
  SimpleDeclarator,
  ParenDeclarator,

  ArraySubscript,
  TrailingReturnType,
  ParametersAndQualifiers,
  MemberPointer
};
/// For debugging purposes.
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, NodeKind K);
@@ -101,11 +110,19 @@ enum class NodeRole : uint8_t {
  ExpressionStatement_expression,
  CompoundStatement_statement,
  StaticAssertDeclaration_condition,
  StaticAssertDeclaration_message
  StaticAssertDeclaration_message,
  SimpleDeclaration_declarator,
  ArraySubscript_sizeExpression,
  TrailingReturnType_arrow,
  TrailingReturnType_declarator,
  ParametersAndQualifiers_parameter,
  ParametersAndQualifiers_trailingReturn
};
/// For debugging purposes.
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, NodeRole R);

class SimpleDeclarator;

/// A root node for a translation unit. Parent is always null.
class TranslationUnit final : public Tree {
public:
@@ -375,6 +392,8 @@ public:
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::SimpleDeclaration;
  }
  /// FIXME: use custom iterator instead of 'vector'.
  std::vector<syntax::SimpleDeclarator *> declarators();
};

/// namespace <name> { <decls> }
@@ -424,6 +443,113 @@ public:
  }
};

/// Covers a name, an initializer and a part of the type outside declaration
/// specifiers. Examples are:
///     `*a` in `int *a`
///     `a[10]` in `int a[10]`
///     `*a = nullptr` in `int *a = nullptr`
/// Declarators can be unnamed too:
///     `**` in `new int**`
///     `* = nullptr` in `void foo(int* = nullptr)`
/// Most declarators you encounter are instances of SimpleDeclarator. They may
/// contain an inner declarator inside parentheses, we represent it as
/// ParenDeclarator. E.g.
///     `(*a)` in `int (*a) = 10`
class Declarator : public Tree {
public:
  Declarator(NodeKind K) : Tree(K) {}
  static bool classof(const Node *N) {
    return NodeKind::SimpleDeclarator <= N->kind() &&
           N->kind() <= NodeKind::ParenDeclarator;
  }
};

/// A top-level declarator without parentheses. See comment of Declarator for
/// more details.
class SimpleDeclarator final : public Declarator {
public:
  SimpleDeclarator() : Declarator(NodeKind::SimpleDeclarator) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::SimpleDeclarator;
  }
};

/// Declarator inside parentheses.
/// E.g. `(***a)` from `int (***a) = nullptr;`
/// See comment of Declarator for more details.
class ParenDeclarator final : public Declarator {
public:
  ParenDeclarator() : Declarator(NodeKind::ParenDeclarator) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::ParenDeclarator;
  }
  syntax::Leaf *lparen();
  syntax::Leaf *rparen();
};

/// Array size specified inside a declarator.
/// E.g:
///   `[10]` in `int a[10];`
///   `[static 10]` in `void f(int xs[static 10]);`
class ArraySubscript final : public Tree {
public:
  ArraySubscript() : Tree(NodeKind::ArraySubscript) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::ArraySubscript;
  }
  // TODO: add an accessor for the "static" keyword.
  syntax::Leaf *lbracket();
  syntax::Expression *sizeExpression();
  syntax::Leaf *rbracket();
};

/// Trailing return type after the parameter list, including the arrow token.
/// E.g. `-> int***`.
class TrailingReturnType final : public Tree {
public:
  TrailingReturnType() : Tree(NodeKind::TrailingReturnType) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::TrailingReturnType;
  }
  // TODO: add accessors for specifiers.
  syntax::Leaf *arrow();
  syntax::SimpleDeclarator *declarator();
};

/// Parameter list for a function type and a trailing return type, if the
/// function has one.
/// E.g.:
///  `(int a) volatile ` in `int foo(int a) volatile;`
///  `(int a) &&` in `int foo(int a) &&;`
///  `() -> int` in `auto foo() -> int;`
///  `() const` in `int foo() const;`
///  `() noexcept` in `int foo() noexcept;`
///  `() throw()` in `int foo() throw();`
///
/// (!) override doesn't belong here.
class ParametersAndQualifiers final : public Tree {
public:
  ParametersAndQualifiers() : Tree(NodeKind::ParametersAndQualifiers) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::ParametersAndQualifiers;
  }
  syntax::Leaf *lparen();
  /// FIXME: use custom iterator instead of 'vector'.
  std::vector<syntax::SimpleDeclaration *> parameters();
  syntax::Leaf *rparen();
  syntax::TrailingReturnType *trailingReturn();
};

/// Member pointer inside a declarator
/// E.g. `X::*` in `int X::* a = 0;`
class MemberPointer final : public Tree {
public:
  MemberPointer() : Tree(NodeKind::MemberPointer) {}
  static bool classof(const Node *N) {
    return N->kind() == NodeKind::MemberPointer;
  }
};

} // namespace syntax
} // namespace clang
#endif
+287 −24
Original line number Diff line number Diff line
@@ -6,10 +6,15 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/Syntax/BuildTree.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeLocVisitor.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -20,6 +25,7 @@
#include "clang/Tooling/Syntax/Tree.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
@@ -34,6 +40,110 @@ using namespace clang;
LLVM_ATTRIBUTE_UNUSED
static bool isImplicitExpr(clang::Expr *E) { return E->IgnoreImplicit() != E; }

static SourceLocation getQualifiedNameStart(DeclaratorDecl *D) {
  auto DN = D->getDeclName();
  bool IsAnonymous = DN.isIdentifier() && !DN.getAsIdentifierInfo();
  if (IsAnonymous)
    return SourceLocation();
  return D->getQualifierLoc() ? D->getQualifierLoc().getBeginLoc()
                              : D->getLocation();
}

namespace {
/// Get start location of the Declarator from the TypeLoc.
/// E.g.:
///   loc of `(` in `int (a)`
///   loc of `*` in `int *(a)`
///   loc of the first `(` in `int (*a)(int)`
///   loc of the `*` in `int *(a)(int)`
///   loc of the first `*` in `const int *const *volatile a;`
///
/// It is non-trivial to get the start location because TypeLocs are stored
/// inside out. In the example above `*volatile` is the TypeLoc returned
/// by `Decl.getTypeSourceInfo()`, and `*const` is what `.getPointeeLoc()`
/// returns.
struct GetStartLoc : TypeLocVisitor<GetStartLoc, SourceLocation> {
  SourceLocation VisitParenTypeLoc(ParenTypeLoc T) {
    auto L = Visit(T.getInnerLoc());
    if (L.isValid())
      return L;
    return T.getLParenLoc();
  }

  // Types spelled in the prefix part of the declarator.
  SourceLocation VisitPointerTypeLoc(PointerTypeLoc T) {
    return HandlePointer(T);
  }

  SourceLocation VisitMemberPointerTypeLoc(MemberPointerTypeLoc T) {
    return HandlePointer(T);
  }

  SourceLocation VisitBlockPointerTypeLoc(BlockPointerTypeLoc T) {
    return HandlePointer(T);
  }

  SourceLocation VisitReferenceTypeLoc(ReferenceTypeLoc T) {
    return HandlePointer(T);
  }

  SourceLocation VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc T) {
    return HandlePointer(T);
  }

  // All other cases are not important, as they are either part of declaration
  // specifiers (e.g. inheritors of TypeSpecTypeLoc) or introduce modifiers on
  // existing declarators (e.g. QualifiedTypeLoc). They cannot start the
  // declarator themselves, but their underlying type can.
  SourceLocation VisitTypeLoc(TypeLoc T) {
    auto N = T.getNextTypeLoc();
    if (!N)
      return SourceLocation();
    return Visit(N);
  }

  SourceLocation VisitFunctionProtoTypeLoc(FunctionProtoTypeLoc T) {
    if (T.getTypePtr()->hasTrailingReturn())
      return SourceLocation(); // avoid recursing into the suffix of declarator.
    return VisitTypeLoc(T);
  }

private:
  template <class PtrLoc> SourceLocation HandlePointer(PtrLoc T) {
    auto L = Visit(T.getPointeeLoc());
    if (L.isValid())
      return L;
    return T.getLocalSourceRange().getBegin();
  }
};
} // namespace

/// Gets the range of declarator as defined by the C++ grammar. E.g.
///     `int a;` -> range of `a`,
///     `int *a;` -> range of `*a`,
///     `int a[10];` -> range of `a[10]`,
///     `int a[1][2][3];` -> range of `a[1][2][3]`,
///     `int *a = nullptr` -> range of `*a = nullptr`.
/// FIMXE: \p Name must be a source range, e.g. for `operator+`.
static SourceRange getDeclaratorRange(const SourceManager &SM, TypeLoc T,
                                      SourceLocation Name,
                                      SourceRange Initializer) {
  SourceLocation Start = GetStartLoc().Visit(T);
  SourceLocation End = T.getSourceRange().getEnd();
  assert(End.isValid());
  if (Name.isValid()) {
    if (Start.isInvalid())
      Start = Name;
    if (SM.isBeforeInTranslationUnit(End, Name))
      End = Name;
  }
  if (Initializer.isValid()) {
    assert(SM.isBeforeInTranslationUnit(End, Initializer.getEnd()));
    End = Initializer.getEnd();
  }
  return SourceRange(Start, End);
}

/// A helper class for constructing the syntax tree while traversing a clang
/// AST.
///
@@ -57,6 +167,7 @@ public:
  }

  llvm::BumpPtrAllocator &allocator() { return Arena.allocator(); }
  const SourceManager &sourceManager() const { return Arena.sourceManager(); }

  /// Populate children for \p New node, assuming it covers tokens from \p
  /// Range.
@@ -64,15 +175,15 @@ public:

  /// Must be called with the range of each `DeclaratorDecl`. Ensures the
  /// corresponding declarator nodes are covered by `SimpleDeclaration`.
  void noticeDeclaratorRange(llvm::ArrayRef<syntax::Token> Range);
  void noticeDeclRange(llvm::ArrayRef<syntax::Token> Range);

  /// Notifies that we should not consume trailing semicolon when computing
  /// token range of \p D.
  void noticeDeclaratorWithoutSemicolon(Decl *D);
  void noticeDeclWithoutSemicolon(Decl *D);

  /// Mark the \p Child node with a corresponding \p Role. All marked children
  /// should be consumed by foldNode.
  /// (!) when called on expressions (clang::Expr is derived from clang::Stmt),
  /// When called on expressions (clang::Expr is derived from clang::Stmt),
  /// wraps expressions into expression statement.
  void markStmtChild(Stmt *Child, NodeRole Role);
  /// Should be called for expressions in non-statement position to avoid
@@ -81,6 +192,13 @@ public:

  /// Set role for a token starting at \p Loc.
  void markChildToken(SourceLocation Loc, NodeRole R);
  /// Set role for \p T.
  void markChildToken(const syntax::Token *T, NodeRole R);

  /// Set role for the node that spans exactly \p Range.
  void markChild(llvm::ArrayRef<syntax::Token> Range, NodeRole R);
  /// Set role for the delayed node that spans exactly \p Range.
  void markDelayedChild(llvm::ArrayRef<syntax::Token> Range, NodeRole R);

  /// Finish building the tree and consume the root node.
  syntax::TranslationUnit *finalize() && {
@@ -141,7 +259,7 @@ private:
  withTrailingSemicolon(llvm::ArrayRef<syntax::Token> Tokens) const {
    assert(!Tokens.empty());
    assert(Tokens.back().kind() != tok::eof);
    // (!) we never consume 'eof', so looking at the next token is ok.
    // We never consume 'eof', so looking at the next token is ok.
    if (Tokens.back().kind() != tok::semi && Tokens.end()->kind() == tok::semi)
      return llvm::makeArrayRef(Tokens.begin(), Tokens.end() + 1);
    return Tokens;
@@ -172,6 +290,14 @@ private:

    ~Forest() { assert(DelayedFolds.empty()); }

    void assignRoleDelayed(llvm::ArrayRef<syntax::Token> Range,
                           syntax::NodeRole Role) {
      auto It = DelayedFolds.find(Range.begin());
      assert(It != DelayedFolds.end());
      assert(It->second.End == Range.end());
      It->second.Role = Role;
    }

    void assignRole(llvm::ArrayRef<syntax::Token> Range,
                    syntax::NodeRole Role) {
      assert(!Range.empty());
@@ -189,12 +315,19 @@ private:
                      llvm::ArrayRef<syntax::Token> Tokens,
                      syntax::Tree *Node) {
      // Execute delayed folds inside `Tokens`.
      auto BeginExecuted = DelayedFolds.lower_bound(Tokens.begin());
      auto It = BeginExecuted;
      for (; It != DelayedFolds.end() && It->second.End <= Tokens.end(); ++It)
      auto BeginFolds = DelayedFolds.lower_bound(Tokens.begin());
      auto EndFolds = BeginFolds;
      for (; EndFolds != DelayedFolds.end() &&
             EndFolds->second.End <= Tokens.end();
           ++EndFolds)
        ;
      // We go in reverse order to ensure we fold deeper nodes first.
      for (auto RevIt = EndFolds; RevIt != BeginFolds; --RevIt) {
        auto It = std::prev(RevIt);
        foldChildrenEager(A, llvm::makeArrayRef(It->first, It->second.End),
                          It->second.Node);
      DelayedFolds.erase(BeginExecuted, It);
      }
      DelayedFolds.erase(BeginFolds, EndFolds);

      // Attach children to `Node`.
      foldChildrenEager(A, Tokens, Node);
@@ -269,7 +402,7 @@ private:
          (EndChildren == Trees.end() || EndChildren->first == Tokens.end()) &&
          "fold crosses boundaries of existing subtrees");

      // (!) we need to go in reverse order, because we can only prepend.
      // We need to go in reverse order, because we can only prepend.
      for (auto It = EndChildren; It != BeginChildren; --It)
        Node->prependChildLowLevel(std::prev(It)->second.Node,
                                   std::prev(It)->second.Role);
@@ -301,6 +434,7 @@ private:
    struct DelayedFold {
      const syntax::Token *End = nullptr;
      syntax::Tree *Node = nullptr;
      NodeRole Role = NodeRole::Unknown;
    };
    std::map<const syntax::Token *, DelayedFold> DelayedFolds;
  };
@@ -324,16 +458,43 @@ public:

  bool shouldTraversePostOrder() const { return true; }

  bool WalkUpFromDeclaratorDecl(DeclaratorDecl *D) {
  bool WalkUpFromDeclaratorDecl(DeclaratorDecl *DD) {
    // Ensure declarators are covered by SimpleDeclaration.
    Builder.noticeDeclaratorRange(Builder.getRange(D));
    // FIXME: build nodes for the declarator too.
    Builder.noticeDeclRange(Builder.getRange(DD));

    // Build the declarator node.
    SourceRange Initializer;
    if (auto *V = llvm::dyn_cast<VarDecl>(DD)) {
      auto *I = V->getInit();
      // Initializers in range-based-for are not part of the declarator
      if (I && !V->isCXXForRangeDecl())
        Initializer = I->getSourceRange();
    }
    auto Declarator = getDeclaratorRange(
        Builder.sourceManager(), DD->getTypeSourceInfo()->getTypeLoc(),
        getQualifiedNameStart(DD), Initializer);
    if (Declarator.isValid()) {
      auto Tokens =
          Builder.getRange(Declarator.getBegin(), Declarator.getEnd());
      Builder.foldNode(Tokens, new (allocator()) syntax::SimpleDeclarator);
      Builder.markChild(Tokens, syntax::NodeRole::SimpleDeclaration_declarator);
    }

    return true;
  }

  bool WalkUpFromTypedefNameDecl(TypedefNameDecl *D) {
    // Also a declarator.
    Builder.noticeDeclaratorRange(Builder.getRange(D));
    // FIXME: build nodes for the declarator too.
    // Ensure declarators are covered by SimpleDeclaration.
    Builder.noticeDeclRange(Builder.getRange(D));

    auto R = getDeclaratorRange(
        Builder.sourceManager(), D->getTypeSourceInfo()->getTypeLoc(),
        /*Name=*/D->getLocation(), /*Initializer=*/SourceRange());
    if (R.isValid()) {
      auto Tokens = Builder.getRange(R.getBegin(), R.getEnd());
      Builder.foldNode(Tokens, new (allocator()) syntax::SimpleDeclarator);
      Builder.markChild(Tokens, syntax::NodeRole::SimpleDeclaration_declarator);
    }
    return true;
  }

@@ -356,7 +517,7 @@ public:
  }

  bool WalkUpFromTranslationUnitDecl(TranslationUnitDecl *TU) {
    // (!) we do not want to call VisitDecl(), the declaration for translation
    // We do not want to call VisitDecl(), the declaration for translation
    // unit is built by finalize().
    return true;
  }
@@ -401,10 +562,10 @@ public:
    if (auto *DS = llvm::dyn_cast_or_null<DeclStmt>(S)) {
      // We want to consume the semicolon, make sure SimpleDeclaration does not.
      for (auto *D : DS->decls())
        Builder.noticeDeclaratorWithoutSemicolon(D);
        Builder.noticeDeclWithoutSemicolon(D);
    } else if (auto *E = llvm::dyn_cast_or_null<Expr>(S)) {
      // (!) do not recurse into subexpressions.
      // we do not have syntax trees for expressions yet, so we only want to see
      // Do not recurse into subexpressions.
      // We do not have syntax trees for expressions yet, so we only want to see
      // the first top-level expression.
      return WalkUpFromExpr(E->IgnoreImplicit());
    }
@@ -431,6 +592,62 @@ public:
    return true;
  }

  bool TraverseParenTypeLoc(ParenTypeLoc L) {
    // We reverse order of traversal to get the proper syntax structure.
    if (!WalkUpFromParenTypeLoc(L))
      return false;
    return TraverseTypeLoc(L.getInnerLoc());
  }

  bool WalkUpFromParenTypeLoc(ParenTypeLoc L) {
    Builder.markChildToken(L.getLParenLoc(), syntax::NodeRole::OpenParen);
    Builder.markChildToken(L.getRParenLoc(), syntax::NodeRole::CloseParen);
    Builder.foldNode(Builder.getRange(L.getLParenLoc(), L.getRParenLoc()),
                     new (allocator()) syntax::ParenDeclarator);
    return true;
  }

  // Declarator chunks, they are produced by type locs and some clang::Decls.
  bool WalkUpFromArrayTypeLoc(ArrayTypeLoc L) {
    Builder.markChildToken(L.getLBracketLoc(), syntax::NodeRole::OpenParen);
    Builder.markExprChild(L.getSizeExpr(),
                          syntax::NodeRole::ArraySubscript_sizeExpression);
    Builder.markChildToken(L.getRBracketLoc(), syntax::NodeRole::CloseParen);
    Builder.foldNode(Builder.getRange(L.getLBracketLoc(), L.getRBracketLoc()),
                     new (allocator()) syntax::ArraySubscript);
    return true;
  }

  bool WalkUpFromFunctionTypeLoc(FunctionTypeLoc L) {
    Builder.markChildToken(L.getLParenLoc(), syntax::NodeRole::OpenParen);
    for (auto *P : L.getParams())
      Builder.markDelayedChild(
          Builder.getRange(P),
          syntax::NodeRole::ParametersAndQualifiers_parameter);
    Builder.markChildToken(L.getRParenLoc(), syntax::NodeRole::CloseParen);
    Builder.foldNode(Builder.getRange(L.getLParenLoc(), L.getEndLoc()),
                     new (allocator()) syntax::ParametersAndQualifiers);
    return true;
  }

  bool WalkUpFromFunctionProtoTypeLoc(FunctionProtoTypeLoc L) {
    if (!L.getTypePtr()->hasTrailingReturn())
      return WalkUpFromFunctionTypeLoc(L);

    auto TrailingReturnTokens = BuildTrailingReturn(L);
    // Finish building the node for parameters.
    Builder.markChild(TrailingReturnTokens,
                      syntax::NodeRole::ParametersAndQualifiers_trailingReturn);
    return WalkUpFromFunctionTypeLoc(L);
  }

  bool WalkUpFromMemberPointerTypeLoc(MemberPointerTypeLoc L) {
    auto SR = L.getLocalSourceRange();
    Builder.foldNode(Builder.getRange(SR.getBegin(), SR.getEnd()),
                     new (allocator()) syntax::MemberPointer);
    return true;
  }

  // The code below is very regular, it could even be generated with some
  // preprocessor magic. We merely assign roles to the corresponding children
  // and fold resulting nodes.
@@ -597,6 +814,37 @@ public:
  }

private:
  /// Returns the range of the built node.
  llvm::ArrayRef<syntax::Token> BuildTrailingReturn(FunctionProtoTypeLoc L) {
    assert(L.getTypePtr()->hasTrailingReturn());

    auto ReturnedType = L.getReturnLoc();
    // Build node for the declarator, if any.
    auto ReturnDeclaratorRange =
        getDeclaratorRange(this->Builder.sourceManager(), ReturnedType,
                           /*Name=*/SourceLocation(),
                           /*Initializer=*/SourceLocation());
    llvm::ArrayRef<syntax::Token> ReturnDeclaratorTokens;
    if (ReturnDeclaratorRange.isValid()) {
      ReturnDeclaratorTokens = Builder.getRange(
          ReturnDeclaratorRange.getBegin(), ReturnDeclaratorRange.getEnd());
      Builder.foldNode(ReturnDeclaratorTokens,
                       new (allocator()) syntax::SimpleDeclarator);
    }

    // Build node for trailing return type.
    auto Return =
        Builder.getRange(ReturnedType.getBeginLoc(), ReturnedType.getEndLoc());
    const auto *Arrow = Return.begin() - 1;
    assert(Arrow->kind() == tok::arrow);
    auto Tokens = llvm::makeArrayRef(Arrow, Return.end());
    Builder.markChildToken(Arrow, syntax::NodeRole::TrailingReturnType_arrow);
    if (!ReturnDeclaratorTokens.empty())
      Builder.markChild(ReturnDeclaratorTokens,
                        syntax::NodeRole::TrailingReturnType_declarator);
    Builder.foldNode(Tokens, new (allocator()) syntax::TrailingReturnType);
    return Tokens;
  }
  /// A small helper to save some typing.
  llvm::BumpPtrAllocator &allocator() { return Builder.allocator(); }

@@ -610,15 +858,14 @@ void syntax::TreeBuilder::foldNode(llvm::ArrayRef<syntax::Token> Range,
  Pending.foldChildren(Arena, Range, New);
}

void syntax::TreeBuilder::noticeDeclaratorRange(
    llvm::ArrayRef<syntax::Token> Range) {
void syntax::TreeBuilder::noticeDeclRange(llvm::ArrayRef<syntax::Token> Range) {
  if (Pending.extendDelayedFold(Range))
    return;
  Pending.foldChildrenDelayed(Range,
                              new (allocator()) syntax::SimpleDeclaration);
}

void syntax::TreeBuilder::noticeDeclaratorWithoutSemicolon(Decl *D) {
void syntax::TreeBuilder::noticeDeclWithoutSemicolon(Decl *D) {
  DeclsWithoutSemicolons.insert(D);
}

@@ -628,6 +875,22 @@ void syntax::TreeBuilder::markChildToken(SourceLocation Loc, NodeRole Role) {
  Pending.assignRole(*findToken(Loc), Role);
}

void syntax::TreeBuilder::markChildToken(const syntax::Token *T, NodeRole R) {
  if (!T)
    return;
  Pending.assignRole(*T, R);
}

void syntax::TreeBuilder::markChild(llvm::ArrayRef<syntax::Token> Range,
                                    NodeRole R) {
  Pending.assignRole(Range, R);
}

void syntax::TreeBuilder::markDelayedChild(llvm::ArrayRef<syntax::Token> Range,
                                           NodeRole R) {
  Pending.assignRoleDelayed(Range, R);
}

void syntax::TreeBuilder::markStmtChild(Stmt *Child, NodeRole Role) {
  if (!Child)
    return;
@@ -638,7 +901,7 @@ void syntax::TreeBuilder::markStmtChild(Stmt *Child, NodeRole Role) {
  if (auto *E = dyn_cast<Expr>(Child)) {
    Pending.assignRole(getExprRange(E),
                       NodeRole::ExpressionStatement_expression);
    // (!) 'getRange(Stmt)' ensures this already covers a trailing semicolon.
    // 'getRange(Stmt)' ensures this already covers a trailing semicolon.
    Pending.foldChildren(Arena, Range,
                         new (allocator()) syntax::ExpressionStatement);
  }
+94 −0

File changed.

Preview size limit exceeded, changes collapsed.

+653 −77

File changed.

Preview size limit exceeded, changes collapsed.