Skip to content

Commit 7d32528

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Infer void return type for FunctionExpression.
dart-lang/language#1092 Bug: #42720 Change-Id: I7ce44a066382c63e22debbcb652a717b5230b267 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154620 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 3d44605 commit 7d32528

File tree

4 files changed

+93
-22
lines changed

4 files changed

+93
-22
lines changed

pkg/analyzer/lib/src/dart/resolver/body_inference_context.dart

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,7 @@ class BodyInferenceContext {
9292
endOfBlockIsReachable: endOfBlockIsReachable,
9393
);
9494

95-
DartType clampedReturnedType;
96-
if (contextType == null ||
97-
_typeSystem.isSubtypeOf2(actualReturnedType, contextType)) {
98-
clampedReturnedType = actualReturnedType;
99-
} else {
100-
clampedReturnedType = nonNullifyType(_typeSystem, contextType);
101-
}
95+
var clampedReturnedType = _clampToContextType(actualReturnedType);
10296

10397
if (_isGenerator) {
10498
if (_isAsynchronous) {
@@ -117,6 +111,35 @@ class BodyInferenceContext {
117111
}
118112
}
119113

114+
/// Let `T` be the **actual returned type** of a function literal.
115+
DartType _clampToContextType(DartType T) {
116+
// Let `R` be the greatest closure of the typing context `K`.
117+
var R = contextType;
118+
if (R == null) {
119+
return T;
120+
}
121+
122+
// If `R` is `void`, or the function literal is marked `async` and `R` is
123+
// `FutureOr<void>`, let `S` be `void`.
124+
if (_typeSystem.isNonNullableByDefault) {
125+
if (R.isVoid ||
126+
_isAsynchronous &&
127+
R is InterfaceType &&
128+
R.isDartAsyncFutureOr &&
129+
R.typeArguments[0].isVoid) {
130+
return VoidTypeImpl.instance;
131+
}
132+
}
133+
134+
// Otherwise, if `T <: R` then let `S` be `T`.
135+
if (_typeSystem.isSubtypeOf2(T, R)) {
136+
return T;
137+
}
138+
139+
// Otherwise, let `S` be `R`.
140+
return nonNullifyType(_typeSystem, R);
141+
}
142+
120143
DartType _computeActualReturnedType({
121144
@required bool endOfBlockIsReachable,
122145
}) {

pkg/analyzer/test/generated/resolver_test.dart

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -501,20 +501,6 @@ main() {
501501
assertType(findNode.simple('v; // return'), 'int');
502502
}
503503

504-
test_functionExpression_asInvocationArgument_notSubtypeOfStaticType() async {
505-
await assertErrorsInCode(r'''
506-
class A {
507-
m(void f(int i)) {}
508-
}
509-
x() {
510-
A a = new A();
511-
a.m(() => 0);
512-
}''', [
513-
error(StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 63, 7),
514-
]);
515-
assertType(findNode.functionExpression('() => 0'), 'int Function()');
516-
}
517-
518504
test_initializer_hasStaticType() async {
519505
await resolveTestCode(r'''
520506
f() {

pkg/analyzer/test/src/dart/resolution/type_inference/function_expression_test.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analyzer/dart/analysis/features.dart';
6+
import 'package:analyzer/src/error/codes.dart';
67
import 'package:analyzer/src/generated/engine.dart';
78
import 'package:test_reflective_loader/test_reflective_loader.dart';
89

@@ -17,6 +18,50 @@ main() {
1718

1819
@reflectiveTest
1920
class FunctionExpressionTest extends DriverResolutionTest {
21+
test_contextFunctionType_returnType_async_blockBody_futureOrVoid() async {
22+
var expectedErrors = expectedErrorsByNullability(
23+
nullable: [
24+
error(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 72, 1),
25+
],
26+
legacy: [],
27+
);
28+
await assertErrorsInCode('''
29+
import 'dart:async';
30+
31+
FutureOr<void> Function() v = () async {
32+
return 0;
33+
};
34+
''', expectedErrors);
35+
_assertReturnType(
36+
'() async {',
37+
typeStringByNullability(
38+
nullable: 'Future<void>',
39+
legacy: 'Future<int>',
40+
),
41+
);
42+
}
43+
44+
test_contextFunctionType_returnType_async_blockBody_futureVoid() async {
45+
var expectedErrors = expectedErrorsByNullability(
46+
nullable: [
47+
error(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 48, 1),
48+
],
49+
legacy: [],
50+
);
51+
await assertErrorsInCode('''
52+
Future<void> Function() v = () async {
53+
return 0;
54+
};
55+
''', expectedErrors);
56+
_assertReturnType(
57+
'() async {',
58+
typeStringByNullability(
59+
nullable: 'Future<void>',
60+
legacy: 'Future<int>',
61+
),
62+
);
63+
}
64+
2065
test_contextFunctionType_returnType_async_expressionBody() async {
2166
await assertNoErrorsInCode('''
2267
Future<num> Function() v = () async => 0;
@@ -118,6 +163,24 @@ int Function() v = () {
118163
_assertReturnType('() {', 'int');
119164
}
120165

166+
test_contextFunctionType_returnType_sync_blockBody_void() async {
167+
var expectedErrors = expectedErrorsByNullability(nullable: [
168+
error(StaticTypeWarningCode.RETURN_OF_INVALID_TYPE_FROM_CLOSURE, 34, 1),
169+
], legacy: []);
170+
await assertErrorsInCode('''
171+
void Function() v = () {
172+
return 0;
173+
};
174+
''', expectedErrors);
175+
_assertReturnType(
176+
'() {',
177+
typeStringByNullability(
178+
nullable: 'void',
179+
legacy: 'int',
180+
),
181+
);
182+
}
183+
121184
test_contextFunctionType_returnType_sync_expressionBody() async {
122185
await assertNoErrorsInCode('''
123186
num Function() v = () => 0;

tests/language/type/implicit_error_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ void main() {
2929

3030
Expect.throws<TypeError>(() {
3131
int x = wrap(noToString); // Implicit cast should throw
32-
return x;
3332
}, (e) {
3433
e.toString(); // Should not throw.
3534
return true;

0 commit comments

Comments
 (0)