Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.

Commit 4c04f83

Browse files
committed
feat: and-chain for use-setstate-synchronously
1 parent 3a523c3 commit 4c04f83

File tree

6 files changed

+54
-49
lines changed

6 files changed

+54
-49
lines changed

lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_async_setstate/avoid_async_setstate_rule.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:analyzer/dart/ast/ast.dart';
22
import 'package:analyzer/dart/ast/token.dart';
33
import 'package:analyzer/dart/ast/visitor.dart';
44

5+
import '../../../../../utils/flutter_types_utils.dart';
56
import '../../../../../utils/node_utils.dart';
67
import '../../../lint_utils.dart';
78
import '../../../models/internal_resolved_unit_result.dart';
Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
11
part of 'avoid_async_setstate_rule.dart';
22

3-
@pragma('vm:prefer-inline')
4-
bool _isIdentifier(Expression node, String ident) =>
5-
node is Identifier && node.name == ident;
6-
7-
@pragma('vm:prefer-inline')
8-
bool _isDivergent(Statement node) =>
9-
node is ReturnStatement ||
10-
node is ExpressionStatement && node.expression is ThrowExpression;
11-
12-
Expression _thisOr(Expression node) {
13-
if (node is PropertyAccess && node.target is ThisExpression) {
14-
return node.propertyName;
15-
}
16-
17-
return node;
18-
}
19-
203
/// If null, the check was not indicative of whether mounted was true.
21-
bool? _extractMountedCheck(Expression condition) {
4+
bool? _extractMountedCheck(Expression node) {
225
// ![this.]mounted
23-
if (condition is PrefixExpression &&
24-
condition.operator.type == TokenType.BANG &&
25-
_isIdentifier(_thisOr(condition.operand), 'mounted')) {
6+
if (node is PrefixExpression &&
7+
node.operator.type == TokenType.BANG &&
8+
_isIdentifier(_thisOr(node.operand), 'mounted')) {
269
return false;
2710
}
2811

2912
// [this.]mounted
30-
if (_isIdentifier(_thisOr(condition), 'mounted')) {
13+
if (_isIdentifier(_thisOr(node), 'mounted')) {
3114
return true;
3215
}
3316

17+
// mounted && ..
18+
if (node is BinaryExpression &&
19+
node.operator.type == TokenType.AMPERSAND_AMPERSAND) {
20+
return _extractMountedCheck(node.leftOperand) ??
21+
_extractMountedCheck(node.rightOperand);
22+
}
23+
3424
return null;
3525
}
3626

37-
bool _blockDiverges(Statement block) {
38-
if (block is! Block) {
39-
return _isDivergent(block);
40-
}
27+
@pragma('vm:prefer-inline')
28+
bool _isIdentifier(Expression node, String ident) =>
29+
node is Identifier && node.name == ident;
4130

42-
return block.statements.any(_isDivergent);
43-
}
31+
@pragma('vm:prefer-inline')
32+
bool _isDivergent(Statement node) =>
33+
node is ReturnStatement ||
34+
node is ExpressionStatement && node.expression is ThrowExpression;
35+
36+
@pragma('vm:prefer-inline')
37+
Expression _thisOr(Expression node) =>
38+
node is PropertyAccess && node.target is ThisExpression
39+
? node.propertyName
40+
: node;
41+
42+
bool _blockDiverges(Statement block) =>
43+
block is Block ? block.statements.any(_isDivergent) : _isDivergent(block);

lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_async_setstate/visitor.dart

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,30 @@ class _Visitor extends RecursiveAstVisitor<void> {
55

66
@override
77
void visitClassDeclaration(ClassDeclaration node) {
8-
if (node.extendsClause?.superclass.name.name == 'State') {
9-
node.visitChildren(this);
10-
}
11-
}
12-
13-
@override
14-
void visitBlockFunctionBody(BlockFunctionBody node) {
15-
if (node.isAsynchronous) {
8+
if (isWidgetStateOrSubclass(node.extendsClause?.superclass.type)) {
169
final visitor = _AsyncSetStateVisitor();
1710
node.visitChildren(visitor);
1811
nodes.addAll(visitor.nodes);
19-
20-
return;
2112
}
22-
23-
node.visitChildren(this);
2413
}
2514
}
2615

2716
class _AsyncSetStateVisitor extends RecursiveAstVisitor<void> {
2817
bool shouldBeMounted = true;
2918
final nodes = <SimpleIdentifier>[];
3019

20+
@override
21+
void visitBlockFunctionBody(BlockFunctionBody node) {
22+
final oldMounted = shouldBeMounted;
23+
shouldBeMounted = true;
24+
node.visitChildren(this);
25+
shouldBeMounted = oldMounted;
26+
}
27+
3128
@override
3229
void visitAwaitExpression(AwaitExpression node) {
3330
shouldBeMounted = false;
34-
node.visitChildren(this);
31+
super.visitAwaitExpression(node);
3532
}
3633

3734
@override
@@ -43,13 +40,12 @@ class _AsyncSetStateVisitor extends RecursiveAstVisitor<void> {
4340
nodes.add(node.methodName);
4441
}
4542

46-
node.visitChildren(this);
43+
super.visitMethodInvocation(node);
4744
}
4845

4946
@override
5047
void visitIfStatement(IfStatement node) {
5148
node.condition.visitChildren(this);
52-
5349
final oldMounted = shouldBeMounted;
5450
final newMounted = _extractMountedCheck(node.condition);
5551
shouldBeMounted = newMounted ?? shouldBeMounted;

test/src/analyzers/lint_analyzer/rules/rules_list/avoid_async_setstate/avoid_async_setstate_rule_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void main() {
2525

2626
RuleTestHelper.verifyIssues(
2727
issues: issues,
28-
startLines: [6, 11, 18, 33, 41],
28+
startLines: [6, 11, 18, 33, 48],
2929
startColumns: [10, 7, 7, 5, 9],
3030
locationTexts: [
3131
'setState',

test/src/analyzers/lint_analyzer/rules/rules_list/avoid_async_setstate/examples/example.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ class _FooState extends State<StatefulWidget> {
3131
return;
3232
}
3333
setState(() {}); // LINT
34+
35+
if (mounted && foo) {
36+
setState(() {});
37+
}
38+
39+
if (foo && !this.mounted) return;
40+
setState(() {});
3441
}
3542

3643
Widget build(context) {
@@ -45,3 +52,5 @@ class _FooState extends State<StatefulWidget> {
4552
);
4653
}
4754
}
55+
56+
class State {}

website/docs/rules/flutter/avoid-async-setstate.mdx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ does not make use of [`mounted`], but rather revolves around refactoring your st
1616

1717
:::info
1818

19-
Only these conditionals are considered for mounted-ness:
19+
Only these patterns are considered for mounted-ness:
2020

21-
- `if ([this.]mounted)`
22-
- `if (![this.]mounted)`
23-
- `while ([this.]mounted)`
24-
- `while (![this.]mounted)`
21+
- `if (mounted)`
22+
- `if (!mounted)`
23+
- `if (mounted && ..)`
2524

2625
If a `!mounted` check diverges, i.e. ends in a `return` or `throw`, the outer scope is considered mounted and vice versa:
2726

0 commit comments

Comments
 (0)