From be14a22b47e5c61ff36e4183dcb4f8b138466157 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 12 Dec 2019 08:04:21 +0100 Subject: [PATCH] [Syntax] Build nodes for simple cases of top level declarations Summary: More complicated nodes (e.g. template declarations) will be implemented in the follow-up patches. Reviewers: gribozavr2 Reviewed By: gribozavr2 Subscribers: merge_guards_bot, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D70856 --- clang/include/clang/Tooling/Syntax/Nodes.h | 94 +++++++++++++- clang/lib/Tooling/Syntax/BuildTree.cpp | 75 ++++++++++- clang/lib/Tooling/Syntax/Nodes.cpp | 30 +++++ clang/unittests/Tooling/Syntax/TreeTest.cpp | 185 +++++++++++++++++++++++++++- 4 files changed, 380 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h index c4db4da..25acc17 100644 --- a/clang/include/clang/Tooling/Syntax/Nodes.h +++ b/clang/include/clang/Tooling/Syntax/Nodes.h @@ -60,7 +60,15 @@ enum class NodeKind : uint16_t { // 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 @@ enum class NodeRole : uint8_t { 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 @@ public: 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 @@ public: } }; +/// 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 @@ public: 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 index 6708149..e13bb2d 100644 --- a/clang/lib/Tooling/Syntax/BuildTree.cpp +++ b/clang/lib/Tooling/Syntax/BuildTree.cpp @@ -295,7 +295,7 @@ private: syntax::Arena &Arena; Forest Pending; - llvm::DenseSet DeclsWithoutSemicolons; + llvm::DenseSet DeclsWithoutSemicolons; }; namespace { @@ -397,6 +397,18 @@ public: 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 @@ public: 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::markStmtChild(Stmt *Child, NodeRole Role) { } 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 index b2ed4ff..5b0c510 100644 --- a/clang/lib/Tooling/Syntax/Nodes.cpp +++ b/clang/lib/Tooling/Syntax/Nodes.cpp @@ -50,8 +50,24 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeKind K) { 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 @@ llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeRole R) { 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 @@ syntax::Leaf *syntax::CompoundStatement::rbrace() { 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 index 98b895a..e9c11c7 100644 --- a/clang/unittests/Tooling/Syntax/TreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -512,7 +512,190 @@ void foo() { | | `-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); -- 2.7.4