Skip to content

Commit 7d1c867

Browse files
srawlinsCommit Queue
authored andcommitted
linter: Fix await_only_futures: Fix for type variables and intersection types
Fixes #58492 Change-Id: Iae036742b05815332553a540c95b4fdf5ad83413 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425402 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent c497d09 commit 7d1c867

File tree

2 files changed

+84
-25
lines changed

2 files changed

+84
-25
lines changed

pkg/linter/lib/src/rules/await_only_futures.dart

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'package:analyzer/dart/element/element.dart';
88
import 'package:analyzer/dart/element/type.dart';
99

1010
import '../analyzer.dart';
11-
import '../extensions.dart';
1211

1312
const _desc = r'Await only futures.';
1413

@@ -24,30 +23,36 @@ class AwaitOnlyFutures extends LintRule {
2423
NodeLintRegistry registry,
2524
LinterContext context,
2625
) {
27-
var visitor = _Visitor(this);
26+
var visitor = _Visitor(this, context);
2827
registry.addAwaitExpression(this, visitor);
2928
}
3029
}
3130

3231
class _Visitor extends SimpleAstVisitor<void> {
3332
final LintRule rule;
3433

35-
_Visitor(this.rule);
34+
final LinterContext context;
35+
36+
_Visitor(this.rule, this.context);
3637

3738
@override
3839
void visitAwaitExpression(AwaitExpression node) {
3940
if (node.expression is NullLiteral) return;
4041

4142
var type = node.expression.staticType;
42-
if (!(type == null ||
43-
type.element3 is ExtensionTypeElement ||
44-
type.isDartAsyncFuture ||
45-
type is DynamicType ||
46-
type is InvalidType ||
47-
type.extendsClass('Future', 'dart.async') ||
48-
type.implementsInterface('Future', 'dart.async') ||
49-
type.isDartAsyncFutureOr)) {
50-
rule.reportAtToken(node.awaitKeyword, arguments: [type]);
43+
if (type == null || type is DynamicType) return;
44+
type = context.typeSystem.promoteToNonNull(type);
45+
if (type.isDartAsyncFutureOr) return;
46+
if (type.element3 is ExtensionTypeElement) return;
47+
if (type is InvalidType) return;
48+
49+
if (context.typeSystem.isAssignableTo(
50+
type,
51+
context.typeProvider.futureDynamicType,
52+
)) {
53+
return;
5154
}
55+
56+
rule.reportAtToken(node.awaitKeyword, arguments: [type]);
5257
}
5358
}

pkg/linter/test/rules/await_only_futures_test.dart

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,28 @@ class AwaitOnlyFuturesTest extends LintRuleTest {
1919

2020
test_dynamic() async {
2121
await assertNoDiagnostics(r'''
22-
void f(dynamic future) async {
23-
await future;
22+
void f(dynamic p) async {
23+
await p;
2424
}
2525
''');
2626
}
2727

28-
// TODO(srawlins): Test `await x` for `T extends Future` type variable.
29-
3028
test_extensionType_implementingFuture() async {
3129
await assertNoDiagnostics(r'''
32-
extension type E(Future f) implements Future { }
30+
extension type E(Future f) implements Future {}
3331
34-
void f() async {
35-
await E(Future.value());
32+
void f(E p) async {
33+
await p;
34+
}
35+
''');
36+
}
37+
38+
test_extensionType_implementingFuture_nullable() async {
39+
await assertNoDiagnostics(r'''
40+
extension type E(Future f) implements Future {}
41+
42+
void f(E? p) async {
43+
await p;
3644
}
3745
''');
3846
}
@@ -55,25 +63,42 @@ void f() async {
5563

5664
test_future() async {
5765
await assertNoDiagnostics(r'''
58-
void f(Future<void> future) async {
59-
await future;
66+
void f(Future<void> p) async {
67+
await p;
68+
}
69+
''');
70+
}
71+
72+
test_future_nullable() async {
73+
await assertNoDiagnostics(r'''
74+
void f(Future<void>? p) async {
75+
await p;
6076
}
6177
''');
6278
}
6379

6480
test_futureOr() async {
6581
await assertNoDiagnostics(r'''
6682
import 'dart:async';
67-
void f(FutureOr<int> future) async {
68-
await future;
83+
void f(FutureOr<int> p) async {
84+
await p;
85+
}
86+
''');
87+
}
88+
89+
test_futureOr_nullable() async {
90+
await assertNoDiagnostics(r'''
91+
import 'dart:async';
92+
void f(FutureOr<int>? p) async {
93+
await p;
6994
}
7095
''');
7196
}
7297

7398
test_futureSubClass() async {
7499
await assertNoDiagnostics(r'''
75-
void f(MyFuture future) async {
76-
await future;
100+
void f(MyFuture<int> p) async {
101+
await p;
77102
}
78103
abstract class MyFuture<T> implements Future<T> {}
79104
''');
@@ -90,6 +115,16 @@ void f() async {
90115
);
91116
}
92117

118+
test_intersectionType_subtypeOfFuture() async {
119+
await assertNoDiagnostics(r'''
120+
void f<T>(T f) async {
121+
if (f is Future<int>) {
122+
await f;
123+
}
124+
}
125+
''');
126+
}
127+
93128
test_null() async {
94129
await assertNoDiagnostics(r'''
95130
void f() async {
@@ -98,6 +133,25 @@ void f() async {
98133
''');
99134
}
100135

136+
test_typeVariable() async {
137+
await assertDiagnostics(
138+
r'''
139+
void f<T>(T f) async {
140+
await f;
141+
}
142+
''',
143+
[lint(25, 5)],
144+
);
145+
}
146+
147+
test_typeVariable_boundToFuture() async {
148+
await assertNoDiagnostics(r'''
149+
void f<T extends Future<dynamic>>(T f) async {
150+
await f;
151+
}
152+
''');
153+
}
154+
101155
test_undefinedClass() async {
102156
await assertDiagnostics(
103157
r'''

0 commit comments

Comments
 (0)