Skip to content

Commit f2d4ca7

Browse files
srawlinsCommit Queue
authored andcommitted
linter: unawaited_futures: catch Future subtypes
Change-Id: Iabdfc1ec4984d6717cc3d85729b577fb2385e749 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424401 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 3021cc4 commit f2d4ca7

File tree

2 files changed

+47
-23
lines changed

2 files changed

+47
-23
lines changed

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/ast/visitor.dart';
77
import 'package:analyzer/dart/element/element.dart';
8+
import 'package:analyzer/dart/element/type.dart';
89

910
import '../analyzer.dart';
1011
import '../extensions.dart';
@@ -51,10 +52,7 @@ class _Visitor extends SimpleAstVisitor<void> {
5152
if (expr is AssignmentExpression) return;
5253

5354
var type = expr.staticType;
54-
if (type == null) {
55-
return;
56-
}
57-
if (!type.implementsInterface('Future', 'dart.async')) {
55+
if (type == null || !type.isOrImplementsFuture) {
5856
return;
5957
}
6058

@@ -96,7 +94,7 @@ class _Visitor extends SimpleAstVisitor<void> {
9694
bool _isMapClass(Element? e) =>
9795
e is ClassElement && e.name3 == 'Map' && e.library2.name3 == 'dart.core';
9896

99-
/// Detects Map.putIfAbsent invocations.
97+
/// Detects `Map.putIfAbsent` invocations.
10098
bool _isMapPutIfAbsentInvocation(Expression expr) =>
10199
expr is MethodInvocation &&
102100
expr.methodName.name == 'putIfAbsent' &&
@@ -107,11 +105,23 @@ class _Visitor extends SimpleAstVisitor<void> {
107105
return;
108106
}
109107

110-
// TODO(srawlins): Check whether `expr`'s static type _implements_ `Future`.
111-
if ((expr.staticType?.isDartAsyncFuture ?? false) &&
112-
_isEnclosedInAsyncFunctionBody(expr) &&
113-
expr is! AssignmentExpression) {
108+
var type = expr.staticType;
109+
if (type == null || !type.isOrImplementsFuture) {
110+
return;
111+
}
112+
113+
if (_isEnclosedInAsyncFunctionBody(expr) && expr is! AssignmentExpression) {
114114
rule.reportAtNode(expr);
115115
}
116116
}
117117
}
118+
119+
extension on DartType {
120+
/// Whether this type is `Future` from dart:async, or is a subtype thereof.
121+
bool get isOrImplementsFuture {
122+
var typeElement = element3;
123+
if (typeElement is! InterfaceElement) return false;
124+
return isDartAsyncFuture ||
125+
typeElement.allSupertypes.any((t) => t.isDartAsyncFuture);
126+
}
127+
}

pkg/linter/test/rules/unawaited_futures_test.dart

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,6 @@ void f<T extends Future<void>>(T p) async {
5757
''');
5858
}
5959

60-
test_classImplementsFuture() async {
61-
// https://github.com/dart-lang/linter/issues/2211
62-
await assertDiagnostics(
63-
r'''
64-
void f(Future2 p) async {
65-
g(p);
66-
}
67-
Future2 g(Future2 p) => p;
68-
abstract class Future2 implements Future {}
69-
''',
70-
[lint(28, 5)],
71-
);
72-
}
73-
7460
test_functionCall_assigned() async {
7561
await assertNoDiagnostics(r'''
7662
Future<int> f() async {
@@ -90,6 +76,20 @@ Future<int> g() => Future.value(0);
9076
''');
9177
}
9278

79+
test_functionCall_classImplementsFuture() async {
80+
// https://github.com/dart-lang/linter/issues/2211
81+
await assertDiagnostics(
82+
r'''
83+
void f(Future2 p) async {
84+
g(p);
85+
}
86+
Future2 g(Future2 p) => p;
87+
abstract class Future2 implements Future {}
88+
''',
89+
[lint(28, 5)],
90+
);
91+
}
92+
9393
test_functionCall_inListContext() async {
9494
await assertNoDiagnostics(r'''
9595
void f() async {
@@ -123,6 +123,20 @@ Future<int> g() => Future.value(0);
123123
''');
124124
}
125125

126+
test_functionCall_interpolated_unawaited_classImplementsFuture() async {
127+
await assertDiagnostics(
128+
r'''
129+
void f() async {
130+
'${g()}';
131+
}
132+
Future2<int> g() => f2;
133+
abstract class Future2<T> implements Future<T> {}
134+
external Future2<int> f2;
135+
''',
136+
[lint(22, 3)],
137+
);
138+
}
139+
126140
test_functionCall_nullableFuture_unawaited() async {
127141
await assertDiagnostics(
128142
r'''

0 commit comments

Comments
 (0)