[SyntaxTree] Add support for `MemberExpression`

Differential Revision: https://reviews.llvm.org/D86227
This commit is contained in:
Eduardo Caldas 2020-08-19 16:43:33 +00:00
parent 7f00938975
commit ba32915db2
4 changed files with 480 additions and 12 deletions

View File

@ -55,6 +55,7 @@ enum class NodeKind : uint16_t {
CharUserDefinedLiteralExpression,
StringUserDefinedLiteralExpression,
IdExpression,
MemberExpression,
// Statements.
UnknownStatement,
@ -173,7 +174,10 @@ enum class NodeRole : uint8_t {
ParametersAndQualifiers_trailingReturn,
IdExpression_id,
IdExpression_qualifier,
ParenExpression_subExpression
ParenExpression_subExpression,
MemberExpression_object,
MemberExpression_accessToken,
MemberExpression_member,
};
/// For debugging purposes.
raw_ostream &operator<<(raw_ostream &OS, NodeRole R);
@ -322,6 +326,26 @@ public:
Leaf *closeParen();
};
/// 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) {
return N->kind() == NodeKind::MemberExpression;
}
Expression *object();
Leaf *accessToken();
Leaf *templateKeyword();
IdExpression *member();
};
/// Expression for literals. C++ [lex.literal]
class LiteralExpression : public Expression {
public:

View File

@ -881,6 +881,49 @@ public:
return true;
}
bool WalkUpFromMemberExpr(MemberExpr *S) {
if (auto QualifierLoc = S->getQualifierLoc())
Builder.markChild(QualifierLoc, syntax::NodeRole::IdExpression_qualifier);
auto TemplateKeywordLoc = S->getTemplateKeywordLoc();
if (TemplateKeywordLoc.isValid())
Builder.markChildToken(TemplateKeywordLoc,
syntax::NodeRole::TemplateKeyword);
auto *TheUnqualifiedId = new (allocator()) syntax::UnqualifiedId;
Builder.foldNode(Builder.getRange(S->getMemberLoc(), S->getEndLoc()),
TheUnqualifiedId, nullptr);
Builder.markChild(TheUnqualifiedId, syntax::NodeRole::IdExpression_id);
auto *TheIdExpression = new (allocator()) syntax::IdExpression;
auto MemberRange =
Builder.getRange(S->hasQualifier() ? S->getQualifierLoc().getBeginLoc()
: S->getMemberLoc(),
S->getEndLoc());
// For `MemberExpr` with implicit `this->` we generate a simple
// `id-expression` syntax node, beacuse an implicit `member-expression` is
// syntactically undistinguishable from an `id-expression`
if (S->isImplicitAccess()) {
Builder.foldNode(MemberRange, TheIdExpression, S);
return true;
}
Builder.foldNode(MemberRange, TheIdExpression, nullptr);
Builder.markChild(TheIdExpression,
syntax::NodeRole::MemberExpression_member);
Builder.markExprChild(S->getBase(),
syntax::NodeRole::MemberExpression_object);
Builder.markChildToken(S->getOperatorLoc(),
syntax::NodeRole::MemberExpression_accessToken);
Builder.foldNode(Builder.getExprRange(S),
new (allocator()) syntax::MemberExpression, S);
return true;
}
bool WalkUpFromDeclRefExpr(DeclRefExpr *S) {
if (auto QualifierLoc = S->getQualifierLoc())
Builder.markChild(QualifierLoc, syntax::NodeRole::IdExpression_qualifier);

View File

@ -126,6 +126,8 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeKind K) {
return OS << "SimpleTemplateNameSpecifier";
case NodeKind::NestedNameSpecifier:
return OS << "NestedNameSpecifier";
case NodeKind::MemberExpression:
return OS << "MemberExpression";
}
llvm_unreachable("unknown node kind");
}
@ -202,6 +204,12 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
return OS << "IdExpression_qualifier";
case syntax::NodeRole::ParenExpression_subExpression:
return OS << "ParenExpression_subExpression";
case syntax::NodeRole::MemberExpression_object:
return OS << "MemberExpression_object";
case syntax::NodeRole::MemberExpression_accessToken:
return OS << "MemberExpression_accessToken";
case syntax::NodeRole::MemberExpression_member:
return OS << "MemberExpression_member";
}
llvm_unreachable("invalid role");
}
@ -230,6 +238,26 @@ syntax::NestedNameSpecifier::specifiersAndDoubleColons() {
return Children;
}
syntax::Expression *syntax::MemberExpression::object() {
return cast_or_null<syntax::Expression>(
findChild(syntax::NodeRole::MemberExpression_object));
}
syntax::Leaf *syntax::MemberExpression::templateKeyword() {
return llvm::cast_or_null<syntax::Leaf>(
findChild(syntax::NodeRole::TemplateKeyword));
}
syntax::Leaf *syntax::MemberExpression::accessToken() {
return llvm::cast_or_null<syntax::Leaf>(
findChild(syntax::NodeRole::MemberExpression_accessToken));
}
syntax::IdExpression *syntax::MemberExpression::member() {
return cast_or_null<syntax::IdExpression>(
findChild(syntax::NodeRole::MemberExpression_member));
}
syntax::NestedNameSpecifier *syntax::IdExpression::qualifier() {
return cast_or_null<syntax::NestedNameSpecifier>(
findChild(syntax::NodeRole::IdExpression_qualifier));

View File

@ -491,19 +491,20 @@ struct X {
operator int();
};
void test(X x) {
// TODO: Expose `id-expression` from `MemberExpr`
[[x.operator int()]];
}
)cpp",
{R"txt(
UnknownExpression
|-UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-x
| |-.
| |-operator
| `-int
| `-IdExpression
| `-UnqualifiedId
| |-operator
| `-int
|-(
`-)
)txt"}));
@ -542,19 +543,20 @@ TEST_P(SyntaxTreeTest, UnqualifiedId_Destructor) {
R"cpp(
struct X { };
void test(X x) {
// TODO: Expose `id-expression` from `MemberExpr`
[[x.~X()]];
}
)cpp",
{R"txt(
UnknownExpression
|-UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-x
| |-.
| |-~
| `-X
| `-IdExpression
| `-UnqualifiedId
| |-~
| `-X
|-(
`-)
)txt"}));
@ -568,18 +570,23 @@ TEST_P(SyntaxTreeTest, UnqualifiedId_DecltypeDestructor) {
R"cpp(
struct X { };
void test(X x) {
// TODO: Expose `id-expression` from `MemberExpr`
// FIXME: Make `decltype(x)` a child of `MemberExpression`. It is currently
// not because `Expr::getSourceRange()` returns the range of `x.~` for the
// `MemberExpr` instead of the expected `x.~decltype(x)`, this is a bug in
// clang.
[[x.~decltype(x)()]];
}
)cpp",
{R"txt(
UnknownExpression
|-UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-x
| |-.
| `-~
| `-IdExpression
| `-UnqualifiedId
| `-~
|-decltype
|-(
|-x
@ -624,6 +631,9 @@ namespace n {
struct S { };
}
void test() {
// FIXME: Remove the `UnknownExpression` wrapping `s1` and `s2`. This
// `UnknownExpression` comes from a leaf `CXXConstructExpr` in the
// ClangAST. We need to ignore leaf implicit nodes.
[[::n::S s1]];
[[n::S s2]];
}
@ -1756,6 +1766,9 @@ TEST_P(SyntaxTreeTest, OverloadedOperator_Plus) {
struct X {
friend X operator+(X, const X&);
};
// FIXME: Remove additional `UnknownExpression` wrapping `x`. For that, ignore
// implicit copy constructor called on `x`. This should've been ignored already,
// as we `IgnoreImplicit` when traversing an `Stmt`.
void test(X x, X y) {
[[x + y]];
}
@ -1961,6 +1974,366 @@ PostfixUnaryOperatorExpression
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_SimpleWithDot) {
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
int a;
};
void test(struct S s) {
[[s.a]];
}
)cpp",
{R"txt(
MemberExpression
|-IdExpression
| `-UnqualifiedId
| `-s
|-.
`-IdExpression
`-UnqualifiedId
`-a
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_StaticDataMember) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
static int a;
};
void test(S s) {
[[s.a]];
}
)cpp",
{R"txt(
MemberExpression
|-IdExpression
| `-UnqualifiedId
| `-s
|-.
`-IdExpression
`-UnqualifiedId
`-a
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_SimpleWithArrow) {
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
int a;
};
void test(struct S* sp) {
[[sp->a]];
}
)cpp",
{R"txt(
MemberExpression
|-IdExpression
| `-UnqualifiedId
| `-sp
|-->
`-IdExpression
`-UnqualifiedId
`-a
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_Chaining) {
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
struct S* next;
};
void test(struct S s){
[[s.next->next]];
}
)cpp",
{R"txt(
MemberExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| `-IdExpression
| `-UnqualifiedId
| `-next
|-->
`-IdExpression
`-UnqualifiedId
`-next
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_OperatorFunction) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
bool operator!();
};
void test(S s) {
[[s.operator!()]];
}
)cpp",
{R"txt(
UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| `-IdExpression
| `-UnqualifiedId
| |-operator
| `-!
|-(
`-)
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_Implicit) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
int a;
int test(){
// FIXME: Remove the `UnknownExpression` wrapping `a`. This
// `UnknownExpression` comes from an implicit leaf `CXXThisExpr`.
[[a]];
}
};
)cpp",
{R"txt(
IdExpression
`-UnqualifiedId
`-UnknownExpression
`-a
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_VariableTemplate) {
if (!GetParam().isCXX14OrLater()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
template<typename T>
static constexpr T x = 42;
};
// FIXME: `<int>` should be a child of `MemberExpression` and `;` of
// `ExpressionStatement`. This is a bug in clang, in `getSourceRange` methods.
void test(S s) [[{
s.x<int>;
}]]
)cpp",
{R"txt(
CompoundStatement
|-{
|-ExpressionStatement
| `-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| `-IdExpression
| `-UnqualifiedId
| `-x
|-<
|-int
|->
|-;
`-}
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_FunctionTemplate) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
template<typename T>
T f();
};
void test(S* sp){
[[sp->f<int>()]];
}
)cpp",
{R"txt(
UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-sp
| |-->
| `-IdExpression
| `-UnqualifiedId
| |-f
| |-<
| |-int
| `->
|-(
`-)
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_FunctionTemplateWithTemplateKeyword) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct S {
template<typename T>
T f();
};
void test(S s){
[[s.template f<int>()]];
}
)cpp",
{R"txt(
UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| |-template
| `-IdExpression
| `-UnqualifiedId
| |-f
| |-<
| |-int
| `->
|-(
`-)
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_WithQualifier) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
struct Base {
void f();
};
struct S : public Base {};
void test(S s){
[[s.Base::f()]];
[[s.::S::~S()]];
}
)cpp",
{R"txt(
UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| `-IdExpression
| |-NestedNameSpecifier
| | |-IdentifierNameSpecifier
| | | `-Base
| | `-::
| `-UnqualifiedId
| `-f
|-(
`-)
)txt",
R"txt(
UnknownExpression
|-MemberExpression
| |-IdExpression
| | `-UnqualifiedId
| | `-s
| |-.
| `-IdExpression
| |-NestedNameSpecifier
| | |-::
| | |-IdentifierNameSpecifier
| | | `-S
| | `-::
| `-UnqualifiedId
| |-~
| `-S
|-(
`-)
)txt"}));
}
TEST_P(SyntaxTreeTest, MemberExpression_Complex) {
if (!GetParam().isCXX()) {
return;
}
EXPECT_TRUE(treeDumpEqualOnAnnotations(
R"cpp(
template<typename T>
struct U {
template<typename U>
U f();
};
struct S {
U<int> getU();
};
void test(S* sp) {
// FIXME: The first 'template' keyword is a child of `NestedNameSpecifier`,
// but it should be a child of `MemberExpression` according to the grammar.
// However one might argue that the 'template' keyword fits better inside
// `NestedNameSpecifier` because if we change `U<int>` to `UI` we would like
// equally to change the `NameSpecifier` `template U<int>` to just `UI`.
[[sp->getU().template U<int>::template f<int>()]];
}
)cpp",
{R"txt(
UnknownExpression
|-MemberExpression
| |-UnknownExpression
| | |-MemberExpression
| | | |-IdExpression
| | | | `-UnqualifiedId
| | | | `-sp
| | | |-->
| | | `-IdExpression
| | | `-UnqualifiedId
| | | `-getU
| | |-(
| | `-)
| |-.
| `-IdExpression
| |-NestedNameSpecifier
| | |-SimpleTemplateNameSpecifier
| | | |-template
| | | |-U
| | | |-<
| | | |-int
| | | `->
| | `-::
| |-template
| `-UnqualifiedId
| |-f
| |-<
| |-int
| `->
|-(
`-)
)txt"}));
}
TEST_P(SyntaxTreeTest, MultipleDeclaratorsGrouping) {
EXPECT_TRUE(treeDumpEqual(
R"cpp(