Skip to content

Commit 2e671d3

Browse files
committed
Merge pull request #145 from chriseth/parsingAmbiguityIndexAccess
Correctly parse ambiguities like `A.B[10] x` and `x.y[10] = 3`.
2 parents 452d473 + 87079bd commit 2e671d3

File tree

4 files changed

+99
-27
lines changed

4 files changed

+99
-27
lines changed

libsolidity/Parser.cpp

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,9 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
734734
// These two cases are very hard to distinguish:
735735
// x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
736736
// In the first case, x is a type name, in the second it is the name of a variable.
737+
// As an extension, we can even have:
738+
// `x.y.z[1][2] a;` and `x.y.z[1][2] = 10;`
739+
// Where in the first, x.y.z leads to a type name where in the second, it accesses structs.
737740
switch (peekStatementType())
738741
{
739742
case LookAheadInfo::VariableDeclarationStatement:
@@ -744,36 +747,43 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
744747
break;
745748
}
746749

747-
// At this point, we have '(Identifier|ElementaryTypeName) "["'.
748-
// We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over
749-
// to ExpressionStatement or create a VariableDeclarationStatement out of it.
750-
ASTPointer<PrimaryExpression> primary;
750+
// At this point, we have 'Identifier "["' or 'Identifier "." Identifier' or 'ElementoryTypeName "["'.
751+
// We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )+'
752+
// until we can decide whether to hand this over to ExpressionStatement or create a
753+
// VariableDeclarationStatement out of it.
754+
755+
vector<ASTPointer<PrimaryExpression>> path;
756+
bool startedWithElementary = false;
751757
if (m_scanner->currentToken() == Token::Identifier)
752-
primary = parseIdentifier();
758+
path.push_back(parseIdentifier());
753759
else
754760
{
755-
primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->currentToken());
761+
startedWithElementary = true;
762+
path.push_back(ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->currentToken()));
763+
m_scanner->next();
764+
}
765+
while (!startedWithElementary && m_scanner->currentToken() == Token::Period)
766+
{
756767
m_scanner->next();
768+
path.push_back(parseIdentifier());
757769
}
758770
vector<pair<ASTPointer<Expression>, SourceLocation>> indices;
759-
solAssert(m_scanner->currentToken() == Token::LBrack, "");
760-
SourceLocation indexLocation = primary->location();
761-
do
771+
while (m_scanner->currentToken() == Token::LBrack)
762772
{
763773
expectToken(Token::LBrack);
764774
ASTPointer<Expression> index;
765775
if (m_scanner->currentToken() != Token::RBrack)
766776
index = parseExpression();
777+
SourceLocation indexLocation = path.front()->location();
767778
indexLocation.end = endPosition();
768779
indices.push_back(make_pair(index, indexLocation));
769780
expectToken(Token::RBrack);
770781
}
771-
while (m_scanner->currentToken() == Token::LBrack);
772782

773783
if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken()))
774-
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
784+
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(path, indices));
775785
else
776-
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
786+
return parseExpressionStatement(expressionFromIndexAccessStructure(path, indices));
777787
}
778788

779789
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
@@ -1090,7 +1100,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
10901100
// We have a variable declaration if we get a keyword that specifies a type name.
10911101
// If it is an identifier or an elementary type name followed by an identifier, we also have
10921102
// a variable declaration.
1093-
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;").
1103+
// If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;").
10941104
// In all other cases, we have an expression statement.
10951105
Token::Value token(m_scanner->currentToken());
10961106
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
@@ -1102,25 +1112,36 @@ Parser::LookAheadInfo Parser::peekStatementType() const
11021112
Token::Value next = m_scanner->peekNextToken();
11031113
if (next == Token::Identifier || Token::isLocationSpecifier(next))
11041114
return LookAheadInfo::VariableDeclarationStatement;
1105-
if (m_scanner->peekNextToken() == Token::LBrack)
1115+
if (next == Token::LBrack || next == Token::Period)
11061116
return LookAheadInfo::IndexAccessStructure;
11071117
}
11081118
return LookAheadInfo::ExpressionStatement;
11091119
}
11101120

