Resolve invalid return statements in async/sync generators.

R=brianwilkerson@google.com, paulberry@google.com

Change-Id: Ic0aa6ffbffc23b10814ab369ce041bde32da1388
Reviewed-on: https://dart-review.googlesource.com/65602
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-07-18 22:06:52 +00:00 committed by commit-bot@chromium.org
parent 863c79a5fa
commit 9c13c9dafc
5 changed files with 91 additions and 15 deletions

View file

@ -2432,20 +2432,6 @@ class CompileTimeErrorCodeTest_Kernel extends CompileTimeErrorCodeTest_Driver {
await super.test_returnInGenerativeConstructor_expressionFunctionBody();
}
@override
@failingTest
test_returnInGenerator_asyncStar() async {
// AnalysisException: Element mismatch in /test.dart at /test.dart
await super.test_returnInGenerator_asyncStar();
}
@override
@failingTest
test_returnInGenerator_syncStar() async {
// AnalysisException: Element mismatch in /test.dart at /test.dart
await super.test_returnInGenerator_syncStar();
}
@override
@failingTest
test_superInInvalidContext_binaryExpression() async {

View file

@ -3496,6 +3496,62 @@ class C {
assertType(thisRef, 'C');
}
test_invalid_generator_async_return_blockBody() async {
addTestFile(r'''
int a = 0;
f() async* {
return a;
}
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var aRef = findNode.simple('a;');
assertElement(aRef, findElement.topGet('a'));
assertType(aRef, 'int');
}
test_invalid_generator_async_return_expressionBody() async {
addTestFile(r'''
int a = 0;
f() async* => a;
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var aRef = findNode.simple('a;');
assertElement(aRef, findElement.topGet('a'));
assertType(aRef, 'int');
}
test_invalid_generator_sync_return_blockBody() async {
addTestFile(r'''
int a = 0;
f() sync* {
return a;
}
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var aRef = findNode.simple('a;');
assertElement(aRef, findElement.topGet('a'));
assertType(aRef, 'int');
}
test_invalid_generator_sync_return_expressionBody() async {
addTestFile(r'''
int a = 0;
f() sync* => a;
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var aRef = findNode.simple('a;');
assertElement(aRef, findElement.topGet('a'));
assertType(aRef, 'int');
}
test_invalid_instanceCreation_abstract() async {
addTestFile(r'''
abstract class C<T> {

View file

@ -703,6 +703,13 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
_typeInferrer.inferFunctionBody(
this, factory, _computeReturnTypeContext(member), asyncModifier, body);
// We finished the invalid body inference, desugar it into its error.
if (body is InvalidStatementJudgment) {
InvalidStatementJudgment judgment = body;
body = new ExpressionStatement(judgment.desugaredError);
}
if (builder.kind == ProcedureKind.Setter) {
bool oneParameter = formals != null &&
formals.required.length == 1 &&
@ -3933,7 +3940,8 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
@override
void handleInvalidStatement(Token token, Message message) {
Statement statement = pop();
push(wrapInCompileTimeErrorStatement(statement, message));
var error = buildCompileTimeError(message, statement.fileOffset, noLength);
push(new InvalidStatementJudgment(error, statement));
}
@override

View file

@ -93,6 +93,7 @@ export 'kernel_shadow_ast.dart'
IndexAssignmentJudgment,
InvalidConstructorInvocationJudgment,
InvalidPropertyGetJudgment,
InvalidStatementJudgment,
InvalidVariableWriteJudgment,
InvalidWriteJudgment,
ShadowInvalidFieldInitializer,

View file

@ -3101,6 +3101,31 @@ class ThrowJudgment extends Throw implements ExpressionJudgment {
}
}
/// Synthetic judgment class representing a statement that is not allowed at
/// the location it was found, and should be replaced with an error.
class InvalidStatementJudgment extends ExpressionStatement
implements StatementJudgment {
final kernel.Expression desugaredError;
final StatementJudgment statement;
InvalidStatementJudgment(this.desugaredError, this.statement)
: super(new NullLiteral());
@override
void infer<Expression, Statement, Initializer, Type>(
ShadowTypeInferrer inferrer,
Factory<Expression, Statement, Initializer, Type> factory) {
inferrer.inferStatement(factory, statement);
// If this judgment is a part of a Block, replace it there.
// Otherwise, the parent would be a FunctionNode, but not yet.
if (parent is Block) {
parent.replaceChild(this, new ExpressionStatement(desugaredError));
parent = null;
}
}
}
/// Concrete shadow object representing a catch clause.
class CatchJudgment extends Catch {
final Token onKeyword;