[clang] 7d382dc - [Syntax] Build declarator nodes

Dmitri Gribenko via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 16 11:22:37 PDT 2020


Author: Marcel Hlopko
Date: 2020-03-16T19:13:59+01:00
New Revision: 7d382dcd46a18c23a01e3754807f577598a9bc84

URL: https://github.com/llvm/llvm-project/commit/7d382dcd46a18c23a01e3754807f577598a9bc84
DIFF: https://github.com/llvm/llvm-project/commit/7d382dcd46a18c23a01e3754807f577598a9bc84.diff

LOG: [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

Added: 
    

Modified: 
    clang/include/clang/Tooling/Syntax/Nodes.h
    clang/lib/Tooling/Syntax/BuildTree.cpp
    clang/lib/Tooling/Syntax/Nodes.cpp
    clang/unittests/Tooling/Syntax/TreeTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h
index 25acc1757428..82fcac33f99b 100644
--- a/clang/include/clang/Tooling/Syntax/Nodes.h
+++ b/clang/include/clang/Tooling/Syntax/Nodes.h
@@ -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 @@ class SimpleDeclaration final : public Declaration {
   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 @@ class TypeAliasDeclaration final : public Declaration {
   }
 };
 
+/// 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

diff  --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index 243039413403..9ebf7d29d8ed 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -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 @@ class syntax::TreeBuilder {
   }
 
   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,16 +175,16 @@ class syntax::TreeBuilder {
 
   /// 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),
-  ///     wraps expressions into expression statement.
+  /// 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
   /// wrapping into expression statement.
@@ -81,6 +192,13 @@ class syntax::TreeBuilder {
 
   /// 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 @@ class syntax::TreeBuilder {
   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 @@ class syntax::TreeBuilder {
 
     ~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 @@ class syntax::TreeBuilder {
                       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 @@ class syntax::TreeBuilder {
           (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 @@ class syntax::TreeBuilder {
     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 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
 
   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 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
   }
 
   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 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
     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 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
     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 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
   }
 
 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);
   }

diff  --git a/clang/lib/Tooling/Syntax/Nodes.cpp b/clang/lib/Tooling/Syntax/Nodes.cpp
index 5b0c5107c134..4f86007e39bb 100644
--- a/clang/lib/Tooling/Syntax/Nodes.cpp
+++ b/clang/lib/Tooling/Syntax/Nodes.cpp
@@ -68,6 +68,18 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeKind K) {
     return OS << "UsingDeclaration";
   case NodeKind::TypeAliasDeclaration:
     return OS << "TypeAliasDeclaration";
+  case NodeKind::SimpleDeclarator:
+    return OS << "SimpleDeclarator";
+  case NodeKind::ParenDeclarator:
+    return OS << "ParenDeclarator";
+  case NodeKind::ArraySubscript:
+    return OS << "ArraySubscript";
+  case NodeKind::TrailingReturnType:
+    return OS << "TrailingReturnType";
+  case NodeKind::ParametersAndQualifiers:
+    return OS << "ParametersAndQualifiers";
+  case NodeKind::MemberPointer:
+    return OS << "MemberPointer";
   }
   llvm_unreachable("unknown node kind");
 }
@@ -104,6 +116,18 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeRole R) {
     return OS << "StaticAssertDeclaration_condition";
   case syntax::NodeRole::StaticAssertDeclaration_message:
     return OS << "StaticAssertDeclaration_message";
+  case syntax::NodeRole::SimpleDeclaration_declarator:
+    return OS << "SimpleDeclaration_declarator";
+  case syntax::NodeRole::ArraySubscript_sizeExpression:
+    return OS << "ArraySubscript_sizeExpression";
+  case syntax::NodeRole::TrailingReturnType_arrow:
+    return OS << "TrailingReturnType_arrow";
+  case syntax::NodeRole::TrailingReturnType_declarator:
+    return OS << "TrailingReturnType_declarator";
+  case syntax::NodeRole::ParametersAndQualifiers_parameter:
+    return OS << "ParametersAndQualifiers_parameter";
+  case syntax::NodeRole::ParametersAndQualifiers_trailingReturn:
+    return OS << "ParametersAndQualifiers_trailingReturn";
   }
   llvm_unreachable("invalid role");
 }
@@ -246,3 +270,73 @@ syntax::Expression *syntax::StaticAssertDeclaration::message() {
   return llvm::cast_or_null<syntax::Expression>(
       findChild(syntax::NodeRole::StaticAssertDeclaration_message));
 }
