[cfe] Use InvalidExpression for await on non-async context.

CFE failed to replace an invalid AwaitExpression in a non-async context
with an InvalidExpression. The lead to untransformed (and thus unexpected)
await expressions crashing the hot reload.

Closes #37108

Change-Id: I4457925b20749d231cbf9dac80203ee9b381a312
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107501
Reviewed-by: Dan Rubel <danrubel@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2019-07-04 08:30:45 +00:00 committed by commit-bot@chromium.org
parent 0272c1d863
commit b8a8cfdcda
14 changed files with 89 additions and 12 deletions

View file

@ -21,6 +21,7 @@ import 'package:front_end/src/fasta/messages.dart'
show
LocatedMessage,
Message,
MessageCode,
messageConstConstructorWithBody,
messageConstMethod,
messageConstructorWithReturnType,
@ -562,6 +563,12 @@ class AstBuilder extends StackListener {
push(ast.awaitExpression(awaitKeyword, pop()));
}
void endInvalidAwaitExpression(
Token awaitKeyword, Token endToken, MessageCode errorCode) {
debugEvent("InvalidAwaitExpression");
endAwaitExpression(awaitKeyword, endToken);
}
@override
void endBinaryExpression(Token operatorToken) {
assert(operatorToken.isOperator ||

View file

@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:front_end/src/fasta/messages.dart' show MessageCode;
import 'package:front_end/src/fasta/parser.dart';
import 'package:front_end/src/fasta/parser/forwarding_listener.dart';
import 'package:front_end/src/scanner/token.dart';
@ -577,6 +578,13 @@ class ForwardingTestListener extends ForwardingListener {
super.endAwaitExpression(beginToken, endToken);
}
@override
void endInvalidAwaitExpression(
Token beginToken, Token endToken, MessageCode errorCode) {
end('InvalidAwaitExpression');
super.endInvalidAwaitExpression(beginToken, endToken, errorCode);
}
@override
void endBlock(int count, Token beginToken, Token endToken) {
end('Block');

View file

@ -2411,6 +2411,14 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
push(forest.awaitExpression(popForValue(), keyword));
}
@override
void endInvalidAwaitExpression(
Token keyword, Token endToken, fasta.MessageCode errorCode) {
debugEvent("AwaitExpression");
popForValue();
push(buildProblem(errorCode, keyword.offset, keyword.length));
}
@override
void handleAsyncModifier(Token asyncToken, Token starToken) {
debugEvent("AsyncModifier");

View file

@ -479,6 +479,12 @@ class ForwardingListener implements Listener {
listener?.endAwaitExpression(beginToken, endToken);
}
@override
void endInvalidAwaitExpression(
Token beginToken, Token endToken, MessageCode errorCode) {
listener?.endInvalidAwaitExpression(beginToken, endToken, errorCode);
}
@override
void endBinaryExpression(Token token) {
listener?.endBinaryExpression(token);

View file

@ -6,7 +6,8 @@ library fasta.parser.listener;
import '../../scanner/token.dart' show Token;
import '../fasta_codes.dart' show Message, templateExperimentNotEnabled;
import '../fasta_codes.dart'
show Message, MessageCode, templateExperimentNotEnabled;
import '../quote.dart' show UnescapeErrorListener;
@ -57,6 +58,11 @@ class Listener implements UnescapeErrorListener {
logEvent("AwaitExpression");
}
void endInvalidAwaitExpression(
Token beginToken, Token endToken, MessageCode errorCode) {
logEvent("InvalidAwaitExpression");
}
void beginBlock(Token token) {}
void endBlock(int count, Token beginToken, Token endToken) {

View file

@ -5744,12 +5744,15 @@ class Parser {
Token awaitToken = token.next;
assert(optional('await', awaitToken));
listener.beginAwaitExpression(awaitToken);
if (!inAsync) {
reportRecoverableError(awaitToken, fasta.messageAwaitNotAsync);
}
token = parsePrecedenceExpression(
awaitToken, POSTFIX_PRECEDENCE, allowCascades);
listener.endAwaitExpression(awaitToken, token.next);
if (inAsync) {
listener.endAwaitExpression(awaitToken, token.next);
} else {
fasta.MessageCode errorCode = fasta.messageAwaitNotAsync;
reportRecoverableError(awaitToken, errorCode);
listener.endInvalidAwaitExpression(awaitToken, token.next, errorCode);
}
return token;
}

View file

@ -6,7 +6,7 @@ library fasta.type_promotion_look_ahead_listener;
import '../builder/builder.dart' show Declaration;
import '../messages.dart' show LocatedMessage, Message;
import '../messages.dart' show LocatedMessage, Message, MessageCode;
import '../parser.dart'
show Assert, IdentifierContext, FormalParameterKind, Listener, MemberKind;
@ -209,6 +209,13 @@ class TypePromotionLookAheadListener extends Listener {
state.popPushNull(beginToken.lexeme, beginToken); // Expression.
}
@override
void endInvalidAwaitExpression(
Token beginToken, Token endToken, MessageCode errorCode) {
debugEvent("InvalidAwaitExpression", beginToken);
state.popPushNull(beginToken.lexeme, beginToken); // Expression.
}
@override
void endBinaryExpression(Token token) {
debugEvent("BinaryExpression", token);

View file

@ -111,6 +111,10 @@ const String EXPECTATIONS = '''
"name": "VerificationError",
"group": "Fail"
},
{
"name": "TransformVerificationError",
"group": "Fail"
},
{
"name": "TextSerializationFailure",
"group": "Fail"
@ -425,7 +429,11 @@ class Transform extends Step<Component, Component, FastaContext> {
}
List<String> errors = VerifyTransformed.verify(component);
if (errors.isNotEmpty) {
return fail(component, errors.join('\n'));
return new Result<Component>(
component,
context.expectationSet["TransformVerificationError"],
errors.join('\n'),
null);
}
return pass(component);
}

View file

@ -12,5 +12,7 @@ static method main() → dynamic {
self::foo();
}
static method foo() → dynamic {
await self::foo();
invalid-expression "pkg/front_end/testcases/await_in_non_async.dart:11:3: Error: 'await' can only be used in 'async' or 'async*' methods.
await foo();
^^^^^";
}

View file

@ -0,0 +1,18 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/await_in_non_async.dart:11:3: Error: 'await' can only be used in 'async' or 'async*' methods.
// await foo();
// ^^^^^
//
import self as self;
static method main() → dynamic {
self::foo();
}
static method foo() → dynamic {
invalid-expression "pkg/front_end/testcases/await_in_non_async.dart:11:3: Error: 'await' can only be used in 'async' or 'async*' methods.
await foo();
^^^^^";
}

View file

@ -12,5 +12,7 @@ static method main() → dynamic {
self::foo();
}
static method foo() → dynamic {
await self::foo();
invalid-expression "pkg/front_end/testcases/await_in_non_async.dart:11:3: Error: 'await' can only be used in 'async' or 'async*' methods.
await foo();
^^^^^";
}

View file

@ -12,5 +12,7 @@ static method main() → dynamic {
self::foo();
}
static method foo() → dynamic {
await self::foo();
invalid-expression "pkg/front_end/testcases/await_in_non_async.dart:11:3: Error: 'await' can only be used in 'async' or 'async*' methods.
await foo();
^^^^^";
}

View file

@ -7,7 +7,7 @@
DeltaBlue: Fail # Fasta and dartk disagree on static initializers
ambiguous_exports: RuntimeError # Expected, this file exports two main methods.
await_in_non_async: Fail # Issue 37108.
await_in_non_async: RuntimeError # Expected.
bug31124: RuntimeError # Test has no main method (and we shouldn't add one).
call: Fail # Test can't run.
constructor_const_inference: RuntimeError # Test exercises strong mode semantics. See also Issue #33813.

View file

@ -10,7 +10,7 @@ abstract_members: TypeCheckError
accessors: RuntimeError
ambiguous_exports: RuntimeError # Expected, this file exports two main methods.
argument_mismatch: InstrumentationMismatch # Test assumes Dart 1.0 semantics
await_in_non_async: Fail # Issue 37108.
await_in_non_async: RuntimeError # Expected.
bug21938: TypeCheckError
bug30695: TypeCheckError
bug31124: RuntimeError # Test has no main method (and we shouldn't add one).