From 87a618bc40db140a29c457a3244ff0f2c00aa8f9 Mon Sep 17 00:00:00 2001 From: Dmitry Zhifarsky Date: Fri, 27 Jan 2023 16:32:42 +0400 Subject: [PATCH 1/2] feat: support context.mounted for use-setstate-synchronously --- CHANGELOG.md | 1 + .../use_setstate_synchronously/helpers.dart | 23 +++++++++++------- .../examples/context_mounted.dart | 24 +++++++++++++++++++ .../use_setstate_synchronously_rule_test.dart | 9 +++++++ 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd866b75e..8aae4e37dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * fix: export missing parts of public API. +* feat: support `context.mounted` for [`use-setstate-synchronously`](https://dcm.dev/docs/individuals/rules/flutter/use-setstate-synchronously). ## 5.5.0 diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/helpers.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/helpers.dart index 7ef6b54c72..f26aee108d 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/helpers.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/helpers.dart @@ -14,15 +14,15 @@ MountedFact _extractMountedCheck( bool permitAnd = true, bool expandOr = false, }) { - // ![this.]mounted + // ![this. || context.]mounted if (node is PrefixExpression && node.operator.type == TokenType.BANG && - _isIdentifier(_thisOr(node.operand), 'mounted')) { + _isIdentifier(_thisOrContextOr(node.operand), 'mounted')) { return false.asFact(); } - // [this.]mounted - if (_isIdentifier(_thisOr(node), 'mounted')) { + // [this. || context.]mounted + if (_isIdentifier(_thisOrContextOr(node), 'mounted')) { return true.asFact(); } @@ -91,10 +91,17 @@ bool _isDivergent(Statement node, {bool allowControlFlow = false}) => node is ExpressionStatement && node.expression is ThrowExpression; @pragma('vm:prefer-inline') -Expression _thisOr(Expression node) => - node is PropertyAccess && node.target is ThisExpression - ? node.propertyName - : node; +Expression _thisOrContextOr(Expression node) { + if (node is PropertyAccess && node.target is ThisExpression) { + return node.propertyName; + } + + if (node is PrefixedIdentifier && isBuildContext(node.prefix.staticType)) { + return node.identifier; + } + + return node; +} bool _blockDiverges(Statement block, {required bool allowControlFlow}) => block is Block diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart new file mode 100644 index 0000000000..30b349b5da --- /dev/null +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart @@ -0,0 +1,24 @@ +class _FooState extends State { + Widget build(BuildContext context) { + return FooWidget( + onChange: (value) async { + setState(() {}); + await fetchData(); + + if (context.mounted) setState(() {}); + }, + ); + } +} + +typedef VoidCallback = void Function(); + +class State { + void setState(VoidCallback callback) {} +} + +class BuildContext { + bool get mounted = true; +} + +Future fetchData() => Future.value('123'); diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/use_setstate_synchronously_rule_test.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/use_setstate_synchronously_rule_test.dart index 6acdc558f2..c9d88d18f9 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/use_setstate_synchronously_rule_test.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/use_setstate_synchronously_rule_test.dart @@ -8,6 +8,8 @@ const _examplePath = 'use_setstate_synchronously/examples/example.dart'; const _issuesPath = 'use_setstate_synchronously/examples/known_errors.dart'; const _trySwitchPath = 'use_setstate_synchronously/examples/extras_try_switch.dart'; +const _contextMountedPath = + 'use_setstate_synchronously/examples/context_mounted.dart'; void main() { group('UseSetStateSynchronouslyTest', () { @@ -115,5 +117,12 @@ void main() { ], ); }); + + test('reports no issues for context.mounted', () async { + final unit = await RuleTestHelper.resolveFromFile(_contextMountedPath); + final issues = UseSetStateSynchronouslyRule().check(unit); + + RuleTestHelper.verifyNoIssues(issues); + }); }); } From 2ecd43e3d10f3717aabee3bbc483cfb09591ac5a Mon Sep 17 00:00:00 2001 From: Dmitry Zhifarsky Date: Sat, 28 Jan 2023 11:11:46 +0400 Subject: [PATCH 2/2] test: fix test example --- .../use_setstate_synchronously/examples/context_mounted.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart index 30b349b5da..1951f8335e 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/use_setstate_synchronously/examples/context_mounted.dart @@ -18,7 +18,7 @@ class State { } class BuildContext { - bool get mounted = true; + bool get mounted => true; } Future fetchData() => Future.value('123');