+
+std::vector<syntax::SimpleDeclarator *>
+syntax::SimpleDeclaration::declarators() {
+  std::vector<syntax::SimpleDeclarator *> Children;
+  for (auto *C = firstChild(); C; C = C->nextSibling()) {
+    if (C->role() == syntax::NodeRole::SimpleDeclaration_declarator)
+      Children.push_back(llvm::cast<syntax::SimpleDeclarator>(C));
+  }
+  return Children;
+}
+
+syntax::Leaf *syntax::ParenDeclarator::lparen() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::OpenParen));
+}
+
+syntax::Leaf *syntax::ParenDeclarator::rparen() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::CloseParen));
+}
+
+syntax::Leaf *syntax::ArraySubscript::lbracket() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::OpenParen));
+}
+
+syntax::Expression *syntax::ArraySubscript::sizeExpression() {
+  return llvm::cast_or_null<syntax::Expression>(
+      findChild(syntax::NodeRole::ArraySubscript_sizeExpression));
+}
+
+syntax::Leaf *syntax::ArraySubscript::rbracket() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::CloseParen));
+}
+
+syntax::Leaf *syntax::TrailingReturnType::arrow() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::TrailingReturnType_arrow));
+}
+
+syntax::SimpleDeclarator *syntax::TrailingReturnType::declarator() {
+  return llvm::cast_or_null<syntax::SimpleDeclarator>(
+      findChild(syntax::NodeRole::TrailingReturnType_declarator));
+}
+
+syntax::Leaf *syntax::ParametersAndQualifiers::lparen() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::OpenParen));
+}
+
+std::vector<syntax::SimpleDeclaration *>
+syntax::ParametersAndQualifiers::parameters() {
+  std::vector<syntax::SimpleDeclaration *> Children;
+  for (auto *C = firstChild(); C; C = C->nextSibling()) {
+    if (C->role() == syntax::NodeRole::ParametersAndQualifiers_parameter)
+      Children.push_back(llvm::cast<syntax::SimpleDeclaration>(C));
+  }
+  return Children;
+}
+
+syntax::Leaf *syntax::ParametersAndQualifiers::rparen() {
+  return llvm::cast_or_null<syntax::Leaf>(
+      findChild(syntax::NodeRole::CloseParen));
+}
+
+syntax::TrailingReturnType *syntax::ParametersAndQualifiers::trailingReturn() {
+  return llvm::cast_or_null<syntax::TrailingReturnType>(
+      findChild(syntax::NodeRole::ParametersAndQualifiers_trailingReturn));
+}

diff  --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp
index 1749e6635a12..6e914b6378c8 100644
--- a/clang/unittests/Tooling/Syntax/TreeTest.cpp
+++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -174,17 +174,21 @@ void foo() {}
 *: TranslationUnit
 |-SimpleDeclaration
 | |-int
-| |-main
-| |-(
-| |-)
+| |-SimpleDeclarator
+| | |-main
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   `-)
 | `-CompoundStatement
 |   |-{
 |   `-}
 `-SimpleDeclaration
   |-void
-  |-foo
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-foo
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     `-}
@@ -201,9 +205,11 @@ int main() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-int
-  |-main
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-main
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-IfStatement
@@ -246,9 +252,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-ForStatement
@@ -268,18 +276,21 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-DeclarationStatement
     | |-SimpleDeclaration
     | | |-int
-    | | |-a
-    | | |-=
-    | | `-UnknownExpression
-    | |   `-10
+    | | `-SimpleDeclarator
+    | |   |-a
+    | |   |-=
+    | |   `-UnknownExpression
+    | |     `-10
     | `-;
     `-}
 )txt"},
