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 @@ -135,22 +135,6 @@ static bool classof(const Node *N); }; -/// Models an `id-expression`, e.g. `std::vector::size`. -/// C++ [expr.prim.id] -/// id-expression: -/// unqualified-id -/// qualified-id -/// qualified-id: -/// nested-name-specifier template_opt unqualified-id -class IdExpression final : public Expression { -public: - IdExpression() : Expression(NodeKind::IdExpression) {} - static bool classof(const Node *N); - NestedNameSpecifier *getQualifier(); - Leaf *getTemplateKeyword(); - UnqualifiedId *getUnqualifiedId(); -}; - /// An expression of an unknown kind, i.e. one not currently handled by the /// syntax tree. class UnknownExpression final : public Expression { @@ -159,14 +143,6 @@ static bool classof(const Node *N); }; -/// Models a this expression `this`. C++ [expr.prim.this] -class ThisExpression final : public Expression { -public: - ThisExpression() : Expression(NodeKind::ThisExpression) {} - static bool classof(const Node *N); - Leaf *getThisKeyword(); -}; - /// Models arguments of a function call. /// call-arguments: /// delimited_list(expression, ',') @@ -180,49 +156,6 @@ std::vector> getArgumentsAndCommas(); }; -/// A function call. C++ [expr.call] -/// call-expression: -/// expression '(' call-arguments ')' -/// e.g `f(1, '2')` or `this->Base::f()` -class CallExpression final : public Expression { -public: - CallExpression() : Expression(NodeKind::CallExpression) {} - static bool classof(const Node *N); - Expression *getCallee(); - Leaf *getOpenParen(); - CallArguments *getArguments(); - Leaf *getCloseParen(); -}; - -/// Models a parenthesized expression `(E)`. C++ [expr.prim.paren] -/// e.g. `(3 + 2)` in `a = 1 + (3 + 2);` -class ParenExpression final : public Expression { -public: - ParenExpression() : Expression(NodeKind::ParenExpression) {} - static bool classof(const Node *N); - Leaf *getOpenParen(); - Expression *getSubExpression(); - Leaf *getCloseParen(); -}; - -/// Models a class member access. C++ [expr.ref] -/// member-expression: -/// expression -> template_opt id-expression -/// expression . template_opt id-expression -/// e.g. `x.a`, `xp->a` -/// -/// Note: An implicit member access inside a class, i.e. `a` instead of -/// `this->a`, is an `id-expression`. -class MemberExpression final : public Expression { -public: - MemberExpression() : Expression(NodeKind::MemberExpression) {} - static bool classof(const Node *N); - Expression *getObject(); - Leaf *getAccessToken(); - Leaf *getTemplateKeyword(); - IdExpression *getMember(); -}; - /// Expression for literals. C++ [lex.literal] class LiteralExpression : public Expression { public: diff --git a/clang/include/clang/Tooling/Syntax/Nodes.td b/clang/include/clang/Tooling/Syntax/Nodes.td --- a/clang/include/clang/Tooling/Syntax/Nodes.td +++ b/clang/include/clang/Tooling/Syntax/Nodes.td @@ -23,6 +23,15 @@ }]; } +def UnqualifiedId : External {} + +// Lists +def List : External {} +def DeclaratorList : External {} +def ParameterDeclarationList : External {} +def CallArguments : External {} +def NestedNameSpecifier : External {} + def Expression : Alternatives { let documentation = [{ A base class for all expressions. Note that expressions are not statements, @@ -34,7 +43,17 @@ def PrefixUnaryOperatorExpression : External {} def PostfixUnaryOperatorExpression : External {} def BinaryOperatorExpression : External {} -def ParenExpression : External {} +def ParenExpression : Sequence { + let documentation = [{ + Models a parenthesized expression `(E)`. C++ [expr.prim.paren] + e.g. `(3 + 2)` in `a = 1 + (3 + 2);` + }]; + let children = [ + Role<"OpenParen", Token<"l_paren">>, + Role<"SubExpression", Expression>, + Role<"CloseParen", Token<"r_paren">>, + ]; +} def LiteralExpression : External {} def IntegerLiteralExpression : External {} def CharacterLiteralExpression : External {} @@ -47,10 +66,62 @@ def FloatUserDefinedLiteralExpression : External {} def CharUserDefinedLiteralExpression : External {} def StringUserDefinedLiteralExpression : External {} -def IdExpression : External {} -def MemberExpression : External {} -def ThisExpression : External {} -def CallExpression : External {} +def IdExpression : Sequence { + let documentation = [{ + Models an `id-expression`, e.g. `std::vector::size`. + C++ [expr.prim.id] + id-expression: + unqualified-id + qualified-id + qualified-id: + nested-name-specifier template_opt unqualified-id + }]; + let children = [ + Role<"Qualifier", Optional>, + Role<"TemplateKeyword", Optional>>, + Role<"UnqualifiedId", UnqualifiedId>, + ]; +} +def MemberExpression : Sequence { + let documentation = [{ + Models a class member access. C++ [expr.ref] + member-expression: + expression -> template_opt id-expression + expression . template_opt id-expression + e.g. `x.a`, `xp->a` + + Note: An implicit member access inside a class, i.e. `a` instead of + `this->a`, is an `id-expression`. + }]; + let children = [ + Role<"Object", Expression>, + Role<"AccessToken", AnyToken<["period","arrow"]>>, + Role<"TemplateKeyword", Optional>>, + Role<"Member", IdExpression>, + ]; +} +def ThisExpression : Sequence { + let documentation = [{ + Models a this expression `this`. C++ [expr.prim.this] + }]; + let children = [ + Role<"IntroducerKeyword", Keyword<"this">>, + ]; +} +def CallExpression : Sequence { + let documentation = [{ + A function call. C++ [expr.call] + call-expression: + expression '(' call-arguments ')' + e.g `f(1, '2')` or `this->Base::f()` + }]; + let children = [ + Role<"Callee", Expression>, + Role<"OpenParen", Token<"l_paren">>, + Role<"Arguments", CallArguments>, + Role<"CloseParen", Token<"r_paren">>, + ]; +} // Statements. def Statement : External {} @@ -94,14 +165,6 @@ def TrailingReturnType : External {} def ParametersAndQualifiers : External {} def MemberPointer : External {} -def UnqualifiedId : External {} - -// Lists -def List : External {} -def DeclaratorList : External {} -def ParameterDeclarationList : External {} -def CallArguments : External {} -def NestedNameSpecifier : External {} // Name Specifiers. def NameSpecifier : Alternatives { diff --git a/clang/include/clang/Tooling/Syntax/Syntax.td b/clang/include/clang/Tooling/Syntax/Syntax.td --- a/clang/include/clang/Tooling/Syntax/Syntax.td +++ b/clang/include/clang/Tooling/Syntax/Syntax.td @@ -27,8 +27,16 @@ // //===----------------------------------------------------------------------===// +// Syntax is any constraint on constructs that can appear somewhere. +class Syntax; +class Optional : Syntax { Syntax inner = inner_; } +class AnyToken kinds_> : Syntax { list kinds = kinds_; } +class Token : AnyToken<[kind_]>; +class Keyword : Token; + // Defs derived from NodeType correspond to syntax tree node types. -class NodeType { +// NodeType is also a syntax constraint: one node of this type. +class NodeType : Syntax { // The NodeType that this node is derived from in the Node class hierarchy. NodeType base = ?; // Documentation for this Node subclass. @@ -55,4 +63,23 @@ // These are generally placeholders for a more precise implementation. class Unconstrained : NodeType { let base = base_; } -// FIXME: add sequence and list archetypes. +class Role { + string role = role_; + Syntax syntax = syntax_; +} + +// A node which contains a fixed sequence of children in a particular order. +// +// Each child is characterized by a role (unique within the sequence), and +// has an allowed base type for the node. +// The role sequence and role/type match are enforced invariants of the class. +// +// We also record whether the child is required to be present, and which tokens +// are permitted (for Leaf nodes). These invariants are not enforced. +class Sequence : NodeType { + let base = base_; + // Children must be Role or have a default role derived from the NodeType. + list children; +} + +// FIXME: add list archetype. 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 @@ -196,58 +196,6 @@ return Children; } -syntax::Expression *syntax::MemberExpression::getObject() { - return cast_or_null(findChild(syntax::NodeRole::Object)); -} - -syntax::Leaf *syntax::MemberExpression::getTemplateKeyword() { - return llvm::cast_or_null( - findChild(syntax::NodeRole::TemplateKeyword)); -} - -syntax::Leaf *syntax::MemberExpression::getAccessToken() { - return llvm::cast_or_null( - findChild(syntax::NodeRole::AccessToken)); -} - -syntax::IdExpression *syntax::MemberExpression::getMember() { - return cast_or_null( - findChild(syntax::NodeRole::Member)); -} - -syntax::NestedNameSpecifier *syntax::IdExpression::getQualifier() { - return cast_or_null( - findChild(syntax::NodeRole::Qualifier)); -} - -syntax::Leaf *syntax::IdExpression::getTemplateKeyword() { - return llvm::cast_or_null( - findChild(syntax::NodeRole::TemplateKeyword)); -} - -syntax::UnqualifiedId *syntax::IdExpression::getUnqualifiedId() { - return cast_or_null( - findChild(syntax::NodeRole::UnqualifiedId)); -} - -syntax::Leaf *syntax::ParenExpression::getOpenParen() { - return cast_or_null(findChild(syntax::NodeRole::OpenParen)); -} - -syntax::Expression *syntax::ParenExpression::getSubExpression() { - return cast_or_null( - findChild(syntax::NodeRole::SubExpression)); -} - -syntax::Leaf *syntax::ParenExpression::getCloseParen() { - return cast_or_null(findChild(syntax::NodeRole::CloseParen)); -} - -syntax::Leaf *syntax::ThisExpression::getThisKeyword() { - return cast_or_null( - findChild(syntax::NodeRole::IntroducerKeyword)); -} - syntax::Leaf *syntax::LiteralExpression::getLiteralToken() { return cast_or_null(findChild(syntax::NodeRole::LiteralToken)); } @@ -274,23 +222,6 @@ findChild(syntax::NodeRole::RightHandSide)); } -syntax::Expression *syntax::CallExpression::getCallee() { - return cast_or_null(findChild(syntax::NodeRole::Callee)); -} - -syntax::Leaf *syntax::CallExpression::getOpenParen() { - return cast_or_null(findChild(syntax::NodeRole::OpenParen)); -} - -syntax::CallArguments *syntax::CallExpression::getArguments() { - return cast_or_null( - findChild(syntax::NodeRole::Arguments)); -} - -syntax::Leaf *syntax::CallExpression::getCloseParen() { - return cast_or_null(findChild(syntax::NodeRole::CloseParen)); -} - syntax::Leaf *syntax::SwitchStatement::getSwitchKeyword() { return cast_or_null( findChild(syntax::NodeRole::IntroducerKeyword)); diff --git a/clang/utils/TableGen/ClangSyntaxEmitter.cpp b/clang/utils/TableGen/ClangSyntaxEmitter.cpp --- a/clang/utils/TableGen/ClangSyntaxEmitter.cpp +++ b/clang/utils/TableGen/ClangSyntaxEmitter.cpp @@ -108,6 +108,23 @@ return N.Derived.empty() ? N : lastConcrete(*N.Derived.back()); } +struct SyntaxConstraint { + SyntaxConstraint(const llvm::Record &R) { + if (R.isSubClassOf("Optional")) { + *this = SyntaxConstraint(*R.getValueAsDef("inner")); + } else if (R.isSubClassOf("AnyToken")) { + NodeType = "Leaf"; + } else if (R.isSubClassOf("NodeType")) { + NodeType = R.getName().str(); + } else { + assert(false && "Unhandled Syntax kind"); + } + } + + std::string NodeType; + // optional and leaf types also go here, once we want to use them. +}; + } // namespace void clang::EmitClangSyntaxNodeList(llvm::RecordKeeper &Records, @@ -196,6 +213,21 @@ OS << formatv("protected:\n {0}(NodeKind K) : {1}(K) {{}\npublic:\n", N.name(), N.Base->name()); + if (N.Record->isSubClassOf("Sequence")) { + // Getters for sequence elements. + for (const auto &C : N.Record->getValueAsListOfDefs("children")) { + assert(C->isSubClassOf("Role")); + llvm::StringRef Role = C->getValueAsString("role"); + SyntaxConstraint Constraint(*C->getValueAsDef("syntax")); + for (const char *Const : {"", "const "}) + OS << formatv( + " {2}{1} *get{0}() {2} {{\n" + " return llvm::cast_or_null<{1}>(findChild(NodeRole::{0}));\n" + " }\n", + Role, Constraint.NodeType, Const); + } + } + // classof. FIXME: move definition inline once ~all nodes are generated. OS << " static bool classof(const Node *N);\n";