diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h --- a/clang/include/clang/Tooling/Syntax/Nodes.h +++ b/clang/include/clang/Tooling/Syntax/Nodes.h @@ -38,10 +38,10 @@ Leaf, TranslationUnit, - // Expressions + // Expressions. UnknownExpression, - // Statements + // Statements. UnknownStatement, DeclarationStatement, EmptyStatement, @@ -58,7 +58,7 @@ ExpressionStatement, CompoundStatement, - // Declarations + // Declarations. UnknownDeclaration, EmptyDeclaration, StaticAssertDeclaration, @@ -68,7 +68,16 @@ 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 @@ 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 @@ static bool classof(const Node *N) { return N->kind() == NodeKind::SimpleDeclaration; } + /// FIXME: use custom iterator instead of 'vector'. + std::vector declarators(); }; /// namespace { } @@ -424,6 +443,113 @@ } }; +/// 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 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 --- 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 @@ 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 { + 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 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 @@ } 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 @@ /// Must be called with the range of each `DeclaratorDecl`. Ensures the /// corresponding declarator nodes are covered by `SimpleDeclaration`. - void noticeDeclaratorRange(llvm::ArrayRef Range); + void noticeDeclRange(llvm::ArrayRef 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 @@ /// 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 Range, NodeRole R); + /// Set role for the delayed node that spans exactly \p Range. + void markDelayedChild(llvm::ArrayRef Range, NodeRole R); /// Finish building the tree and consume the root node. syntax::TranslationUnit *finalize() && { @@ -141,7 +259,7 @@ withTrailingSemicolon(llvm::ArrayRef 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 @@ ~Forest() { assert(DelayedFolds.empty()); } + void assignRoleDelayed(llvm::ArrayRef 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 Range, syntax::NodeRole Role) { assert(!Range.empty()); @@ -189,12 +315,19 @@ llvm::ArrayRef 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 @@ (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 @@ struct DelayedFold { const syntax::Token *End = nullptr; syntax::Tree *Node = nullptr; + NodeRole Role = NodeRole::Unknown; }; std::map DelayedFolds; }; @@ -324,16 +458,43 @@ 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(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 @@ } 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 @@ if (auto *DS = llvm::dyn_cast_or_null(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(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 @@ 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 @@ } private: + /// Returns the range of the built node. + llvm::ArrayRef 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 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 @@ Pending.foldChildren(Arena, Range, New); } -void syntax::TreeBuilder::noticeDeclaratorRange( - llvm::ArrayRef Range) { +void syntax::TreeBuilder::noticeDeclRange(llvm::ArrayRef 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 @@ 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 Range, + NodeRole R) { + Pending.assignRole(Range, R); +} + +void syntax::TreeBuilder::markDelayedChild(llvm::ArrayRef Range, + NodeRole R) { + Pending.assignRoleDelayed(Range, R); +} + void syntax::TreeBuilder::markStmtChild(Stmt *Child, NodeRole Role) { if (!Child) return; @@ -638,7 +901,7 @@ if (auto *E = dyn_cast(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 --- a/clang/lib/Tooling/Syntax/Nodes.cpp +++ b/clang/lib/Tooling/Syntax/Nodes.cpp @@ -68,6 +68,18 @@ 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 @@ 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 @@ return llvm::cast_or_null( findChild(syntax::NodeRole::StaticAssertDeclaration_message)); } + +std::vector +syntax::SimpleDeclaration::declarators() { + std::vector Children; + for (auto *C = firstChild(); C; C = C->nextSibling()) { + if (C->role() == syntax::NodeRole::SimpleDeclaration_declarator) + Children.push_back(llvm::cast(C)); + } + return Children; +} + +syntax::Leaf *syntax::ParenDeclarator::lparen() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::OpenParen)); +} + +syntax::Leaf *syntax::ParenDeclarator::rparen() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::CloseParen)); +} + +syntax::Leaf *syntax::ArraySubscript::lbracket() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::OpenParen)); +} + +syntax::Expression *syntax::ArraySubscript::sizeExpression() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::ArraySubscript_sizeExpression)); +} + +syntax::Leaf *syntax::ArraySubscript::rbracket() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::CloseParen)); +} + +syntax::Leaf *syntax::TrailingReturnType::arrow() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::TrailingReturnType_arrow)); +} + +syntax::SimpleDeclarator *syntax::TrailingReturnType::declarator() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::TrailingReturnType_declarator)); +} + +syntax::Leaf *syntax::ParametersAndQualifiers::lparen() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::OpenParen)); +} + +std::vector +syntax::ParametersAndQualifiers::parameters() { + std::vector Children; + for (auto *C = firstChild(); C; C = C->nextSibling()) { + if (C->role() == syntax::NodeRole::ParametersAndQualifiers_parameter) + Children.push_back(llvm::cast(C)); + } + return Children; +} + +syntax::Leaf *syntax::ParametersAndQualifiers::rparen() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::CloseParen)); +} + +syntax::TrailingReturnType *syntax::ParametersAndQualifiers::trailingReturn() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::ParametersAndQualifiers_trailingReturn)); +} diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp --- a/clang/unittests/Tooling/Syntax/TreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -174,17 +174,21 @@ *: TranslationUnit |-SimpleDeclaration | |-int -| |-main -| |-( -| |-) +| |-SimpleDeclarator +| | |-main +| | `-ParametersAndQualifiers +| | |-( +| | `-) | `-CompoundStatement | |-{ | `-} `-SimpleDeclaration |-void - |-foo - |-( - |-) + |-SimpleDeclarator + | |-foo + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ `-} @@ -201,9 +205,11 @@ *: TranslationUnit `-SimpleDeclaration |-int - |-main - |-( - |-) + |-SimpleDeclarator + | |-main + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-IfStatement @@ -246,9 +252,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-ForStatement @@ -268,18 +276,21 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-DeclarationStatement | |-SimpleDeclaration | | |-int - | | |-a - | | |-= - | | `-UnknownExpression - | | `-10 + | | `-SimpleDeclarator + | | |-a + | | |-= + | | `-UnknownExpression + | | `-10 | `-; `-} )txt"}, @@ -287,9 +298,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-EmptyStatement @@ -309,9 +322,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-SwitchStatement @@ -345,9 +360,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-WhileStatement @@ -375,9 +392,11 @@ *: TranslationUnit `-SimpleDeclaration |-int - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-ReturnStatement @@ -398,26 +417,31 @@ *: 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 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-main - |-( - |-) + |-SimpleDeclarator + | |-main + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-UnknownStatement @@ -460,9 +486,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-ExpressionStatement @@ -500,10 +528,12 @@ *: TranslationUnit `-SimpleDeclaration |-int - |-* - |-a + |-SimpleDeclarator + | |-* + | `-a |-, - |-b + |-SimpleDeclarator + | `-b `-; )txt"}, {R"cpp( @@ -514,10 +544,12 @@ `-SimpleDeclaration |-typedef |-int - |-* - |-a + |-SimpleDeclarator + | |-* + | `-a |-, - |-b + |-SimpleDeclarator + | `-b `-; )txt"}, // Multiple declarators inside a statement. @@ -531,27 +563,33 @@ *: 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 @@ |-SimpleDeclaration | |-struct | |-Y -| |-* -| |-y1 +| |-SimpleDeclarator +| | |-* +| | `-y1 | `-; |-SimpleDeclaration | |-struct | |-Y | |-{ | |-} -| |-* -| |-y2 +| |-SimpleDeclarator +| | |-* +| | `-y2 | `-; `-SimpleDeclaration |-struct |-{ |-} - |-* - |-a1 + |-SimpleDeclarator + | |-* + | `-a1 `-; )txt"}, {R"cpp( @@ -666,7 +707,8 @@ | |-{ | |-SimpleDeclaration | | |-int -| | |-a +| | |-SimpleDeclarator +| | | `-a | | `-; | `-} `-UsingDeclaration @@ -766,7 +808,8 @@ | |-"C" | `-SimpleDeclaration | |-int -| |-a +| |-SimpleDeclarator +| | `-a | `-; `-LinkageSpecificationDeclaration |-extern @@ -774,11 +817,13 @@ |-{ |-SimpleDeclaration | |-int - | |-b + | |-SimpleDeclarator + | | `-b | `-; |-SimpleDeclaration | |-int - | |-c + | |-SimpleDeclarator + | | `-c | `-; `-} )txt"}, @@ -793,9 +838,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-IfStatement @@ -834,9 +881,11 @@ *: TranslationUnit `-SimpleDeclaration |-void - |-test - |-( - |-) + |-SimpleDeclarator + | |-test + | `-ParametersAndQualifiers + | |-( + | `-) `-CompoundStatement |-{ |-CompoundStatement @@ -855,6 +904,533 @@ | `-} `-} )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) {