[SyntaxTree] Use simplified grammar rule for `NestedNameSpecifier` grammar nodes

This is our grammar rule for nested-name-specifiers:
globalbal-specifier:
  /*empty*/
simple-template-specifier:
  template_opt simple-template-id
name-specifier:
  global-specifier
  decltype-specifier
  identifier
  simple-template-specifier
nested-name-specifier:
  list(name-specifier, ::, non-empty, terminated)

It is a relaxed version of C++ [expr.prim.id] and quite simpler to map to our API.

TODO: refine name specifiers, `simple-template-name-specifier` and
decltype-name-specifier` are token soup for now.
This commit is contained in:
Eduardo Caldas 2020-08-04 17:33:36 +00:00
parent 25367dfefb
commit 8abb5fb68f
4 changed files with 411 additions and 144 deletions

View File

@ -95,9 +95,13 @@ enum class NodeKind : uint16_t {
TrailingReturnType,
ParametersAndQualifiers,
MemberPointer,
UnqualifiedId,
// Nested Name Specifiers.
NestedNameSpecifier,
NameSpecifier,
UnqualifiedId
GlobalNameSpecifier,
DecltypeNameSpecifier,
IdentifierNameSpecifier,
SimpleTemplateNameSpecifier
};
/// For debugging purposes.
raw_ostream &operator<<(raw_ostream &OS, NodeKind K);
@ -138,6 +142,7 @@ enum class NodeRole : uint8_t {
/// Tokens or Keywords
ArrowToken,
ExternKeyword,
TemplateKeyword,
/// An inner statement for those that have only a single child of kind
/// statement, e.g. loop body for while, for, etc; inner statement for case,
/// default, etc.
@ -167,6 +172,7 @@ enum class NodeRole : uint8_t {
IdExpression_id,
IdExpression_qualifier,
NestedNameSpecifier_specifier,
NestedNameSpecifier_delimiter,
ParenExpression_subExpression
};
/// For debugging purposes.
@ -195,12 +201,60 @@ public:
};
/// A sequence of these specifiers make a `nested-name-specifier`.
/// e.g. the `std::` or `vector<int>::` in `std::vector<int>::size`.
class NameSpecifier final : public Tree {
/// e.g. the `std` or `vector<int>` in `std::vector<int>::size`.
class NameSpecifier : public Tree {
public:
NameSpecifier() : Tree(NodeKind::NameSpecifier) {}
NameSpecifier(NodeKind K) : Tree(K) {}
static bool classof(const Node *N) {
return N->kind() == NodeKind::NameSpecifier;
return N->kind() == NodeKind::GlobalNameSpecifier ||
N->kind() == NodeKind::DecltypeNameSpecifier ||
N->kind() == NodeKind::IdentifierNameSpecifier ||
N->kind() == NodeKind::SimpleTemplateNameSpecifier;
}
};
/// The global namespace name specifier, this specifier doesn't correspond to a
/// token instead an absence of tokens before a `::` characterizes it, in
/// `::std::vector<int>` it would be characterized by the absence of a token
/// before the first `::`
class GlobalNameSpecifier final : public NameSpecifier {
public:
GlobalNameSpecifier() : NameSpecifier(NodeKind::GlobalNameSpecifier) {}
static bool classof(const Node *N) {
return N->kind() == NodeKind::GlobalNameSpecifier;
}
};
/// A name specifier holding a decltype, of the form: `decltype ( expression ) `
/// e.g. the `decltype(s)` in `decltype(s)::size`.
class DecltypeNameSpecifier final : public NameSpecifier {
public:
DecltypeNameSpecifier() : NameSpecifier(NodeKind::DecltypeNameSpecifier) {}
static bool classof(const Node *N) {
return N->kind() == NodeKind::DecltypeNameSpecifier;
}
};
/// A identifier name specifier, of the form `identifier`
/// e.g. the `std` in `std::vector<int>::size`.
class IdentifierNameSpecifier final : public NameSpecifier {
public:
IdentifierNameSpecifier()
: NameSpecifier(NodeKind::IdentifierNameSpecifier) {}
static bool classof(const Node *N) {
return N->kind() == NodeKind::IdentifierNameSpecifier;
}
};
/// A name specifier with a simple-template-id, of the form `template_opt
/// identifier < template-args >` e.g. the `vector<int>` in
/// `std::vector<int>::size`.
class SimpleTemplateNameSpecifier final : public NameSpecifier {
public:
SimpleTemplateNameSpecifier()
: NameSpecifier(NodeKind::SimpleTemplateNameSpecifier) {}
static bool classof(const Node *N) {
return N->kind() == NodeKind::SimpleTemplateNameSpecifier;
}
};
@ -213,6 +267,7 @@ public:
return N->kind() <= NodeKind::NestedNameSpecifier;
}
std::vector<NameSpecifier *> specifiers();
std::vector<Leaf *> delimiters();
};
/// Models an `unqualified-id`. C++ [expr.prim.id.unqual]
@ -239,8 +294,7 @@ public:
return N->kind() == NodeKind::IdExpression;
}
NestedNameSpecifier *qualifier();
// TODO after expose `id-expression` from `DependentScopeDeclRefExpr`:
// Add accessor for `template_opt`.
Leaf *templateKeyword();
UnqualifiedId *unqualifiedId();
};

