mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
linter: ubcs: account for when clauses in if-like nodes
Fixes https://github.com/dart-lang/linter/issues/4795 Change-Id: I4cce15ddaf8a669921646fa81dd0e455859e46e3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332361 Auto-Submit: Samuel Rawlins <srawlins@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com> Commit-Queue: Phil Quitslund <pquitslund@google.com>
This commit is contained in:
parent
95c40c924a
commit
716b127a1a
|
@ -262,6 +262,10 @@ class AsyncStateVisitor extends SimpleAstVisitor<AsyncState> {
|
|||
AsyncState? visitCascadeExpression(CascadeExpression node) =>
|
||||
_asynchronousIfAnyIsAsync([node.target, ...node.cascadeSections]);
|
||||
|
||||
@override
|
||||
AsyncState? visitCaseClause(CaseClause node) =>
|
||||
node.guardedPattern.accept(this);
|
||||
|
||||
@override
|
||||
AsyncState? visitCatchClause(CatchClause node) =>
|
||||
node.body.accept(this)?.asynchronousOrNull;
|
||||
|
@ -269,7 +273,8 @@ class AsyncStateVisitor extends SimpleAstVisitor<AsyncState> {
|
|||
@override
|
||||
AsyncState? visitConditionalExpression(ConditionalExpression node) =>
|
||||
_visitIfLike(
|
||||
condition: node.condition,
|
||||
expression: node.condition,
|
||||
caseClause: null,
|
||||
thenBranch: node.thenExpression,
|
||||
elseBranch: node.elseExpression,
|
||||
);
|
||||
|
@ -367,16 +372,22 @@ class AsyncStateVisitor extends SimpleAstVisitor<AsyncState> {
|
|||
_asynchronousIfAnyIsAsync(
|
||||
[node.function, ...node.argumentList.arguments]);
|
||||
|
||||
@override
|
||||
AsyncState? visitGuardedPattern(GuardedPattern node) =>
|
||||
node.whenClause?.accept(this);
|
||||
|
||||
@override
|
||||
AsyncState? visitIfElement(IfElement node) => _visitIfLike(
|
||||
condition: node.expression,
|
||||
expression: node.expression,
|
||||
caseClause: node.caseClause,
|
||||
thenBranch: node.thenElement,
|
||||
elseBranch: node.elseElement,
|
||||
);
|
||||
|
||||
@override
|
||||
AsyncState? visitIfStatement(IfStatement node) => _visitIfLike(
|
||||
condition: node.expression,
|
||||
expression: node.expression,
|
||||
caseClause: node.caseClause,
|
||||
thenBranch: node.thenStatement,
|
||||
elseBranch: node.elseStatement,
|
||||
);
|
||||
|
@ -708,26 +719,54 @@ class AsyncStateVisitor extends SimpleAstVisitor<AsyncState> {
|
|||
.firstWhereOrNull((state) => state != null);
|
||||
}
|
||||
|
||||
/// Compute the [AsyncState] of an "if-like" node which has a [condition], a
|
||||
/// [thenBranch], and a possible [elseBranch].
|
||||
/// Compute the [AsyncState] of an "if-like" node which has a [expression], a
|
||||
/// possible [caseClause], a [thenBranch], and a possible [elseBranch].
|
||||
AsyncState? _visitIfLike({
|
||||
required AstNode condition,
|
||||
required Expression expression,
|
||||
required CaseClause? caseClause,
|
||||
required AstNode thenBranch,
|
||||
required AstNode? elseBranch,
|
||||
}) {
|
||||
if (reference == condition) {
|
||||
if (reference == expression) {
|
||||
// The async state of the condition is not affected by the case-clause,
|
||||
// then-branch, or else-branch.
|
||||
return null;
|
||||
}
|
||||
var conditionMountedCheck = condition.accept(this);
|
||||
var expressionAsyncState = expression.accept(this);
|
||||
if (reference == caseClause) {
|
||||
return switch (expressionAsyncState) {
|
||||
AsyncState.asynchronous => AsyncState.asynchronous,
|
||||
AsyncState.mountedCheck => AsyncState.mountedCheck,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
var caseClauseAsyncState = caseClause?.accept(this);
|
||||
// The condition state is the combined state of `expression` and
|
||||
// `caseClause`.
|
||||
var conditionAsyncState =
|
||||
switch ((expressionAsyncState, caseClauseAsyncState)) {
|
||||
// If the left is uninteresting, just return the state of the right.
|
||||
(null, _) => caseClauseAsyncState,
|
||||
// If the right is uninteresting, just return the state of the left.
|
||||
(_, null) => expressionAsyncState,
|
||||
// Anything on the left followed by async on the right is async.
|
||||
(_, AsyncState.asynchronous) => AsyncState.asynchronous,
|
||||
// An async state on the left is superseded by the state on the right.
|
||||
(AsyncState.asynchronous, _) => caseClauseAsyncState,
|
||||
// Otherwise just use the state on the left.
|
||||
(AsyncState.mountedCheck, _) => AsyncState.mountedCheck,
|
||||
(AsyncState.notMountedCheck, _) => AsyncState.notMountedCheck,
|
||||
};
|
||||
|
||||
if (reference == thenBranch) {
|
||||
return switch (conditionMountedCheck) {
|
||||
return switch (conditionAsyncState) {
|
||||
AsyncState.asynchronous => AsyncState.asynchronous,
|
||||
AsyncState.mountedCheck => AsyncState.mountedCheck,
|
||||
_ => null,
|
||||
};
|
||||
} else if (reference == elseBranch) {
|
||||
return switch (conditionMountedCheck) {
|
||||
return switch (conditionAsyncState) {
|
||||
AsyncState.asynchronous => AsyncState.asynchronous,
|
||||
AsyncState.notMountedCheck => AsyncState.mountedCheck,
|
||||
_ => null,
|
||||
|
@ -756,16 +795,15 @@ class AsyncStateVisitor extends SimpleAstVisitor<AsyncState> {
|
|||
return AsyncState.asynchronous;
|
||||
}
|
||||
|
||||
if (conditionMountedCheck == AsyncState.asynchronous) {
|
||||
if (conditionAsyncState == AsyncState.asynchronous) {
|
||||
return AsyncState.asynchronous;
|
||||
}
|
||||
|
||||
if (conditionMountedCheck == AsyncState.mountedCheck && elseTerminates) {
|
||||
if (conditionAsyncState == AsyncState.mountedCheck && elseTerminates) {
|
||||
return AsyncState.notMountedCheck;
|
||||
}
|
||||
|
||||
if (conditionMountedCheck == AsyncState.notMountedCheck &&
|
||||
thenTerminates) {
|
||||
if (conditionAsyncState == AsyncState.notMountedCheck && thenTerminates) {
|
||||
return AsyncState.notMountedCheck;
|
||||
}
|
||||
|
||||
|
|
|
@ -758,6 +758,22 @@ void foo(BuildContext context) async {
|
|||
expect(block.asyncStateFor(reference), AsyncState.notMountedCheck);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInCaseWhen_asyncInCondition() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
void foo(BuildContext context, Future<num> value) async {
|
||||
if (await value case int() when f(context)) {
|
||||
context /* ref */;
|
||||
}
|
||||
}
|
||||
|
||||
bool f(BuildContext context) => true;
|
||||
''');
|
||||
var ifStatement = findNode.ifStatement('if (');
|
||||
var reference = findNode.block('context /* ref */');
|
||||
expect(ifStatement.asyncStateFor(reference), AsyncState.asynchronous);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInElse_asyncInCondition() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -862,6 +878,20 @@ void foo(BuildContext context, bool m) async {
|
|||
expect(ifStatement.asyncStateFor(reference), AsyncState.mountedCheck);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInThen_asyncInCaseWhen() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
void foo(BuildContext context, Object value) async {
|
||||
if (value case int() when await Future.value(true)) {
|
||||
context /* ref */;
|
||||
}
|
||||
}
|
||||
''');
|
||||
var ifStatement = findNode.ifStatement('if (');
|
||||
var reference = findNode.block('context /* ref */');
|
||||
expect(ifStatement.asyncStateFor(reference), AsyncState.asynchronous);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInThen_asyncInCondition() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -932,6 +962,20 @@ void foo(BuildContext context) async {
|
|||
expect(ifStatement.asyncStateFor(reference), AsyncState.asynchronous);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInThen_mountedGuardInCaseWhen() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
void foo(BuildContext context, Object value) async {
|
||||
if (value case int() when context.mounted) {
|
||||
context /* ref */;
|
||||
}
|
||||
}
|
||||
''');
|
||||
var ifStatement = findNode.ifStatement('if (');
|
||||
var reference = findNode.block('context /* ref */');
|
||||
expect(ifStatement.asyncStateFor(reference), AsyncState.mountedCheck);
|
||||
}
|
||||
|
||||
test_ifStatement_referenceInThen_mountedInCondition() async {
|
||||
await resolveCode(r'''
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
|
Loading…
Reference in a new issue