@@ -287,9 +298,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-EmptyStatement
@@ -309,9 +322,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-SwitchStatement
@@ -345,9 +360,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-WhileStatement
@@ -375,9 +392,11 @@ int test() { return 1; }
 *: TranslationUnit
 `-SimpleDeclaration
   |-int
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-ReturnStatement
@@ -398,26 +417,31 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-DeclarationStatement
     | |-SimpleDeclaration
     | | |-int
-    | | |-a
-    | | |-[
-    | | |-UnknownExpression
-    | | | `-3
-    | | `-]
+    | | `-SimpleDeclarator
+    | |   |-a
+    | |   `-ArraySubscript
+    | |     |-[
+    | |     |-UnknownExpression
+    | |     | `-3
+    | |     `-]
     | `-;
     |-RangeBasedForStatement
     | |-for
     | |-(
     | |-SimpleDeclaration
     | | |-int
-    | | |-x
+    | | |-SimpleDeclarator
+    | | | `-x
     | | `-:
     | |-UnknownExpression
     | | `-a
@@ -433,9 +457,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-main
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-main
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-UnknownStatement
@@ -460,9 +486,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-ExpressionStatement
@@ -500,10 +528,12 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-int
-  |-*
-  |-a
+  |-SimpleDeclarator
+  | |-*
+  | `-a
   |-,
-  |-b
+  |-SimpleDeclarator
+  | `-b
   `-;
   )txt"},
       {R"cpp(
@@ -514,10 +544,12 @@ void test() {
 `-SimpleDeclaration
   |-typedef
   |-int
-  |-*
-  |-a
+  |-SimpleDeclarator
+  | |-*
+  | `-a
   |-,
-  |-b
+  |-SimpleDeclarator
+  | `-b
   `-;
   )txt"},
       // Multiple declarators inside a statement.
@@ -531,27 +563,33 @@ void foo() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-foo
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-foo
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-DeclarationStatement
     | |-SimpleDeclaration
     | | |-int
-    | | |-*
-    | | |-a
+    | | |-SimpleDeclarator
+    | | | |-*
+    | | | `-a
     | | |-,
-    | | `-b
+    | | `-SimpleDeclarator
+    | |   `-b
     | `-;
     |-DeclarationStatement
     | |-SimpleDeclaration
     | | |-typedef
     | | |-int
-    | | |-*
-    | | |-ta
+    | | |-SimpleDeclarator
+    | | | |-*
+    | | | `-ta
     | | |-,
-    | | `-tb
+    | | `-SimpleDeclarator
+    | |   `-tb
     | `-;
     `-}
   )txt"},
@@ -617,23 +655,26 @@ struct {} *a1;
 |-SimpleDeclaration
 | |-struct
 | |-Y
-| |-*
-| |-y1
+| |-SimpleDeclarator
+| | |-*
+| | `-y1
 | `-;
 |-SimpleDeclaration
 | |-struct
 | |-Y
 | |-{
 | |-}
-| |-*
-| |-y2
+| |-SimpleDeclarator
+| | |-*
+| | `-y2
 | `-;
 `-SimpleDeclaration
   |-struct
   |-{
   |-}
-  |-*
-  |-a1
+  |-SimpleDeclarator
+  | |-*
+  | `-a1
   `-;
 )txt"},
       {R"cpp(
@@ -666,7 +707,8 @@ using ns::a;
 | |-{
 | |-SimpleDeclaration
 | | |-int
-| | |-a
+| | |-SimpleDeclarator
+| | | `-a
 | | `-;
 | `-}
 `-UsingDeclaration
@@ -766,7 +808,8 @@ extern "C" { int b; int c; }
 | |-"C"
 | `-SimpleDeclaration
 |   |-int
-|   |-a
+|   |-SimpleDeclarator
+|   | `-a
 |   `-;
 `-LinkageSpecificationDeclaration
   |-extern
@@ -774,11 +817,13 @@ extern "C" { int b; int c; }
   |-{
   |-SimpleDeclaration
   | |-int
-  | |-b
+  | |-SimpleDeclarator
+  | | `-b
   | `-;
   |-SimpleDeclaration
   | |-int
-  | |-c
+  | |-SimpleDeclarator
+  | | `-c
   | `-;
   `-}
        )txt"},
@@ -793,9 +838,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-IfStatement
@@ -834,9 +881,11 @@ void test() {
 *: TranslationUnit
 `-SimpleDeclaration
   |-void
-  |-test
-  |-(
-  |-)
+  |-SimpleDeclarator
+  | |-test
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   `-)
   `-CompoundStatement
     |-{
     |-CompoundStatement
@@ -855,6 +904,533 @@ void test() {
     | `-}
     `-}
        )txt"},
+      // Array subscripts in declarators.
+      {R"cpp(
+int a[10];
+int b[1][2][3];
+int c[] = {1,2,3};
+void f(int xs[static 10]);
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-a
+| | `-ArraySubscript
+| |   |-[
+| |   |-UnknownExpression
+| |   | `-10
+| |   `-]
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-b
+| | |-ArraySubscript
+| | | |-[
+| | | |-UnknownExpression
+| | | | `-1
+| | | `-]
+| | |-ArraySubscript
+| | | |-[
+| | | |-UnknownExpression
+| | | | `-2
+| | | `-]
+| | `-ArraySubscript
+| |   |-[
+| |   |-UnknownExpression
+| |   | `-3
+| |   `-]
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-c
+| | |-ArraySubscript
+| | | |-[
+| | | `-]
+| | |-=
+| | `-UnknownExpression
+| |   |-{
+| |   |-1
+| |   |-,
+| |   |-2
+| |   |-,
+| |   |-3
+| |   `-}
+| `-;
+`-SimpleDeclaration
+  |-void
+  |-SimpleDeclarator
+  | |-f
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-SimpleDeclaration
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   |-xs
+  |   |   `-ArraySubscript
+  |   |     |-[
+  |   |     |-static
+  |   |     |-UnknownExpression
+  |   |     | `-10
+  |   |     `-]
+  |   `-)
+  `-;
+       )txt"},
+      // Parameter lists in declarators.
+      {R"cpp(
+int a() const;
+int b() volatile;
+int c() &;
+int d() &&;
+int foo(int a, int b);
+int foo(
+  const int a,
+  volatile int b,
+  const volatile int c,
+  int* d,
+  int& e,
+  int&& f
+);
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-a
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   `-const
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-b
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   `-volatile
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-c
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   `-&
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-d
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   `-&&
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-foo
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-SimpleDeclaration
+| |   | |-int
+| |   | `-SimpleDeclarator
+| |   |   `-a
+| |   |-,
+| |   |-SimpleDeclaration
+| |   | |-int
+| |   | `-SimpleDeclarator
+| |   |   `-b
+| |   `-)
+| `-;
+`-SimpleDeclaration
+  |-int
+  |-SimpleDeclarator
+  | |-foo
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-SimpleDeclaration
+  |   | |-const
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   `-a
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-volatile
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   `-b
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-const
+  |   | |-volatile
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   `-c
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   |-*
+  |   |   `-d
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   |-&
+  |   |   `-e
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-int
+  |   | `-SimpleDeclarator
+  |   |   |-&&
+  |   |   `-f
+  |   `-)
+  `-;
+       )txt"},
+      // Trailing const qualifier.
+      {R"cpp(
+struct X {
+  int foo() const;
+}
+    )cpp",
+       R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+  |-struct
+  |-X
+  |-{
+  |-SimpleDeclaration
+  | |-int
+  | |-SimpleDeclarator
+  | | |-foo
+  | | `-ParametersAndQualifiers
+  | |   |-(
+  | |   |-)
+  | |   `-const
+  | `-;
+  `-}
+    )txt"},
+      // Trailing return type in parameter lists.
+      {R"cpp(
+auto foo() -> int;
+    )cpp",
+       R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+  |-auto
+  |-SimpleDeclarator
+  | |-foo
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-)
+  |   `-TrailingReturnType
+  |     |-->
+  |     `-int
+  `-;
+       )txt"},
+      // Exception specification in parameter lists.
+      {R"cpp(
+int a() noexcept;
+int b() noexcept(true);
+int c() throw();
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-a
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   `-noexcept
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-b
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-)
+| |   |-noexcept
+| |   |-(
+| |   |-UnknownExpression
+| |   | `-true
+| |   `-)
+| `-;
+`-SimpleDeclaration
+  |-int
+  |-SimpleDeclarator
+  | |-c
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-)
+  |   |-throw
+  |   |-(
+  |   `-)
+  `-;
+       )txt"},
+      // Declarators in parentheses.
+      {R"cpp(
+int (a);
+int *(b);
+int (*c)(int);
+int *(d)(int);
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | `-ParenDeclarator
+| |   |-(
+| |   |-a
+| |   `-)
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-*
+| | `-ParenDeclarator
+| |   |-(
+| |   |-b
+| |   `-)
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-ParenDeclarator
+| | | |-(
+| | | |-*
+| | | |-c
+| | | `-)
+| | `-ParametersAndQualifiers
+| |   |-(
+| |   |-SimpleDeclaration
+| |   | `-int
+| |   `-)
+| `-;
+`-SimpleDeclaration
+  |-int
+  |-SimpleDeclarator
+  | |-*
+  | |-ParenDeclarator
+  | | |-(
+  | | |-d
+  | | `-)
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-SimpleDeclaration
+  |   | `-int
+  |   `-)
+  `-;
+       )txt"},
+      // CV qualifiers.
+      {R"cpp(
+const int west = -1;
+int const east = 1;
+const int const universal = 0;
+const int const *const *volatile b;
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-const
+| |-int
+| |-SimpleDeclarator
+| | |-west
+| | |-=
+| | `-UnknownExpression
+| |   |--
+| |   `-1
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-const
+| |-SimpleDeclarator
+| | |-east
+| | |-=
+| | `-UnknownExpression
+| |   `-1
+| `-;
+|-SimpleDeclaration
+| |-const
+| |-int
+| |-const
+| |-SimpleDeclarator
+| | |-universal
+| | |-=
+| | `-UnknownExpression
+| |   `-0
+| `-;
+`-SimpleDeclaration
+  |-const
+  |-int
+  |-const
+  |-SimpleDeclarator
+  | |-*
+  | |-const
+  | |-*
+  | |-volatile
+  | `-b
+  `-;
+       )txt"},
+      // Ranges of declarators with trailing return types.
+      {R"cpp(
+auto foo() -> auto(*)(int) -> double*;
+    )cpp",
+       R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+  |-auto
+  |-SimpleDeclarator
+  | |-foo
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-)
+  |   `-TrailingReturnType
+  |     |-->
+  |     |-auto
+  |     `-SimpleDeclarator
+  |       |-ParenDeclarator
+  |       | |-(
+  |       | |-*
+  |       | `-)
+  |       `-ParametersAndQualifiers
+  |         |-(
+  |         |-SimpleDeclaration
+  |         | `-int
+  |         |-)
+  |         `-TrailingReturnType
+  |           |-->
+  |           |-double
+  |           `-SimpleDeclarator
+  |             `-*
+  `-;
+       )txt"},
+      // Member pointers.
+      {R"cpp(
+struct X {};
+int X::* a;
+const int X::* b;
+    )cpp",
+       R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-struct
+| |-X
+| |-{
+| |-}
+| `-;
+|-SimpleDeclaration
+| |-int
+| |-SimpleDeclarator
+| | |-MemberPointer
+| | | |-X
+| | | |-::
+| | | `-*
+| | `-a
+| `-;
+`-SimpleDeclaration
+  |-const
+  |-int
+  |-SimpleDeclarator
+  | |-MemberPointer
+  | | |-X
+  | | |-::
+  | | `-*
+  | `-b
+  `-;
+       )txt"},
+      // All-in-one tests.
+      {R"cpp(
+void x(char a, short (*b)(int));
+    )cpp",
+       R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+  |-void
+  |-SimpleDeclarator
+  | |-x
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-SimpleDeclaration
+  |   | |-char
+  |   | `-SimpleDeclarator
+  |   |   `-a
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-short
+  |   | `-SimpleDeclarator
+  |   |   |-ParenDeclarator
+  |   |   | |-(
+  |   |   | |-*
+  |   |   | |-b
+  |   |   | `-)
+  |   |   `-ParametersAndQualifiers
+  |   |     |-(
+  |   |     |-SimpleDeclaration
+  |   |     | `-int
+  |   |     `-)
+  |   `-)
+  `-;
+       )txt"},
+      {R"cpp(
+void x(char a, short (*b)(int), long (**c)(long long));
+    )cpp",
+       R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+  |-void
+  |-SimpleDeclarator
+  | |-x
+  | `-ParametersAndQualifiers
+  |   |-(
+  |   |-SimpleDeclaration
+  |   | |-char
+  |   | `-SimpleDeclarator
+  |   |   `-a
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-short
+  |   | `-SimpleDeclarator
+  |   |   |-ParenDeclarator
+  |   |   | |-(
+  |   |   | |-*
+  |   |   | |-b
+  |   |   | `-)
+  |   |   `-ParametersAndQualifiers
+  |   |     |-(
+  |   |     |-SimpleDeclaration
+  |   |     | `-int
+  |   |     `-)
+  |   |-,
+  |   |-SimpleDeclaration
+  |   | |-long
+  |   | `-SimpleDeclarator
+  |   |   |-ParenDeclarator
+  |   |   | |-(
+  |   |   | |-*
+  |   |   | |-*
+  |   |   | |-c
+  |   |   | `-)
+  |   |   `-ParametersAndQualifiers
+  |   |     |-(
+  |   |     |-SimpleDeclaration
+  |   |     | |-long
+  |   |     | `-long
+  |   |     `-)
+  |   `-)
+  `-;
+       )txt"},
   };
 
   for (const auto &T : Cases) {


        


More information about the cfe-commits mailing list