linter: Fix await_only_futures: Fix for type variables and intersection types
Fixes https://github.com/dart-lang/sdk/issues/58492 Change-Id: Iae036742b05815332553a540c95b4fdf5ad83413 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425402 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
committed by
Commit Queue
parent
c497d099db
commit
7d1c867f42
@@ -8,7 +8,6 @@ import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
|
||||
import '../analyzer.dart';
|
||||
import '../extensions.dart';
|
||||
|
||||
const _desc = r'Await only futures.';
|
||||
|
||||
@@ -24,7 +23,7 @@ class AwaitOnlyFutures extends LintRule {
|
||||
NodeLintRegistry registry,
|
||||
LinterContext context,
|
||||
) {
|
||||
var visitor = _Visitor(this);
|
||||
var visitor = _Visitor(this, context);
|
||||
registry.addAwaitExpression(this, visitor);
|
||||
}
|
||||
}
|
||||
@@ -32,22 +31,28 @@ class AwaitOnlyFutures extends LintRule {
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
final LintRule rule;
|
||||
|
||||
_Visitor(this.rule);
|
||||
final LinterContext context;
|
||||
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
@override
|
||||
void visitAwaitExpression(AwaitExpression node) {
|
||||
if (node.expression is NullLiteral) return;
|
||||
|
||||
var type = node.expression.staticType;
|
||||
if (!(type == null ||
|
||||
type.element3 is ExtensionTypeElement ||
|
||||
type.isDartAsyncFuture ||
|
||||
type is DynamicType ||
|
||||
type is InvalidType ||
|
||||
type.extendsClass('Future', 'dart.async') ||
|
||||
type.implementsInterface('Future', 'dart.async') ||
|
||||
type.isDartAsyncFutureOr)) {
|
||||
rule.reportAtToken(node.awaitKeyword, arguments: [type]);
|
||||
if (type == null || type is DynamicType) return;
|
||||
type = context.typeSystem.promoteToNonNull(type);
|
||||
if (type.isDartAsyncFutureOr) return;
|
||||
if (type.element3 is ExtensionTypeElement) return;
|
||||
if (type is InvalidType) return;
|
||||
|
||||
if (context.typeSystem.isAssignableTo(
|
||||
type,
|
||||
context.typeProvider.futureDynamicType,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rule.reportAtToken(node.awaitKeyword, arguments: [type]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,28 @@ class AwaitOnlyFuturesTest extends LintRuleTest {
|
||||
|
||||
test_dynamic() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f(dynamic future) async {
|
||||
await future;
|
||||
void f(dynamic p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
// TODO(srawlins): Test `await x` for `T extends Future` type variable.
|
||||
|
||||
test_extensionType_implementingFuture() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
extension type E(Future f) implements Future { }
|
||||
extension type E(Future f) implements Future {}
|
||||
|
||||
void f() async {
|
||||
await E(Future.value());
|
||||
void f(E p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_extensionType_implementingFuture_nullable() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
extension type E(Future f) implements Future {}
|
||||
|
||||
void f(E? p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
@@ -55,8 +63,16 @@ void f() async {
|
||||
|
||||
test_future() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f(Future<void> future) async {
|
||||
await future;
|
||||
void f(Future<void> p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_future_nullable() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f(Future<void>? p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
@@ -64,16 +80,25 @@ void f(Future<void> future) async {
|
||||
test_futureOr() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
import 'dart:async';
|
||||
void f(FutureOr<int> future) async {
|
||||
await future;
|
||||
void f(FutureOr<int> p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_futureOr_nullable() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
import 'dart:async';
|
||||
void f(FutureOr<int>? p) async {
|
||||
await p;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_futureSubClass() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f(MyFuture future) async {
|
||||
await future;
|
||||
void f(MyFuture<int> p) async {
|
||||
await p;
|
||||
}
|
||||
abstract class MyFuture<T> implements Future<T> {}
|
||||
''');
|
||||
@@ -90,6 +115,16 @@ void f() async {
|
||||
);
|
||||
}
|
||||
|
||||
test_intersectionType_subtypeOfFuture() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f<T>(T f) async {
|
||||
if (f is Future<int>) {
|
||||
await f;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_null() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f() async {
|
||||
@@ -98,6 +133,25 @@ void f() async {
|
||||
''');
|
||||
}
|
||||
|
||||
test_typeVariable() async {
|
||||
await assertDiagnostics(
|
||||
r'''
|
||||
void f<T>(T f) async {
|
||||
await f;
|
||||
}
|
||||
''',
|
||||
[lint(25, 5)],
|
||||
);
|
||||
}
|
||||
|
||||
test_typeVariable_boundToFuture() async {
|
||||
await assertNoDiagnostics(r'''
|
||||
void f<T extends Future<dynamic>>(T f) async {
|
||||
await f;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_undefinedClass() async {
|
||||
await assertDiagnostics(
|
||||
r'''
|
||||
|
||||
Reference in New Issue
Block a user