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 @@ -45,6 +45,7 @@ BinaryOperatorExpression, CxxNullPtrExpression, IntegerLiteralExpression, + IdExpression, // Statements. UnknownStatement, @@ -84,7 +85,10 @@ ArraySubscript, TrailingReturnType, ParametersAndQualifiers, - MemberPointer + MemberPointer, + NestedNameSpecifier, + NameSpecifier, + UnqualifiedId }; /// For debugging purposes. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, NodeKind K); @@ -187,6 +191,109 @@ } }; +/// A sequence of these specifiers makes a `nested-name-specifier`. +/// e.g. the `std::` or `vector::` in `std::vector::size`. +class NameSpecifier final : public Tree { +private: +public: + NameSpecifier() : Tree(NodeKind::NameSpecifier) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::NameSpecifier; + } + + // Same solution as in `unqualified-id` + NodeKind getChildKind(); + syntax::Leaf *getIdentifier(); + syntax::Leaf *getNamespaceName(); + //(...) + + // Should we create accessors to everything? + syntax::Leaf *doubleCommaToken(); +}; + +/// Models a `nested-name-specifier`. C++ [expr.prim.id.qual] +/// e.g. the `std::vector::` in `std::vector::size`. +class NestedNameSpecifier final : public Tree { +public: + NestedNameSpecifier() : Tree(NodeKind::NestedNameSpecifier) {} + static bool classof(const Node *N) { + return N->kind() <= NodeKind::NestedNameSpecifier; + } + std::vector specifiers(); +}; + +// To be implemented, not interesting for discussion +class OperatorFunctionId; // Models `operator-function-id`. +class Identifier; // Models `identifier`. +// (...) + +/// Models an `unqualified-id`, e.g. the `size` in `std::vector::size`. +// C++ [expr.prim.id.unqual] +// unqualified-id: +// identifier +// operator-function-id +// conversion-function-id +// literal-operator-id +// ~ type-name +// ~ decltype-specifier +// template-id +class UnqualifiedId final : public Tree { + // As {`identifier`, `operator-function-id`, ...} appear in other parts of the + // grammar - `template-id` appears almost everywhere - we implement them as + // standalone nodes instead of derived from `unqualified-id`. We encode the + // relation `unqualified-id` = {`identifier` OR `operator-function-id` OR ...} + // through a parent-child relation and to discover which child we have we + // check the `NodeKind` of the child. +private: +public: + UnqualifiedId() : Tree(NodeKind::UnqualifiedId) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::UnqualifiedId; + } + + // We use `getChildKind()` and use the returned `NodeKind` to access the + // appropriate variant via an accessor + NodeKind getChildKind(); + // Accessors + syntax::Identifier * + getIdentifier(); // Drawback: Identifier is merely a Token, as such we + // could use a `syntax::Leaf` instead. Alternative: Use + // NodeRole information as well + syntax::OperatorFunctionId *getOperatorFunctionId(); + // (...) +}; + +/// 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 +/// For simplicity we use: +/// id-expression: +/// nested-name-specifier_opt template_opt unqualified-id +/// That is a bit more general than the grammar rule and allows forbidden +/// syntax. That's ok, it is not our duty to verify the syntax of the program +class IdExpression final : public Expression { +public: + IdExpression() : Expression(NodeKind::IdExpression) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::IdExpression; + } + // `qualifier` and `templateKeyword` are optional children of + // id-expression whereas `id` is a required child. + // How to make this distinction clear both to the reader and to the compiler? + // For children that are: + // * required: use reference; optional: use pointer + // * required: reference; optional: optional reference + // We could also use attributes to document that a pointer is not null + syntax::NestedNameSpecifier *qualifier(); + syntax::Leaf *templateKeyword(); + + syntax::UnqualifiedId *id(); +}; + /// C++11 'nullptr' expression. class CxxNullPtrExpression final : public Expression { public: