Skip to content

Commit 0501720

Browse files
srawlinsCommit Queue
authored andcommitted
linter: Consider @awaitNotRequired in the discared_futures rule
Work towards #46218 Change-Id: I9e356ab28996351d2ebfb15f655816f35a825339 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424480 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 509e103 commit 0501720

File tree

4 files changed

+89
-75
lines changed

4 files changed

+89
-75
lines changed

pkg/linter/lib/src/extensions.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ extension ElementExtension on Element2? {
328328
_ => this,
329329
};
330330

331+
/// Whether this is an [Annotatable] which is annotated with `@awaitNotRequired`.
332+
bool get hasAwaitNotRequired =>
333+
this is Annotatable &&
334+
(this! as Annotatable).metadata2.hasAwaitNotRequired;
335+
331336
bool get isDartCorePrint {
332337
var self = this;
333338
return self is TopLevelFunctionElement &&
@@ -336,7 +341,23 @@ extension ElementExtension on Element2? {
336341
}
337342
}
338343

339-
extension ExpressionExtension on Expression? {
344+
extension ExpressionExtension on Expression {
345+
/// Returns whether `await` is not required for this expression.
346+
// TODO(srawlins): Handle inheritence in each of these cases; the
347+
// `@awaitNotRequired` annotation should be inherited.
348+
bool get isAwaitNotRequired => switch (this) {
349+
BinaryExpression(:var element) => element.hasAwaitNotRequired,
350+
MethodInvocation(:var methodName) => methodName.element.hasAwaitNotRequired,
351+
PrefixedIdentifier(:var identifier) =>
352+
identifier.element.hasAwaitNotRequired,
353+
PrefixExpression(:var element) => element.hasAwaitNotRequired,
354+
PropertyAccess(:var propertyName) =>
355+
propertyName.element.hasAwaitNotRequired,
356+
_ => false,
357+
};
358+
}
359+
360+
extension ExpressionNullableExtension on Expression? {
340361
/// A very, very, very rough approximation of the context type of this node.
341362
///
342363
/// This approximation will never be accurate for some expressions.

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:analyzer/dart/element/element2.dart';
77
import 'package:analyzer/dart/element/type.dart';
88

99
import '../analyzer.dart';
10+
import '../extensions.dart';
1011

1112
const _desc =
1213
'There should be no `Future`-returning calls in synchronous functions unless they '
@@ -57,6 +58,10 @@ class _Visitor extends SimpleAstVisitor<void> {
5758
expr = expression;
5859
}
5960

61+
if (expr.isAwaitNotRequired) {
62+
return;
63+
}
64+
6065
var type = expr.staticType;
6166
if (type == null) {
6267
return;
@@ -70,8 +75,6 @@ class _Visitor extends SimpleAstVisitor<void> {
7075

7176
_reportOnExpression(expr);
7277
}
73-
74-
// TODO(srawlins): Take `@awaitNotRequired` into account.
7578
}
7679

7780
@override
@@ -113,6 +116,10 @@ class _Visitor extends SimpleAstVisitor<void> {
113116
}
114117

115118
void _visit(Expression expr) {
119+
if (expr.isAwaitNotRequired) {
120+
return;
121+
}
122+
116123
if ((expr.staticType.isFutureOrFutureOr) &&
117124
!_isEnclosedInAsyncFunctionBody(expr) &&
118125
expr is! AssignmentExpression) {

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

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class _Visitor extends SimpleAstVisitor<void> {
6464
return;
6565
}
6666

67-
if (_isAwaitNotRequired(expr)) {
67+
if (expr.isAwaitNotRequired) {
6868
return;
6969
}
7070

@@ -80,39 +80,6 @@ class _Visitor extends SimpleAstVisitor<void> {
8080
_visit(node.expression);
8181
}
8282

83-
bool _isAwaitNotRequired(Expression node) {
84-
// TODO(srawlins): Handle inheritence in each of these cases; the
85-
// `@awaitNotRequired` annotation should be inherited.
86-
switch (node) {
87-
case BinaryExpression():
88-
if (node.element.hasAwaitNotRequired) {
89-
return true;
90-
}
91-
92-
case MethodInvocation():
93-
if (node.methodName.element.hasAwaitNotRequired) {
94-
return true;
95-
}
96-
97-
case PrefixedIdentifier():
98-
if (node.identifier.element.hasAwaitNotRequired) {
99-
return true;
100-
}
101-
102-
case PrefixExpression():
103-
if (node.element.hasAwaitNotRequired) {
104-
return true;
105-
}
106-
107-
case PropertyAccess():
108-
if (node.propertyName.element.hasAwaitNotRequired) {
109-
return true;
110-
}
111-
}
112-
113-
return false;
114-
}
115-
11683
bool _isEnclosedInAsyncFunctionBody(AstNode node) {
11784
var enclosingFunctionBody = node.thisOrAncestorOfType<FunctionBody>();
11885
return enclosingFunctionBody?.isAsynchronous ?? false;
@@ -136,7 +103,7 @@ class _Visitor extends SimpleAstVisitor<void> {
136103
_isMapClass(expr.methodName.element?.enclosingElement2);
137104

138105
void _visit(Expression expr) {
139-
if (_isAwaitNotRequired(expr)) {
106+
if (expr.isAwaitNotRequired) {
140107
return;
141108
}
142109

@@ -148,9 +115,3 @@ class _Visitor extends SimpleAstVisitor<void> {
148115
}
149116
}
150117
}
151-
152-
extension on Element2? {
153-
bool get hasAwaitNotRequired =>
154-
this is Annotatable &&
155-
(this! as Annotatable).metadata2.hasAwaitNotRequired;
156-
}

pkg/linter/test/rules/discarded_futures_test.dart

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ void main() {
1414

1515
@reflectiveTest
1616
class DiscardedFuturesTest extends LintRuleTest {
17+
@override
18+
bool get addMetaPackageDep => true;
19+
1720
@override
1821
String get lintRule => LintNames.discarded_futures;
1922

@@ -101,7 +104,7 @@ void foo() {
101104
}
102105
103106
Future<int> g() async => 0;
104-
''');
107+
''');
105108
}
106109

107110
Future<void> test_assignment_ok_implicit_setOfFuture() async {
@@ -111,7 +114,7 @@ void foo() {
111114
}
112115
113116
Future<int> g() async => 0;
114-
''');
117+
''');
115118
}
116119

117120
Future<void> test_cascade_ok_future() async {
@@ -146,7 +149,7 @@ void foo() {
146149
extension on int {
147150
Future<int> g() async => this;
148151
}
149-
''',
152+
''',
150153
[lint(26, 1)],
151154
);
152155
}
@@ -306,18 +309,28 @@ Future<int> g() async => 0;
306309
Future<void> test_function() async {
307310
await assertDiagnostics(
308311
r'''
309-
void recreateDir(String path) {
310-
deleteDir(path);
311-
createDir(path);
312+
void f() {
313+
g();
312314
}
313315
314-
Future<void> deleteDir(String path) async {}
315-
Future<void> createDir(String path) async {}
316+
Future<int> g() async => 7;
316317
''',
317-
[lint(34, 9), lint(53, 9)],
318+
[lint(13, 1)],
318319
);
319320
}
320321

322+
Future<void> test_function_awaitNotRequired() async {
323+
await assertNoDiagnostics(r'''
324+
import 'package:meta/meta.dart';
325+
void f() {
326+
g();
327+
}
328+
329+
@awaitNotRequired
330+
Future<int> g() async => 7;
331+
''');
332+
}
333+
321334
Future<void> test_function_closure() async {
322335
await assertDiagnostics(
323336
r'''
@@ -361,6 +374,21 @@ Future<int> g() async => 0;
361374
''');
362375
}
363376

377+
Future<void> test_function_futureOr() async {
378+
await assertDiagnostics(
379+
'''
380+
import 'dart:async';
381+
382+
void f() {
383+
g();
384+
}
385+
386+
FutureOr<int> g() async => 0;
387+
''',
388+
[lint(35, 1)],
389+
);
390+
}
391+
364392
Future<void> test_function_ok_async() async {
365393
await assertNoDiagnostics(r'''
366394
Future<void> recreateDir(String path) async {
@@ -431,17 +459,15 @@ Future<int> g() async => 0;
431459
Future<void> test_method() async {
432460
await assertDiagnostics(
433461
r'''
434-
class Dir{
435-
void recreateDir(String path) {
436-
deleteDir(path);
437-
createDir(path);
462+
class C {
463+
void m() {
464+
g();
438465
}
439466
440-
Future<void> deleteDir(String path) async {}
441-
Future<void> createDir(String path) async {}
467+
Future<void> g() async {}
442468
}
443469
''',
444-
[lint(49, 9), lint(70, 9)],
470+
[lint(27, 1)],
445471
);
446472
}
447473

@@ -497,6 +523,20 @@ Future<int> g() async => 0;
497523
''');
498524
}
499525

526+
Future<void> test_method_awaitNotRequired() async {
527+
await assertNoDiagnostics(r'''
528+
import 'package:meta/meta.dart';
529+
class C {
530+
void m() {
531+
g();
532+
}
533+
534+
@awaitNotRequired
535+
Future<void> g() async {}
536+
}
537+
''');
538+
}
539+
500540
Future<void> test_method_record_namedAssignment_ok_future() async {
501541
await assertNoDiagnostics(r'''
502542
void foo() {
@@ -702,21 +742,6 @@ Future<int> g() async => 0;
702742
''');
703743
}
704744

705-
Future<void> test_trigger_futureOr() async {
706-
await assertDiagnostics(
707-
'''
708-
import 'dart:async';
709-
710-
void foo() {
711-
g();
712-
}
713-
714-
FutureOr<int> g() async => 0;
715-
''',
716-
[lint(37, 1)],
717-
);
718-
}
719-
720745
Future<void> test_variable_assignment() async {
721746
await assertDiagnostics(
722747
r'''

0 commit comments

Comments
 (0)