L#4210. Skip empty return when inferring function literal return type when generator.

Bug: https://github.com/dart-lang/language/pull/4210
Change-Id: I098d80fe36ba6629d8613f2b4e80d4562c3232b5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446183
Reviewed-by: Erik Ernst <eernst@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Konstantin Shcheglov
2025-08-21 10:01:19 -07:00
committed by Commit Queue
parent 8f3035707f
commit 94920ec28f
2 changed files with 105 additions and 1 deletions
@@ -10,6 +10,7 @@ import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
/// See https://github.com/dart-lang/language/blob/main/resources/type-system/inference.md#function-literal-return-type-inference
class BodyInferenceContext {
final TypeSystemImpl _typeSystem;
final bool isAsynchronous;
@@ -67,7 +68,12 @@ class BodyInferenceContext {
void addReturnExpression(Expression? expression) {
if (expression == null) {
_returnTypes.add(_typeProvider.nullType);
// If the enclosing function is not marked `sync*` or `async*`:
// For each `return;` statement in the block, update
// `T` to be `UP(Null, T)`.
if (!isGenerator) {
_returnTypes.add(_typeProvider.nullType);
}
} else {
var type = expression.typeOrThrow;
if (isAsynchronous) {
@@ -661,6 +661,55 @@ var v = () async* {
_assertReturnType('() async* {', 'Stream<int>');
}
test_noContext_returnType_asyncStar_blockBody_hasReturn_empty() async {
await resolveTestCode(r'''
var v = () async* {
yield 0;
return;
};
''');
_assertReturnType('() async* {', 'Stream<int>');
}
test_noContext_returnType_asyncStar_blockBody_hasReturn_noYield() async {
await resolveTestCode(r'''
var v = () async* {
return;
};
''');
_assertReturnType('() async* {', 'Stream<dynamic>');
}
test_noContext_returnType_asyncStar_blockBody_lubNum() async {
await resolveTestCode(r'''
var v = () async* {
yield 0;
yield 1.1;
};
''');
_assertReturnType('() async* {', 'Stream<num>');
}
test_noContext_returnType_asyncStar_blockBody_lubObject() async {
await resolveTestCode(r'''
var v = () async* {
yield 0;
yield '';
};
''');
_assertReturnType('() async* {', 'Stream<Object>');
}
test_noContext_returnType_asyncStar_blockBody_lubWithNull() async {
await resolveTestCode(r'''
var v = () async* {
yield 0;
yield null;
};
''');
_assertReturnType('() async* {', 'Stream<int?>');
}
test_noContext_returnType_sync_blockBody() async {
await resolveTestCode('''
var v = () {
@@ -875,6 +924,55 @@ var v = () sync* {
_assertReturnType('() sync* {', 'Iterable<int>');
}
test_noContext_returnType_syncStar_blockBody_hasReturn_empty() async {
await resolveTestCode(r'''
var v = () sync* {
yield 0;
return;
};
''');
_assertReturnType('() sync* {', 'Iterable<int>');
}
test_noContext_returnType_syncStar_blockBody_hasReturn_noYield() async {
await resolveTestCode(r'''
var v = () sync* {
return;
};
''');
_assertReturnType('() sync* {', 'Iterable<dynamic>');
}
test_noContext_returnType_syncStar_blockBody_lubNum() async {
await resolveTestCode(r'''
var v = () sync* {
yield 0;
yield 1.1;
};
''');
_assertReturnType('() sync* {', 'Iterable<num>');
}
test_noContext_returnType_syncStar_blockBody_lubObject() async {
await resolveTestCode(r'''
var v = () sync* {
yield 0;
yield '';
};
''');
_assertReturnType('() sync* {', 'Iterable<Object>');
}
test_noContext_returnType_syncStar_blockBody_lubWithNull() async {
await resolveTestCode(r'''
var v = () sync* {
yield 0;
yield null;
};
''');
_assertReturnType('() sync* {', 'Iterable<int?>');
}
test_targetBoundedByFunctionType_argumentTypeMismatch() async {
await assertErrorsInCode(
r'''