Skip to content

Commit 9404899

Browse files
johnniwintherCommit Queue
authored andcommitted
[parser] Split handleEndingBinaryExpression into handleDotAccess and handleCascadeAccess
This splits handleEndingBinaryExpression into two new listeners handleDotAccess for `.` and `?.` access and handleCascadeAccess for `..` and `?..`, both with an explicit `isNullAware` flag. This is a step towards handling `a.b` different from `a + b` in the parser such that listeners don't have to create a value for `b` the works in both use cases; in the first is just a named operation performed on the receiver, whereas in the second case it is a full expression in its own right. Change-Id: I04ec80401f8f2dbb8dffa86543c434e530b18cf7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442821 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Jens Johansen <[email protected]>
1 parent dfba8a7 commit 9404899

File tree

198 files changed

+745
-599
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

198 files changed

+745
-599
lines changed

pkg/_fe_analyzer_shared/lib/src/metadata/parser.dart

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -374,25 +374,17 @@ class AnnotationsListener extends StackListener {
374374
}
375375

376376
@override
377-
void handleEndingBinaryExpression(Token token, Token endToken) {
377+
void handleDotAccess(Token token, Token endToken, bool isNullAware) {
378378
assert(
379-
checkState(token, [
380-
/* right */ _ValueKinds._Proto,
381-
/* left */ _ValueKinds._Proto,
382-
]),
379+
checkState(token, [
380+
/* right */ _ValueKinds._Proto,
381+
/* left */ _ValueKinds._Proto,
382+
]),
383383
);
384384
Proto right = pop() as Proto;
385385
Proto left = pop() as Proto;
386-
switch (token.lexeme) {
387-
case '.':
388-
IdentifierProto identifierProto = right as IdentifierProto;
389-
push(left.apply(identifierProto));
390-
case '?.':
391-
IdentifierProto identifierProto = right as IdentifierProto;
392-
push(left.apply(identifierProto, isNullAware: true));
393-
default:
394-
throw new UnimplementedError("Binary operator '${token.lexeme}'.");
395-
}
386+
IdentifierProto identifierProto = right as IdentifierProto;
387+
push(left.apply(identifierProto, isNullAware: isNullAware));
396388
}
397389

398390
@override

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,13 @@ class ForwardingListener implements Listener {
678678
}
679679

680680
@override
681-
void handleEndingBinaryExpression(Token token, Token endToken) {
682-
listener?.handleEndingBinaryExpression(token, endToken);
681+
void handleDotAccess(Token token, Token endToken, bool isNullAware) {
682+
listener?.handleDotAccess(token, endToken, isNullAware);
683+
}
684+
685+
@override
686+
void handleCascadeAccess(Token token, Token endToken, bool isNullAware) {
687+
listener?.handleCascadeAccess(token, endToken, isNullAware);
683688
}
684689

685690
@override

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,8 +2074,15 @@ class Listener implements UnescapeErrorListener {
20742074
logEvent("BinaryPattern");
20752075
}
20762076

2077-
/// Called for `.`, `?.` and `..`.
2078-
void handleEndingBinaryExpression(Token token, Token endToken) {}
2077+
/// Called for property access through `.` and `?.`.
2078+
///
2079+
/// [isNullAware] is `true` if the access uses `?.`.
2080+
void handleDotAccess(Token token, Token endToken, bool isNullAware) {}
2081+
2082+
/// Called for cascade access through `..` and `?..`.
2083+
///
2084+
/// [isNullAware] is `true` if the access uses `?..`.
2085+
void handleCascadeAccess(Token token, Token endToken, bool isNullAware) {}
20792086

20802087
/// Called when the parser encounters a `?` operator and begins parsing a
20812088
/// conditional expression.

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6808,7 +6808,11 @@ class Parser {
68086808
listener.handleDotShorthandHead(dot);
68096809
isDotShorthand = false;
68106810
} else {
6811-
listener.handleEndingBinaryExpression(operator, token);
6811+
listener.handleDotAccess(
6812+
operator,
6813+
token,
6814+
/* isNullAware = */ identical(type, TokenType.QUESTION_PERIOD),
6815+
);
68126816
}
68136817

68146818
Token bangToken = token;
@@ -7153,21 +7157,28 @@ class Parser {
71537157
IdentifierContext.expressionContinuation,
71547158
ConstantPatternContext.none,
71557159
);
7156-
listener.handleEndingBinaryExpression(cascadeOperator, token);
7160+
listener.handleCascadeAccess(
7161+
cascadeOperator,
7162+
token,
7163+
/* isNullAware = */ cascadeOperator.isA(
7164+
TokenType.QUESTION_PERIOD_PERIOD,
7165+
),
7166+
);
71577167
}
71587168
Token next = token.next!;
71597169
Token mark;
71607170
do {
71617171
mark = token;
71627172
if (next.isA(TokenType.PERIOD) || next.isA(TokenType.QUESTION_PERIOD)) {
7173+
bool isNullAware = next.isA(TokenType.QUESTION_PERIOD);
71637174
Token period = next;
71647175
token = parseSend(
71657176
next,
71667177
IdentifierContext.expressionContinuation,
71677178
ConstantPatternContext.none,
71687179
);
71697180
next = token.next!;
7170-
listener.handleEndingBinaryExpression(period, token);
7181+
listener.handleDotAccess(period, token, isNullAware);
71717182
} else if (next.isA(TokenType.BANG)) {
71727183
listener.handleNonNullAssertExpression(next);
71737184
token = next;
@@ -8972,7 +8983,11 @@ class Parser {
89728983
listener.handleNoTypeArguments(next4);
89738984
listener.handleNoArguments(next4);
89748985
listener.handleSend(next3, next3);
8975-
listener.handleEndingBinaryExpression(next2, next3);
8986+
listener.handleDotAccess(
8987+
next2,
8988+
next3,
8989+
/* isNullAware = */ false,
8990+
);
89768991
token = next3;
89778992
expressionHandled = true;
89788993
}

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4062,6 +4062,18 @@ class AstBuilder extends StackListener {
40624062
);
40634063
}
40644064

4065+
@override
4066+
void handleCascadeAccess(
4067+
Token operatorToken,
4068+
Token endToken,
4069+
bool isNullAware,
4070+
) {
4071+
assert(optional('..', operatorToken) || optional('?..', operatorToken));
4072+
debugEvent("CascadeAccess");
4073+
4074+
doDotExpression(operatorToken);
4075+
}
4076+
40654077
@override
40664078
void handleCastPattern(Token asOperator) {
40674079
assert(optional('as', asOperator));
@@ -4261,6 +4273,14 @@ class AstBuilder extends StackListener {
42614273
);
42624274
}
42634275

4276+
@override
4277+
void handleDotAccess(Token operatorToken, Token endToken, bool isNullAware) {
4278+
assert(optional('.', operatorToken) || optional('?.', operatorToken));
4279+
debugEvent("DotAccess");
4280+
4281+
doDotExpression(operatorToken);
4282+
}
4283+
42644284
@override
42654285
void handleDotShorthandContext(Token token) {
42664286
debugEvent("DotShorthandContext");
@@ -4352,19 +4372,6 @@ class AstBuilder extends StackListener {
43524372
push(EmptyStatementImpl(semicolon: semicolon));
43534373
}
43544374

4355-
@override
4356-
void handleEndingBinaryExpression(Token operatorToken, Token endToken) {
4357-
assert(
4358-
optional('.', operatorToken) ||
4359-
optional('?.', operatorToken) ||
4360-
optional('..', operatorToken) ||
4361-
optional('?..', operatorToken),
4362-
);
4363-
debugEvent("EndingBinaryExpression");
4364-
4365-
doDotExpression(operatorToken);
4366-
}
4367-
43684375
@override
43694376
void handleEnumElement(Token beginToken, Token? augmentToken) {
43704377
debugEvent("EnumElement");

pkg/analyzer/tool/summary/mini_ast.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,15 @@ class MiniAstBuilder extends StackListener {
458458
debugEvent("AsyncModifier");
459459
}
460460

461+
@override
462+
void handleCascadeAccess(Token token, Token endToken, bool isNullAware) {
463+
debugEvent("CascadeAccess");
464+
465+
pop(); // RHS
466+
pop(); // LHS
467+
push(UnknownExpression());
468+
}
469+
461470
@override
462471
void handleClassNoWithClause() {
463472
debugEvent("NoClassWithClause");
@@ -469,10 +478,10 @@ class MiniAstBuilder extends StackListener {
469478
}
470479

471480
@override
472-
void handleEndingBinaryExpression(Token token, Token endToken) {
473-
debugEvent("EndingBinaryExpression");
481+
void handleDotAccess(Token token, Token endToken, bool isNullAware) {
482+
debugEvent("DotAccess");
474483

475-
if (identical('.', token.stringValue)) {
484+
if (!isNullAware) {
476485
var rightOperand = pop() as String;
477486
var leftOperand = pop();
478487
if (leftOperand is String && !leftOperand.contains('.')) {

pkg/front_end/lib/src/kernel/body_builder.dart

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,23 +2554,19 @@ class BodyBuilder extends StackListenerImpl
25542554
}
25552555

25562556
@override
2557-
void handleEndingBinaryExpression(Token token, Token endToken) {
2557+
void handleDotAccess(Token token, Token endToken, bool isNullAware) {
25582558
assert(checkState(token, [
25592559
unionOfKinds([
25602560
ValueKinds.Expression,
25612561
ValueKinds.Generator,
25622562
ValueKinds.Selector,
25632563
]),
25642564
]));
2565-
debugEvent("BinaryExpression");
2566-
if (token.isA(TokenType.PERIOD) ||
2567-
token.isA(TokenType.PERIOD_PERIOD) ||
2568-
token.isA(TokenType.QUESTION_PERIOD_PERIOD)) {
2569-
doDotOrCascadeExpression(token);
2570-
} else if (token.isA(TokenType.QUESTION_PERIOD)) {
2565+
debugEvent("DotAccess");
2566+
if (isNullAware) {
25712567
doIfNotNull(token);
25722568
} else {
2573-
throw new UnsupportedError("Unexpected ending binary $token.");
2569+
doDotExpression(token);
25742570
}
25752571
assert(checkState(token, [
25762572
unionOfKinds([
@@ -2581,6 +2577,26 @@ class BodyBuilder extends StackListenerImpl
25812577
]));
25822578
}
25832579

2580+
@override
2581+
void handleCascadeAccess(Token token, Token endToken, bool isNullAware) {
2582+
assert(checkState(token, [
2583+
unionOfKinds([
2584+
ValueKinds.Expression,
2585+
ValueKinds.Generator,
2586+
ValueKinds.Selector,
2587+
]),
2588+
]));
2589+
debugEvent("CascadeAccess");
2590+
doCascadeExpression(token);
2591+
assert(checkState(token, [
2592+
unionOfKinds([
2593+
ValueKinds.Expression,
2594+
ValueKinds.Generator,
2595+
ValueKinds.Initializer,
2596+
]),
2597+
]));
2598+
}
2599+
25842600
@override
25852601
void endBinaryExpression(Token token, Token endToken) {
25862602
assert(checkState(token, [
@@ -2891,22 +2907,22 @@ class BodyBuilder extends StackListenerImpl
28912907
]));
28922908
}
28932909

2894-
void doDotOrCascadeExpression(Token token) {
2910+
void doDotExpression(Token token) {
28952911
assert(checkState(token, <ValueKind>[
2896-
/* after . or .. */ unionOfKinds([
2912+
/* after . */ unionOfKinds([
28972913
ValueKinds.Expression,
28982914
ValueKinds.Generator,
28992915
ValueKinds.Selector,
29002916
]),
2901-
/* before . or .. */ unionOfKinds([
2917+
/* before . */ unionOfKinds([
29022918
ValueKinds.Expression,
29032919
ValueKinds.Generator,
29042920
ValueKinds.Initializer,
29052921
]),
29062922
]));
29072923
Object? send = pop();
29082924
if (send is Selector) {
2909-
Object? receiver = token.isA(TokenType.PERIOD) ? pop() : popForValue();
2925+
Object? receiver = pop();
29102926
push(send.withReceiver(receiver, token.charOffset));
29112927
} else if (send is IncompleteErrorGenerator) {
29122928
// Pop the "receiver" and push the error.
@@ -2928,6 +2944,45 @@ class BodyBuilder extends StackListenerImpl
29282944
]));
29292945
}
29302946

2947+
void doCascadeExpression(Token token) {
2948+
assert(checkState(token, <ValueKind>[
2949+
/* after .. */ unionOfKinds([
2950+
ValueKinds.Expression,
2951+
ValueKinds.Generator,
2952+
ValueKinds.Selector,
2953+
]),
2954+
/* before .. */ unionOfKinds([
2955+
ValueKinds.Expression,
2956+
ValueKinds.Generator,
2957+
ValueKinds.Initializer,
2958+
]),
2959+
]));
2960+
Object? send = pop();
2961+
if (send is Selector) {
2962+
Object? receiver = popForValue();
2963+
push(send.withReceiver(receiver, token.charOffset));
2964+
}
2965+
// Coverage-ignore(suite): Not run.
2966+
else if (send is IncompleteErrorGenerator) {
2967+
// Pop the "receiver" and push the error.
2968+
pop();
2969+
push(send);
2970+
} else {
2971+
// Pop the "receiver" and push the error.
2972+
pop();
2973+
token = token.next!;
2974+
push(buildProblem(cfe.templateExpectedIdentifier.withArguments(token),
2975+
offsetForToken(token), lengthForToken(token)));
2976+
}
2977+
assert(checkState(token, <ValueKind>[
2978+
unionOfKinds([
2979+
ValueKinds.Expression,
2980+
ValueKinds.Generator,
2981+
ValueKinds.Initializer,
2982+
]),
2983+
]));
2984+
}
2985+
29312986
@override
29322987
Expression buildUnresolvedError(String name, int charOffset,
29332988
{Member? candidate,

0 commit comments

Comments
 (0)