void_checks: Specify individual types which a FutureOr<void> target can accept (#2274)
* void_checks: Specify individual types which a FutureOr<void> target can accept
diff --git a/lib/src/rules/void_checks.dart b/lib/src/rules/void_checks.dart
index 24b9f14..2e59a69 100644
--- a/lib/src/rules/void_checks.dart
+++ b/lib/src/rules/void_checks.dart
@@ -42,7 +42,6 @@
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
final visitor = _Visitor(this, context);
- registry.addCompilationUnit(this, visitor);
registry.addMethodInvocation(this, visitor);
registry.addInstanceCreationExpression(this, visitor);
registry.addAssignmentExpression(this, visitor);
@@ -56,8 +55,6 @@
final LinterContext context;
final TypeSystem typeSystem;
- InterfaceType _futureDynamicType;
-
_Visitor(this.rule, this.context) : typeSystem = context.typeSystem;
bool isTypeAcceptableWhenExpectingVoid(DartType type) {
@@ -65,13 +62,24 @@
if (type.isDartCoreNull) return true;
if (type.isDartAsyncFuture &&
type is InterfaceType &&
- (type.typeArguments.first.isVoid ||
- type.typeArguments.first.isDartCoreNull)) {
+ isTypeAcceptableWhenExpectingVoid(type.typeArguments.first)) {
return true;
}
return false;
}
+ bool isTypeAcceptableWhenExpectingFutureOrVoid(DartType type) {
+ if (type.isDynamic) return true;
+ if (isTypeAcceptableWhenExpectingVoid(type)) return true;
+ if (type.isDartAsyncFutureOr &&
+ type is InterfaceType &&
+ isTypeAcceptableWhenExpectingFutureOrVoid(type.typeArguments.first)) {
+ return true;
+ }
+
+ return false;
+ }
+
@override
void visitAssignmentExpression(AssignmentExpression node) {
final type = node.writeType;
@@ -80,11 +88,6 @@
}
@override
- void visitCompilationUnit(CompilationUnit node) {
- _futureDynamicType = context.typeProvider.futureDynamicType;
- }
-
- @override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
final args = node.argumentList.arguments;
final parameters = node.constructorName.staticElement?.parameters;
@@ -131,11 +134,12 @@
checkedNode ??= node;
if (expectedType == null || type == null) {
return;
- } else if (expectedType.isVoid &&
- !isTypeAcceptableWhenExpectingVoid(type) ||
- expectedType.isDartAsyncFutureOr &&
- (expectedType as InterfaceType).typeArguments.first.isVoid &&
- !typeSystem.isAssignableTo(type, _futureDynamicType)) {
+ }
+ if (expectedType.isVoid && !isTypeAcceptableWhenExpectingVoid(type)) {
+ rule.reportLint(node);
+ } else if (expectedType.isDartAsyncFutureOr &&
+ (expectedType as InterfaceType).typeArguments.first.isVoid &&
+ !isTypeAcceptableWhenExpectingFutureOrVoid(type)) {
rule.reportLint(node);
} else if (checkedNode is FunctionExpression &&
checkedNode.body is! ExpressionFunctionBody &&
diff --git a/test/rules/experiments/nnbd/rules/void_checks.dart b/test/rules/experiments/nnbd/rules/void_checks.dart
new file mode 100644
index 0000000..99ce214
--- /dev/null
+++ b/test/rules/experiments/nnbd/rules/void_checks.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// test w/ `pub run test -N void_checks`
+
+import 'dart:async';
+
+void emptyFunctionExpressionReturningFutureOrVoid(FutureOr<void> Function() f) {
+ f = () {}; // OK
+}
diff --git a/test/rules/void_checks.dart b/test/rules/void_checks.dart
index 0fa4f7e..97feb86 100644
--- a/test/rules/void_checks.dart
+++ b/test/rules/void_checks.dart
@@ -207,3 +207,7 @@
void foo() {}
foo(0);
}
+
+void emptyFunctionExpressionReturningFutureOrVoid(FutureOr<void> Function() f) {
+ f = () {}; // OK
+}