11111121
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
1112-
ASTPointer<PrimaryExpression> const& _primary,
1122+
vector<ASTPointer<PrimaryExpression>> const& _path,
11131123
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
11141124
)
11151125
{
1116-
ASTNodeFactory nodeFactory(*this, _primary);
1126+
solAssert(!_path.empty(), "");
1127+
ASTNodeFactory nodeFactory(*this);
1128+
SourceLocation location = _path.front()->location();
1129+
location.end = _path.back()->location().end;
1130+
nodeFactory.setLocation(location);
1131+
11171132
ASTPointer<TypeName> type;
1118-
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
1119-
type = nodeFactory.createNode<UserDefinedTypeName>(vector<ASTString>{identifier->name()});
1120-
else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
1133+
if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_path.front().get()))
1134+
{
1135+
solAssert(_path.size() == 1, "");
11211136
type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken());
1137+
}
11221138
else
1123-
solAssert(false, "Invalid type name for array look-ahead.");
1139+
{
1140+
vector<ASTString> path;
1141+
for (auto const& el: _path)
1142+
path.push_back(dynamic_cast<Identifier const&>(*el).name());
1143+
type = nodeFactory.createNode<UserDefinedTypeName>(path);
1144+
}
11241145
for (auto const& lengthExpression: _indices)
11251146
{
11261147
nodeFactory.setLocation(lengthExpression.second);
@@ -1130,12 +1151,24 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
11301151
}
11311152

11321153
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
1133-
ASTPointer<PrimaryExpression> const& _primary,
1154+
vector<ASTPointer<PrimaryExpression>> const& _path,
11341155
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
11351156
)
11361157
{
1137-
ASTNodeFactory nodeFactory(*this, _primary);
1138-
ASTPointer<Expression> expression(_primary);
1158+
solAssert(!_path.empty(), "");
1159+
ASTNodeFactory nodeFactory(*this, _path.front());
1160+
ASTPointer<Expression> expression(_path.front());
1161+
for (size_t i = 1; i < _path.size(); ++i)
1162+
{
1163+
SourceLocation location(_path.front()->location());
1164+
location.end = _path[i]->location().end;
1165+
nodeFactory.setLocation(location);
1166+
Identifier const& identifier = dynamic_cast<Identifier const&>(*_path[i]);
1167+
expression = nodeFactory.createNode<MemberAccess>(
1168+
expression,
1169+
make_shared<ASTString>(identifier.name())
1170+
);
1171+
}
11391172
for (auto const& index: _indices)
11401173
{
11411174
nodeFactory.setLocation(index.second);

libsolidity/Parser.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ class Parser
125125
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
126126
/// decide with constant look-ahead.
127127
LookAheadInfo peekStatementType() const;
128-
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
128+
/// Returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]".
129129
ASTPointer<TypeName> typeNameIndexAccessStructure(
130-
ASTPointer<PrimaryExpression> const& _primary,
130+
std::vector<ASTPointer<PrimaryExpression>> const& _path,
131131
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
132132
);
133-
/// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]".
133+
/// Returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]".
134134
ASTPointer<Expression> expressionFromIndexAccessStructure(
135-
ASTPointer<PrimaryExpression> const& _primary,
135+
std::vector<ASTPointer<PrimaryExpression>> const& _path,
136136
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
137137
);
138138
/// If current token value is not _value, throw exception otherwise advance token.

test/libsolidity/SolidityNameAndTypeResolution.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,27 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
25052505
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
25062506
}
25072507

2508+
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
2509+
{
2510+
char const* text = R"(
2511+
contract C {
2512+
struct R { uint[10][10] y; }
2513+
struct S { uint a; uint b; uint[20][20][20] c; R d; }
2514+
S data;
2515+
function f() {
2516+
C.S x = data;
2517+
C.S memory y;
2518+
C.S[10] memory z;
2519+
C.S[10];
2520+
y.a = 2;
2521+
x.c[1][2][3] = 9;
2522+
x.d.y[2][2] = 3;
2523+
}
2524+
}
2525+
)";
2526+
BOOST_CHECK(success(text));
2527+
}
2528+
25082529
BOOST_AUTO_TEST_SUITE_END()
25092530

25102531
}

test/libsolidity/SolidityParser.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,24 @@ BOOST_AUTO_TEST_CASE(tuples)
10151015
BOOST_CHECK(successParse(text));
10161016
}
10171017

1018+
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
1019+
{
1020+
char const* text = R"(
1021+
contract C {
1022+
struct S { uint a; uint b; uint[][][] c; }
1023+
function f() {
1024+
C.S x;
1025+
C.S memory y;
1026+
C.S[10] memory z;
1027+
C.S[10](x);
1028+
x.a = 2;
1029+
x.c[1][2][3] = 9;
1030+
}
1031+
}
1032+
)";
1033+
BOOST_CHECK(successParse(text));
1034+
}
1035+
10181036
BOOST_AUTO_TEST_SUITE_END()
10191037

10201038
}

0 commit comments

Comments
 (0)