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 @@ -60,7 +60,15 @@ // Declarations UnknownDeclaration, + EmptyDeclaration, + StaticAssertDeclaration, + LinkageSpecificationDeclaration, SimpleDeclaration, + NamespaceDefinition, + NamespaceAliasDefinition, + UsingNamespaceDirective, + UsingDeclaration, + TypeAliasDeclaration }; /// For debugging purposes. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, NodeKind K); @@ -91,7 +99,9 @@ IfStatement_elseStatement, ReturnStatement_value, ExpressionStatement_expression, - CompoundStatement_statement + CompoundStatement_statement, + StaticAssertDeclaration_condition, + StaticAssertDeclaration_message }; /// For debugging purposes. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, NodeRole R); @@ -311,7 +321,7 @@ Declaration(NodeKind K) : Tree(K) {} static bool classof(const Node *N) { return NodeKind::UnknownDeclaration <= N->kind() && - N->kind() <= NodeKind::SimpleDeclaration; + N->kind() <= NodeKind::TypeAliasDeclaration; } }; @@ -324,6 +334,38 @@ } }; +/// A semicolon in the top-level context. Does not declare anything. +class EmptyDeclaration final : public Declaration { +public: + EmptyDeclaration() : Declaration(NodeKind::EmptyDeclaration) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::EmptyDeclaration; + } +}; + +/// static_assert(, ) +/// static_assert() +class StaticAssertDeclaration final : public Declaration { +public: + StaticAssertDeclaration() : Declaration(NodeKind::StaticAssertDeclaration) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::StaticAssertDeclaration; + } + syntax::Expression *condition(); + syntax::Expression *message(); +}; + +/// extern declaration +/// extern { } +class LinkageSpecificationDeclaration final : public Declaration { +public: + LinkageSpecificationDeclaration() + : Declaration(NodeKind::LinkageSpecificationDeclaration) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::LinkageSpecificationDeclaration; + } +}; + /// Groups multiple declarators (e.g. variables, typedefs, etc.) together. All /// grouped declarators share the same declaration specifiers (e.g. 'int' or /// 'typedef'). @@ -334,6 +376,54 @@ return N->kind() == NodeKind::SimpleDeclaration; } }; + +/// namespace { } +class NamespaceDefinition final : public Declaration { +public: + NamespaceDefinition() : Declaration(NodeKind::NamespaceDefinition) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::NamespaceDefinition; + } +}; + +/// namespace = +class NamespaceAliasDefinition final : public Declaration { +public: + NamespaceAliasDefinition() + : Declaration(NodeKind::NamespaceAliasDefinition) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::NamespaceAliasDefinition; + } +}; + +/// using namespace +class UsingNamespaceDirective final : public Declaration { +public: + UsingNamespaceDirective() : Declaration(NodeKind::UsingNamespaceDirective) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::UsingNamespaceDirective; + } +}; + +/// using :: +/// using typename :: +class UsingDeclaration final : public Declaration { +public: + UsingDeclaration() : Declaration(NodeKind::UsingDeclaration) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::UsingDeclaration; + } +}; + +/// using = +class TypeAliasDeclaration final : public Declaration { +public: + TypeAliasDeclaration() : Declaration(NodeKind::TypeAliasDeclaration) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::TypeAliasDeclaration; + } +}; + } // 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 @@ -295,7 +295,7 @@ syntax::Arena &Arena; Forest Pending; - llvm::DenseSet DeclsWithoutSemicolons; + llvm::DenseSet DeclsWithoutSemicolons; }; namespace { @@ -397,6 +397,18 @@ return true; } + bool WalkUpFromNamespaceDecl(NamespaceDecl *S) { + auto Tokens = Builder.getRange(S); + if (Tokens.front().kind() == tok::coloncolon) { + // Handle nested namespace definitions. Those start at '::' token, e.g. + // namespace a^::b {} + // FIXME: build corresponding nodes for the name of this namespace. + return true; + } + Builder.foldNode(Tokens, new (allocator()) syntax::NamespaceDefinition); + 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. @@ -504,6 +516,64 @@ return true; } + bool WalkUpFromEmptyDecl(EmptyDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::EmptyDeclaration); + return true; + } + + bool WalkUpFromStaticAssertDecl(StaticAssertDecl *S) { + Builder.markExprChild(S->getAssertExpr(), + syntax::NodeRole::StaticAssertDeclaration_condition); + Builder.markExprChild(S->getMessage(), + syntax::NodeRole::StaticAssertDeclaration_message); + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::StaticAssertDeclaration); + return true; + } + + bool WalkUpFromLinkageSpecDecl(LinkageSpecDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::LinkageSpecificationDeclaration); + return true; + } + + bool WalkUpFromNamespaceAliasDecl(NamespaceAliasDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::NamespaceAliasDefinition); + return true; + } + + bool WalkUpFromUsingDirectiveDecl(UsingDirectiveDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::UsingNamespaceDirective); + return true; + } + + bool WalkUpFromUsingDecl(UsingDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::UsingDeclaration); + return true; + } + + bool WalkUpFromUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::UsingDeclaration); + return true; + } + + bool WalkUpFromUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::UsingDeclaration); + return true; + } + + bool WalkUpFromTypeAliasDecl(TypeAliasDecl *S) { + Builder.foldNode(Builder.getRange(S), + new (allocator()) syntax::TypeAliasDeclaration); + return true; + } + private: /// A small helper to save some typing. llvm::BumpPtrAllocator &allocator() { return Builder.allocator(); } @@ -553,6 +623,9 @@ } void syntax::TreeBuilder::markExprChild(Expr *Child, NodeRole Role) { + if (!Child) + return; + Pending.assignRole(getExprRange(Child), Role); } 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 @@ -50,8 +50,24 @@ return OS << "CompoundStatement"; case NodeKind::UnknownDeclaration: return OS << "UnknownDeclaration"; + case NodeKind::EmptyDeclaration: + return OS << "EmptyDeclaration"; + case NodeKind::StaticAssertDeclaration: + return OS << "StaticAssertDeclaration"; + case NodeKind::LinkageSpecificationDeclaration: + return OS << "LinkageSpecificationDeclaration"; case NodeKind::SimpleDeclaration: return OS << "SimpleDeclaration"; + case NodeKind::NamespaceDefinition: + return OS << "NamespaceDefinition"; + case NodeKind::NamespaceAliasDefinition: + return OS << "NamespaceAliasDefinition"; + case NodeKind::UsingNamespaceDirective: + return OS << "UsingNamespaceDirective"; + case NodeKind::UsingDeclaration: + return OS << "UsingDeclaration"; + case NodeKind::TypeAliasDeclaration: + return OS << "TypeAliasDeclaration"; } llvm_unreachable("unknown node kind"); } @@ -84,6 +100,10 @@ return OS << "ExpressionStatement_expression"; case syntax::NodeRole::CompoundStatement_statement: return OS << "CompoundStatement_statement"; + case syntax::NodeRole::StaticAssertDeclaration_condition: + return OS << "StaticAssertDeclaration_condition"; + case syntax::NodeRole::StaticAssertDeclaration_message: + return OS << "StaticAssertDeclaration_message"; } llvm_unreachable("invalid role"); } @@ -216,3 +236,13 @@ return llvm::cast_or_null( findChild(syntax::NodeRole::CloseParen)); } + +syntax::Expression *syntax::StaticAssertDeclaration::condition() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::StaticAssertDeclaration_condition)); +} + +syntax::Expression *syntax::StaticAssertDeclaration::message() { + return llvm::cast_or_null( + findChild(syntax::NodeRole::StaticAssertDeclaration_message)); +} 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 @@ -512,7 +512,190 @@ | | `-tb | `-; `-} - )txt"}}; + )txt"}, + {R"cpp( +namespace a { namespace b {} } +namespace a::b {} +namespace {} + +namespace foo = a; + )cpp", + R"txt( +*: TranslationUnit +|-NamespaceDefinition +| |-namespace +| |-a +| |-{ +| |-NamespaceDefinition +| | |-namespace +| | |-b +| | |-{ +| | `-} +| `-} +|-NamespaceDefinition +| |-namespace +| |-a +| |-:: +| |-b +| |-{ +| `-} +|-NamespaceDefinition +| |-namespace +| |-{ +| `-} +`-NamespaceAliasDefinition + |-namespace + |-foo + |-= + |-a + `-; +)txt"}, + {R"cpp( +namespace ns {} +using namespace ::ns; + )cpp", + R"txt( +*: TranslationUnit +|-NamespaceDefinition +| |-namespace +| |-ns +| |-{ +| `-} +`-UsingNamespaceDirective + |-using + |-namespace + |-:: + |-ns + `-; + )txt"}, + {R"cpp( +namespace ns { int a; } +using ns::a; + )cpp", + R"txt( +*: TranslationUnit +|-NamespaceDefinition +| |-namespace +| |-ns +| |-{ +| |-SimpleDeclaration +| | |-int +| | |-a +| | `-; +| `-} +`-UsingDeclaration + |-using + |-ns + |-:: + |-a + `-; + )txt"}, + {R"cpp( +template struct X { + using T::foo; + using typename T::bar; +}; + )cpp", + R"txt( +*: TranslationUnit +`-UnknownDeclaration + |-template + |-< + |-UnknownDeclaration + | |-class + | `-T + |-> + |-struct + |-X + |-{ + |-UsingDeclaration + | |-using + | |-T + | |-:: + | |-foo + | `-; + |-UsingDeclaration + | |-using + | |-typename + | |-T + | |-:: + | |-bar + | `-; + |-} + `-; + )txt"}, + {R"cpp( +using type = int; + )cpp", + R"txt( +*: TranslationUnit +`-TypeAliasDeclaration + |-using + |-type + |-= + |-int + `-; + )txt"}, + {R"cpp( +; + )cpp", + R"txt( +*: TranslationUnit +`-EmptyDeclaration + `-; + )txt"}, + {R"cpp( +static_assert(true, "message"); +static_assert(true); + )cpp", + R"txt( +*: TranslationUnit +|-StaticAssertDeclaration +| |-static_assert +| |-( +| |-UnknownExpression +| | `-true +| |-, +| |-UnknownExpression +| | `-"message" +| |-) +| `-; +`-StaticAssertDeclaration + |-static_assert + |-( + |-UnknownExpression + | `-true + |-) + `-; + )txt"}, + {R"cpp( +extern "C" int a; +extern "C" { int b; int c; } + )cpp", + R"txt( +*: TranslationUnit +|-LinkageSpecificationDeclaration +| |-extern +| |-"C" +| `-SimpleDeclaration +| |-int +| |-a +| `-; +`-LinkageSpecificationDeclaration + |-extern + |-"C" + |-{ + |-SimpleDeclaration + | |-int + | |-b + | `-; + |-SimpleDeclaration + | |-int + | |-c + | `-; + `-} + )txt"}, + }; for (const auto &T : Cases) { SCOPED_TRACE(T.first);