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 @@ -99,10 +99,14 @@ ParametersAndQualifiers, MemberPointer, UnqualifiedId, + + // Lists + DeclaratorList, ParameterDeclarationList, CallArguments, - // Nested Name Specifiers. NestedNameSpecifier, + + // Name Specifiers. GlobalNameSpecifier, DecltypeNameSpecifier, IdentifierNameSpecifier, @@ -179,6 +183,7 @@ Member, Callee, Arguments, + Declarators }; /// For debugging purposes. raw_ostream &operator<<(raw_ostream &OS, NodeRole R); @@ -823,6 +828,17 @@ } }; +class DeclaratorList final : public List { +public: + DeclaratorList() : List(NodeKind::DeclaratorList) {} + static bool classof(const Node *N) { + return N->getKind() == NodeKind::DeclaratorList; + } + std::vector getDeclarators(); + std::vector> + getDeclaratorsAndCommas(); +}; + /// Groups multiple declarators (e.g. variables, typedefs, etc.) together. All /// grouped declarators share the same declaration specifiers (e.g. 'int' or /// 'typedef'). 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 @@ -397,6 +397,17 @@ Mapping.add(From, New); } + /// Populate children for \p New list, assuming it covers tokens from a + /// subrange of \p SuperRange. + void foldList(ArrayRef SuperRange, syntax::List *New, + ASTPtr From) { + assert(New); + auto ListRange = Pending.shrinkToFitList(SuperRange); + Pending.foldChildren(Arena, ListRange, New); + if (From) + Mapping.add(From, New); + } + /// Notifies that we should not consume trailing semicolon when computing /// token range of \p D. void noticeDeclWithoutSemicolon(Decl *D); @@ -579,6 +590,35 @@ It->second->setRole(Role); } + /// Shrink \p Range to a subrange that only contains tokens of a list. + /// List elements and delimiters should already have correct roles. + ArrayRef shrinkToFitList(ArrayRef Range) { + auto BeginChildren = Trees.lower_bound(Range.begin()); + assert((BeginChildren == Trees.end() || + BeginChildren->first == Range.begin()) && + "Range crosses boundaries of existing subtrees"); + + auto EndChildren = Trees.lower_bound(Range.end()); + assert( + (EndChildren == Trees.end() || EndChildren->first == Range.end()) && + "Range crosses boundaries of existing subtrees"); + + auto BelongsToList = [](decltype(Trees)::value_type KV) { + auto Role = KV.second->getRole(); + return Role == syntax::NodeRole::ListElement || + Role == syntax::NodeRole::ListDelimiter; + }; + + auto BeginListChildren = + std::find_if(BeginChildren, EndChildren, BelongsToList); + + auto EndListChildren = + std::find_if_not(BeginListChildren, EndChildren, BelongsToList); + + return ArrayRef(BeginListChildren->first, + EndListChildren->first); + } + /// Add \p Node to the forest and attach child nodes based on \p Tokens. void foldChildren(const syntax::Arena &A, ArrayRef Tokens, syntax::Tree *Node) { @@ -1513,14 +1553,31 @@ // There doesn't have to be a declarator (e.g. `void foo(int)` only has // declaration, but no declarator). - if (Range.getBegin().isValid()) { - auto *N = new (allocator()) syntax::SimpleDeclarator; - Builder.foldNode(Builder.getRange(Range), N, nullptr); - Builder.markChild(N, syntax::NodeRole::Declarator); + if (!Range.getBegin().isValid()) { + Builder.markChild(new (allocator()) syntax::DeclaratorList, + syntax::NodeRole::Declarators); + Builder.foldNode(Builder.getDeclarationRange(D), + new (allocator()) syntax::SimpleDeclaration, D); + return true; } - if (Builder.isResponsibleForCreatingDeclaration(D)) { - Builder.foldNode(Builder.getDeclarationRange(D), + auto *N = new (allocator()) syntax::SimpleDeclarator; + Builder.foldNode(Builder.getRange(Range), N, nullptr); + Builder.markChild(N, syntax::NodeRole::ListElement); + + if (!Builder.isResponsibleForCreatingDeclaration(D)) { + // If this is not the last declarator in the declaration we expect a + // delimiter after it. + const auto *DelimiterToken = std::next(Builder.findToken(Range.getEnd())); + if (DelimiterToken->kind() == clang::tok::TokenKind::comma) + Builder.markChildToken(DelimiterToken, syntax::NodeRole::ListDelimiter); + } else { + auto *DL = new (allocator()) syntax::DeclaratorList; + auto DeclarationRange = Builder.getDeclarationRange(D); + Builder.foldList(DeclarationRange, DL, nullptr); + + Builder.markChild(DL, syntax::NodeRole::Declarators); + Builder.foldNode(DeclarationRange, new (allocator()) syntax::SimpleDeclaration, D); } return true; 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 @@ -136,6 +136,8 @@ return OS << "CallArguments"; case NodeKind::ParameterDeclarationList: return OS << "ParameterDeclarationList"; + case NodeKind::DeclaratorList: + return OS << "DeclaratorList"; } llvm_unreachable("unknown node kind"); } @@ -218,6 +220,8 @@ return OS << "Callee"; case syntax::NodeRole::Arguments: return OS << "Arguments"; + case syntax::NodeRole::Declarators: + return OS << "Declarators"; } llvm_unreachable("invalid role"); } @@ -291,6 +295,29 @@ return Children; } +std::vector +syntax::DeclaratorList::getDeclarators() { + auto DeclaratorsAsNodes = getElementsAsNodes(); + std::vector Children; + for (const auto &DeclaratorAsNode : DeclaratorsAsNodes) { + Children.push_back(llvm::cast(DeclaratorAsNode)); + } + return Children; +} + +std::vector> +syntax::DeclaratorList::getDeclaratorsAndCommas() { + auto DeclaratorsAsNodesAndCommas = getElementsAsNodesAndDelimiters(); + std::vector> + Children; + for (const auto &DeclaratorAsNodeAndComma : DeclaratorsAsNodesAndCommas) { + Children.push_back( + {llvm::cast(DeclaratorAsNodeAndComma.element), + DeclaratorAsNodeAndComma.delimiter}); + } + return Children; +} + syntax::Expression *syntax::MemberExpression::getObject() { return cast_or_null(findChild(syntax::NodeRole::Object)); } diff --git a/clang/lib/Tooling/Syntax/Synthesis.cpp b/clang/lib/Tooling/Syntax/Synthesis.cpp --- a/clang/lib/Tooling/Syntax/Synthesis.cpp +++ b/clang/lib/Tooling/Syntax/Synthesis.cpp @@ -183,6 +183,8 @@ return new (A.getAllocator()) syntax::CallArguments; case syntax::NodeKind::ParameterDeclarationList: return new (A.getAllocator()) syntax::ParameterDeclarationList; + case syntax::NodeKind::DeclaratorList: + return new (A.getAllocator()) syntax::DeclaratorList; } llvm_unreachable("unknown node kind"); } diff --git a/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp b/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp --- a/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/BuildTreeTest.cpp @@ -2875,21 +2875,23 @@ TranslationUnit Detached |-SimpleDeclaration | |-'int' -| |-SimpleDeclarator Declarator -| | |-'*' -| | `-'a' -| |-',' -| |-SimpleDeclarator Declarator -| | `-'b' +| |-DeclaratorList Declarators +| | |-SimpleDeclarator ListElement +| | | |-'*' +| | | `-'a' +| | |-',' ListDelimiter +| | `-SimpleDeclarator ListElement +| | `-'b' | `-';' `-SimpleDeclaration |-'int' - |-SimpleDeclarator Declarator - | |-'*' - | `-'c' - |-',' - |-SimpleDeclarator Declarator - | `-'d' + |-DeclaratorList Declarators + | |-SimpleDeclarator ListElement + | | |-'*' + | | `-'c' + | |-',' ListDelimiter + | `-SimpleDeclarator ListElement + | `-'d' `-';' )txt")); } @@ -2904,12 +2906,13 @@ `-SimpleDeclaration |-'typedef' |-'int' - |-SimpleDeclarator Declarator - | |-'*' - | `-'a' - |-',' - |-SimpleDeclarator Declarator - | `-'b' + |-DeclaratorList Declarators + | |-SimpleDeclarator ListElement + | | |-'*' + | | `-'a' + | |-',' ListDelimiter + | `-SimpleDeclarator ListElement + | `-'b' `-';' )txt")); } @@ -2926,33 +2929,36 @@ TranslationUnit Detached `-SimpleDeclaration |-'void' - |-SimpleDeclarator Declarator - | |-'foo' - | `-ParametersAndQualifiers - | |-'(' OpenParen - | `-')' CloseParen + |-DeclaratorList Declarators + | `-SimpleDeclarator ListElement + | |-'foo' + | `-ParametersAndQualifiers + | |-'(' OpenParen + | `-')' CloseParen `-CompoundStatement |-'{' OpenParen |-DeclarationStatement Statement | |-SimpleDeclaration | | |-'int' - | | |-SimpleDeclarator Declarator - | | | |-'*' - | | | `-'a' - | | |-',' - | | `-SimpleDeclarator Declarator - | | `-'b' + | | `-DeclaratorList Declarators + | | |-SimpleDeclarator ListElement + | | | |-'*' + | | | `-'a' + | | |-',' ListDelimiter + | | `-SimpleDeclarator ListElement + | | `-'b' | `-';' |-DeclarationStatement Statement | |-SimpleDeclaration | | |-'typedef' | | |-'int' - | | |-SimpleDeclarator Declarator - | | | |-'*' - | | | `-'ta' - | | |-',' - | | `-SimpleDeclarator Declarator - | | `-'tb' + | | `-DeclaratorList Declarators + | | |-SimpleDeclarator ListElement + | | | |-'*' + | | | `-'ta' + | | |-',' ListDelimiter + | | `-SimpleDeclarator ListElement + | | `-'tb' | `-';' `-'}' CloseParen )txt"));