View File

@ -286,6 +286,11 @@ public:
foldNode(Range, New, nullptr);
}
void foldNode(ArrayRef<syntax::Token> Range, syntax::Tree *New,
NestedNameSpecifierLoc L) {
// FIXME: add mapping for NestedNameSpecifierLoc
foldNode(Range, New, nullptr);
}
/// Notifies that we should not consume trailing semicolon when computing
/// token range of \p D.
void noticeDeclWithoutSemicolon(Decl *D);
@ -690,21 +695,6 @@ public:
return true;
}
syntax::NestedNameSpecifier *
BuildNestedNameSpecifier(NestedNameSpecifierLoc QualifierLoc) {
if (!QualifierLoc)
return nullptr;
for (auto it = QualifierLoc; it; it = it.getPrefix()) {
auto *NS = new (allocator()) syntax::NameSpecifier;
Builder.foldNode(Builder.getRange(it.getLocalSourceRange()), NS, nullptr);
Builder.markChild(NS, syntax::NodeRole::NestedNameSpecifier_specifier);
}
auto *NNS = new (allocator()) syntax::NestedNameSpecifier;
Builder.foldNode(Builder.getRange(QualifierLoc.getSourceRange()), NNS,
nullptr);
return NNS;
}
bool TraverseUserDefinedLiteral(UserDefinedLiteral *S) {
// The semantic AST node `UserDefinedLiteral` (UDL) may have one child node
// referencing the location of the UDL suffix (`_w` in `1.2_w`). The
@ -754,23 +744,118 @@ public:
return true;
}
syntax::NameSpecifier *BuildNameSpecifier(const NestedNameSpecifier &NNS) {
switch (NNS.getKind()) {
case NestedNameSpecifier::Global:
return new (allocator()) syntax::GlobalNameSpecifier;
case NestedNameSpecifier::Namespace:
case NestedNameSpecifier::NamespaceAlias:
case NestedNameSpecifier::Identifier:
return new (allocator()) syntax::IdentifierNameSpecifier;
case NestedNameSpecifier::TypeSpecWithTemplate:
return new (allocator()) syntax::SimpleTemplateNameSpecifier;
case NestedNameSpecifier::TypeSpec: {
const auto *NNSType = NNS.getAsType();
assert(NNSType);
if (isa<DecltypeType>(NNSType))
return new (allocator()) syntax::DecltypeNameSpecifier;
if (isa<TemplateSpecializationType, DependentTemplateSpecializationType>(
NNSType))
return new (allocator()) syntax::SimpleTemplateNameSpecifier;
return new (allocator()) syntax::IdentifierNameSpecifier;
}
case NestedNameSpecifier::Super:
// FIXME: Support Microsoft's __super
llvm::report_fatal_error("We don't yet support the __super specifier",
true);
}
}
// FIXME: Fix `NestedNameSpecifierLoc::getLocalSourceRange` for the
// `DependentTemplateSpecializationType` case.
/// Given a nested-name-specifier return the range for the last name specifier
///
/// e.g. `std::T::template X<U>::` => `template X<U>::`
SourceRange getLocalSourceRange(const NestedNameSpecifierLoc &NNSLoc) {
auto SR = NNSLoc.getLocalSourceRange();
// The method `NestedNameSpecifierLoc::getLocalSourceRange` *should* return
// the desired `SourceRange`, but there is a corner
// case. For a `DependentTemplateSpecializationType` this method returns its
// qualifiers as well, in other words in the example above this method
// returns `T::template X<U>::` instead of only `template X<U>::`
if (auto TL = NNSLoc.getTypeLoc()) {
if (auto DependentTL =
TL.getAs<DependentTemplateSpecializationTypeLoc>()) {
// The 'template' keyword is always present in dependent template
// specializations. Except in the case of incorrect code
// TODO: Treat the case of incorrect code.
SR.setBegin(DependentTL.getTemplateKeywordLoc());
}
}
return SR;
}
syntax::NestedNameSpecifier *
BuildNestedNameSpecifier(const NestedNameSpecifierLoc &QualifierLoc) {
if (!QualifierLoc)
return nullptr;
for (auto it = QualifierLoc; it; it = it.getPrefix()) {
assert(it.hasQualifier());
auto *NS = BuildNameSpecifier(*it.getNestedNameSpecifier());
assert(NS);
if (!isa<syntax::GlobalNameSpecifier>(NS))
Builder.foldNode(Builder.getRange(getLocalSourceRange(it)).drop_back(),
NS, it);
Builder.markChild(NS, syntax::NodeRole::NestedNameSpecifier_specifier);
Builder.markChildToken(it.getEndLoc(),
syntax::NodeRole::NestedNameSpecifier_delimiter);
}
auto *NNS = new (allocator()) syntax::NestedNameSpecifier;
Builder.foldNode(Builder.getRange(QualifierLoc.getSourceRange()), NNS,
QualifierLoc);
return NNS;
}
bool WalkUpFromDeclRefExpr(DeclRefExpr *S) {
if (auto *NNS = BuildNestedNameSpecifier(S->getQualifierLoc()))
Builder.markChild(NNS, syntax::NodeRole::IdExpression_qualifier);
auto *Qualifier = BuildNestedNameSpecifier(S->getQualifierLoc());
if (Qualifier)
Builder.markChild(Qualifier, syntax::NodeRole::IdExpression_qualifier);
auto TemplateKeywordLoc = S->getTemplateKeywordLoc();
if (TemplateKeywordLoc.isValid())
Builder.markChildToken(TemplateKeywordLoc,
syntax::NodeRole::TemplateKeyword);
auto *unqualifiedId = new (allocator()) syntax::UnqualifiedId;
// Get `UnqualifiedId` from `DeclRefExpr`.
// FIXME: Extract this logic so that it can be used by `MemberExpr`,
// and other semantic constructs, now it is tied to `DeclRefExpr`.
if (!S->hasExplicitTemplateArgs()) {
Builder.foldNode(Builder.getRange(S->getNameInfo().getSourceRange()),
unqualifiedId, nullptr);
} else {
auto templateIdSourceRange =
SourceRange(S->getNameInfo().getBeginLoc(), S->getRAngleLoc());
Builder.foldNode(Builder.getRange(templateIdSourceRange), unqualifiedId,
nullptr);
}
Builder.foldNode(Builder.getRange(S->getLocation(), S->getEndLoc()),
unqualifiedId, nullptr);
Builder.markChild(unqualifiedId, syntax::NodeRole::IdExpression_id);
Builder.foldNode(Builder.getExprRange(S),
new (allocator()) syntax::IdExpression, S);
return true;
}
// Same logic as DeclRefExpr.
bool WalkUpFromDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) {
auto *Qualifier = BuildNestedNameSpecifier(S->getQualifierLoc());
if (Qualifier)
Builder.markChild(Qualifier, syntax::NodeRole::IdExpression_qualifier);
auto TemplateKeywordLoc = S->getTemplateKeywordLoc();
if (TemplateKeywordLoc.isValid())
Builder.markChildToken(TemplateKeywordLoc,
syntax::NodeRole::TemplateKeyword);
auto *unqualifiedId = new (allocator()) syntax::UnqualifiedId;
Builder.foldNode(Builder.getRange(S->getLocation(), S->getEndLoc()),
unqualifiedId, nullptr);
Builder.markChild(unqualifiedId, syntax::NodeRole::IdExpression_id);
Builder.foldNode(Builder.getExprRange(S),

View File

@ -116,8 +116,14 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeKind K) {
return OS << "ParametersAndQualifiers";
case NodeKind::MemberPointer:
return OS << "MemberPointer";
case NodeKind::NameSpecifier:
return OS << "NameSpecifier";
case NodeKind::GlobalNameSpecifier:
return OS << "GlobalNameSpecifier";
case NodeKind::DecltypeNameSpecifier:
return OS << "DecltypeNameSpecifier";
case NodeKind::IdentifierNameSpecifier:
return OS << "IdentifierNameSpecifier";
case NodeKind::SimpleTemplateNameSpecifier:
return OS << "SimpleTemplateNameSpecifier";
case NodeKind::NestedNameSpecifier:
return OS << "NestedNameSpecifier";
}
@ -142,6 +148,8 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
return OS << "ArrowToken";
case syntax::NodeRole::ExternKeyword:
return OS << "ExternKeyword";
case syntax::NodeRole::TemplateKeyword:
return OS << "TemplateKeyword";
case syntax::NodeRole::BodyStatement:
return OS << "BodyStatement";
case syntax::NodeRole::CaseStatement_value:
@ -190,12 +198,23 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
return OS << "IdExpression_qualifier";
case syntax::NodeRole::NestedNameSpecifier_specifier:
return OS << "NestedNameSpecifier_specifier";
case syntax::NodeRole::NestedNameSpecifier_delimiter:
return OS << "NestedNameSpecifier_delimiter";
case syntax::NodeRole::ParenExpression_subExpression:
return OS << "ParenExpression_subExpression";
}
llvm_unreachable("invalid role");
}
std::vector<syntax::Leaf *> syntax::NestedNameSpecifier::delimiters() {
std::vector<syntax::Leaf *> Children;
for (auto *C = firstChild(); C; C = C->nextSibling()) {
assert(C->role() == syntax::NodeRole::NestedNameSpecifier_delimiter);
Children.push_back(llvm::cast<syntax::Leaf>(C));
}
return Children;
}
std::vector<syntax::NameSpecifier *> syntax::NestedNameSpecifier::specifiers() {
std::vector<syntax::NameSpecifier *> Children;
for (auto *C = firstChild(); C; C = C->nextSibling()) {
@ -210,6 +229,11 @@ syntax::NestedNameSpecifier *syntax::IdExpression::qualifier() {
findChild(syntax::NodeRole::IdExpression_qualifier));
}
syntax::Leaf *syntax::IdExpression::templateKeyword() {
return llvm::cast_or_null<syntax::Leaf>(
findChild(syntax::NodeRole::TemplateKeyword));
}
syntax::UnqualifiedId *syntax::IdExpression::unqualifiedId() {
return cast_or_null<syntax::UnqualifiedId>(
findChild(syntax::NodeRole::IdExpression_id));

View File

@ -873,24 +873,47 @@ TEST_P(SyntaxTreeTest, QualifiedId) {
}
EXPECT_TRUE(treeDumpEqual(
R"cpp(
namespace a {
namespace n {
struct S {
template<typename T>
static T f(){}
struct ST {
static void f();
};
};
}
template<typename T>
struct ST {
struct S {
template<typename U>
static U f();
};
};
void test() {
:: // global-namespace-specifier
a:: // namespace-specifier
S:: // type-name-specifier
:: // global-namespace-specifier
n:: // namespace-specifier
S:: // type-name-specifier
template ST<int>:: // type-template-instantiation-specifier
f();
n:: // namespace-specifier
S:: // type-name-specifier
ST<int>:: // type-template-instantiation-specifier
f();
ST<int>:: // type-name-specifier
S:: // type-name-specifier
f<int>();
ST<int>:: // type-name-specifier
S:: // type-name-specifier
template f<int>();
}
)cpp",
R"txt(
*: TranslationUnit
|-NamespaceDefinition
| |-namespace
| |-a
| |-n
| |-{
| |-SimpleDeclaration
| | |-struct
@ -904,19 +927,58 @@ void test() {
| | | | `-T
| | | |->
| | | `-SimpleDeclaration
| | | |-static
| | | |-T
| | | |-SimpleDeclarator
| | | | |-f
| | | | `-ParametersAndQualifiers
| | | | |-(
| | | | `-)
| | | `-CompoundStatement
| | | |-{
| | | `-}
| | | |-struct
| | | |-ST
| | | |-{
| | | |-SimpleDeclaration
| | | | |-static
| | | | |-void
| | | | |-SimpleDeclarator
| | | | | |-f
| | | | | `-ParametersAndQualifiers
| | | | | |-(
| | | | | `-)
| | | | `-;
| | | |-}
| | | `-;
| | |-}
| | `-;
| `-}
|-TemplateDeclaration
| |-template
| |-<
| |-UnknownDeclaration
| | |-typename
| | `-T
| |->
| `-SimpleDeclaration
| |-struct
| |-ST
| |-{
| |-SimpleDeclaration
| | |-struct
| | |-S
| | |-{
| | |-TemplateDeclaration
| | | |-template
| | | |-<
| | | |-UnknownDeclaration
| | | | |-typename
| | | | `-U
| | | |->
| | | `-SimpleDeclaration
| | | |-static
| | | |-U
| | | |-SimpleDeclarator
| | | | |-f
| | | | `-ParametersAndQualifiers
| | | | |-(
| | | | `-)
| | | `-;
| | |-}
| | `-;
| |-}
| `-;
`-SimpleDeclaration
|-void
|-SimpleDeclarator
@ -930,14 +992,81 @@ void test() {
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-NameSpecifier
| | | | | `-::
| | | | |-NameSpecifier
| | | | | |-a
| | | | | `-::
| | | | `-NameSpecifier
| | | | |-S
| | | | `-::
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-n
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-S
| | | | |-::
| | | | |-SimpleTemplateNameSpecifier
| | | | | |-template
| | | | | |-ST
| | | | | |-<
| | | | | |-int
| | | | | `->
| | | | `-::
| | | `-UnqualifiedId
| | | `-f
| | |-(
| | `-)
| `-;
|-ExpressionStatement
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-IdentifierNameSpecifier
| | | | | `-n
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-S
| | | | |-::
| | | | |-SimpleTemplateNameSpecifier
| | | | | |-ST
| | | | | |-<
| | | | | |-int
| | | | | `->
| | | | `-::
| | | `-UnqualifiedId
| | | `-f
| | |-(
| | `-)
| `-;
|-ExpressionStatement
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-SimpleTemplateNameSpecifier
| | | | | |-ST
| | | | | |-<
| | | | | |-int
| | | | | `->
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-S
| | | | `-::
| | | `-UnqualifiedId
| | | |-f
| | | |-<
| | | |-int
| | | `->
| | |-(
| | `-)
| `-;
|-ExpressionStatement
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-SimpleTemplateNameSpecifier
| | | | | |-ST
| | | | | |-<
| | | | | |-int
| | | | | `->
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-S
| | | | `-::
| | | |-template
| | | `-UnqualifiedId
| | | |-f
| | | |-<
@ -950,7 +1079,7 @@ void test() {
)txt"));
}
TEST_P(SyntaxTreeTest, QualifiedIdWithTemplateKeyword) {
TEST_P(SyntaxTreeTest, QualifiedIdWithDependentType) {
if (!GetParam().isCXX()) {
return;
}
@ -961,63 +1090,17 @@ TEST_P(SyntaxTreeTest, QualifiedIdWithTemplateKeyword) {
}
EXPECT_TRUE(treeDumpEqual(
R"cpp(
struct X {
template<int> static void f();
template<int>
struct Y {
static void f();
};
};
template<typename T> void test() {
// TODO: Expose `id-expression` from `DependentScopeDeclRefExpr`
T::template f<0>(); // nested-name-specifier template unqualified-id
T::template Y<0>::f(); // nested-name-specifier template :: unqualified-id
template <typename T>
void test() {
T::template U<int>::f();
T::U::f();
T::template f<0>();
}
)cpp",
R"txt(
*: TranslationUnit
|-SimpleDeclaration
| |-struct
| |-X
| |-{
| |-TemplateDeclaration
| | |-template
| | |-<
| | |-SimpleDeclaration
| | | `-int
| | |->
| | `-SimpleDeclaration
| | |-static
| | |-void
| | |-SimpleDeclarator
| | | |-f
| | | `-ParametersAndQualifiers
| | | |-(
| | | `-)
| | `-;
| |-TemplateDeclaration
| | |-template
| | |-<
| | |-SimpleDeclaration
| | | `-int
| | |->
| | `-SimpleDeclaration
| | |-struct
| | |-Y
| | |-{
| | |-SimpleDeclaration
| | | |-static
| | | |-void
| | | |-SimpleDeclarator
| | | | |-f
| | | | `-ParametersAndQualifiers
| | | | |-(
| | | | `-)
| | | `-;
| | |-}
| | `-;
| |-}
| `-;
`-TemplateDeclaration
|-template
|-<
@ -1036,31 +1119,52 @@ template<typename T> void test() {
|-{
|-ExpressionStatement
| |-UnknownExpression
| | |-UnknownExpression
| | | |-T
| | | |-::
| | | |-template
| | | |-f
| | | |-<
| | | |-IntegerLiteralExpression
| | | | `-0
| | | `->
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-IdentifierNameSpecifier
| | | | | `-T
| | | | |-::
| | | | |-SimpleTemplateNameSpecifier
| | | | | |-template
| | | | | |-U
| | | | | |-<
| | | | | |-int
| | | | | `->
| | | | `-::
| | | `-UnqualifiedId
| | | `-f
| | |-(
| | `-)
| `-;
|-ExpressionStatement
| |-UnknownExpression
| | |-UnknownExpression
| | | |-T
| | | |-::
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-IdentifierNameSpecifier
| | | | | `-T
| | | | |-::
| | | | |-IdentifierNameSpecifier
| | | | | `-U
| | | | `-::
| | | `-UnqualifiedId
| | | `-f
| | |-(
| | `-)
| `-;
|-ExpressionStatement
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | |-IdentifierNameSpecifier
| | | | | `-T
| | | | `-::
| | | |-template
| | | |-Y
| | | |-<
| | | |-IntegerLiteralExpression
| | | | `-0
| | | |->
| | | |-::
| | | `-f
| | | `-UnqualifiedId
| | | |-f
| | | |-<
| | | |-IntegerLiteralExpression
| | | | `-0
| | | `->
| | |-(
| | `-)
| `-;
@ -1118,14 +1222,14 @@ void test(S s) {
| |-UnknownExpression
| | |-IdExpression
| | | |-NestedNameSpecifier
| | | | `-NameSpecifier
| | | | |-decltype
| | | | |-(
| | | | |-IdExpression
| | | | | `-UnqualifiedId
| | | | | `-s
| | | | |-)
| | | | `-::
| | | | |-DecltypeNameSpecifier
| | | | | |-decltype
| | | | | |-(
| | | | | |-IdExpression
| | | | | | `-UnqualifiedId
| | | | | | `-s
| | | | | `-)
| | | | `-::
| | | `-UnqualifiedId
| | | `-f
| | |-(