linter: Support for inherited @awaitNotRequired annotations
Change-Id: I6c55f6423f6a44838674165ab311f31c64ffd399
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424682
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/linter/lib/src/extensions.dart b/pkg/linter/lib/src/extensions.dart
index b3a954e..0136f79 100644
--- a/pkg/linter/lib/src/extensions.dart
+++ b/pkg/linter/lib/src/extensions.dart
@@ -343,18 +343,31 @@
extension ExpressionExtension on Expression {
/// Returns whether `await` is not required for this expression.
- // TODO(srawlins): Handle inheritence in each of these cases; the
- // `@awaitNotRequired` annotation should be inherited.
- bool get isAwaitNotRequired => switch (this) {
- BinaryExpression(:var element) => element.hasAwaitNotRequired,
- MethodInvocation(:var methodName) => methodName.element.hasAwaitNotRequired,
- PrefixedIdentifier(:var identifier) =>
- identifier.element.hasAwaitNotRequired,
- PrefixExpression(:var element) => element.hasAwaitNotRequired,
- PropertyAccess(:var propertyName) =>
- propertyName.element.hasAwaitNotRequired,
- _ => false,
- };
+ bool get isAwaitNotRequired {
+ var element = switch (this) {
+ BinaryExpression(:var element) => element,
+ MethodInvocation(:var methodName) => methodName.element,
+ PrefixedIdentifier(:var identifier) => identifier.element,
+ PrefixExpression(:var element) => element,
+ PropertyAccess(:var propertyName) => propertyName.element,
+ _ => null,
+ };
+ if (element == null) return false;
+ if (element.hasAwaitNotRequired) return true;
+
+ var elementName = element.name3;
+ if (elementName == null) return false;
+
+ var enclosingElement = element.enclosingElement2;
+ if (enclosingElement is! InterfaceElement2) return false;
+
+ var superTypes = enclosingElement.allSupertypes;
+ var superMembers =
+ element is MethodElement2
+ ? superTypes.map((t) => t.getMethod2(elementName))
+ : superTypes.map((t) => t.getGetter2(elementName));
+ return superMembers.any((e) => e.hasAwaitNotRequired);
+ }
}
extension ExpressionNullableExtension on Expression? {
diff --git a/pkg/linter/test/rules/unawaited_futures_test.dart b/pkg/linter/test/rules/unawaited_futures_test.dart
index 2aa0854..08aee44 100644
--- a/pkg/linter/test/rules/unawaited_futures_test.dart
+++ b/pkg/linter/test/rules/unawaited_futures_test.dart
@@ -181,8 +181,6 @@
);
}
- // TODO(srawlins): Test that `@awaitNotRequired` is inherited.
-
test_functionCallInCascade_assignment() async {
await assertNoDiagnostics(r'''
void f() async {
@@ -199,11 +197,27 @@
await assertNoDiagnostics(r'''
import 'package:meta/meta.dart';
void f() async {
- C()..doAsync();
+ C()..m();
}
class C {
@awaitNotRequired
- Future<void> doAsync() async {}
+ Future<void> m() async {}
+}
+''');
+ }
+
+ test_functionCallInCascade_awaitNotRequiredInherited() async {
+ await assertNoDiagnostics(r'''
+import 'package:meta/meta.dart';
+void f() async {
+ C()..m();
+}
+class C {
+ @awaitNotRequired
+ Future<void> m() async {}
+}
+class D {
+ Future<void> m() => Future.value();
}
''');
}
@@ -233,6 +247,20 @@
}
test_instanceProperty_unawaited() async {
+ await assertDiagnostics(
+ r'''
+void f(C c) async {
+ c.p;
+}
+abstract class C {
+ Future<int> get p;
+}
+''',
+ [lint(22, 4)],
+ );
+ }
+
+ test_instanceProperty_unawaited_awaitNotRequired() async {
await assertNoDiagnostics(r'''
import 'package:meta/meta.dart';
void f(C c) async {
@@ -245,18 +273,20 @@
''');
}
- test_instanceProperty_unawaited_awaitNotRequired() async {
- await assertDiagnostics(
- r'''
-void f(C c) async {
- c.p;
+ test_instanceProperty_unawaited_awaitNotRequiredInherited() async {
+ await assertNoDiagnostics(r'''
+import 'package:meta/meta.dart';
+void f(D d) async {
+ d.p;
}
abstract class C {
+ @awaitNotRequired
Future<int> get p;
}
-''',
- [lint(22, 4)],
- );
+class D extends C {
+ Future<int> p = Future.value(7);
+}
+''');
}
test_parameter_unawaited() async {