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

Commit 755ca4f

Browse files
authored
feat: add static code diagnostic avoid-redundant-async-on-load (#1168)
* feat: add new rule correct-game-instantiating * feat: add static code diagnostic avoid-initializing-in-on-mount * feat: add avoid-creating-vector-in-update * feat: add static code diagnostic avoid-redundant-async-on-load
1 parent 588a34d commit 755ca4f

File tree

8 files changed

+231
-5
lines changed

8 files changed

+231
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* feat: add static code diagnostic [`correct-game-instantiating`](https://dcm.dev/docs/individuals/rules/flame/correct-game-instantiating).
77
* feat: add static code diagnostic [`avoid-initializing-in-on-mount`](https://dcm.dev/docs/individuals/rules/flame/avoid-initializing-in-on-mount).
88
* feat: add static code diagnostic [`avoid-creating-vector-in-update`](https://dcm.dev/docs/individuals/rules/flame/avoid-creating-vector-in-update).
9+
* feat: add static code diagnostic [`avoid-redundant-async-on-load`](https://dcm.dev/docs/individuals/rules/flame/avoid-redundant-async-on-load).
910

1011
## 5.5.1
1112

lib/src/analyzers/lint_analyzer/rules/rules_factory.dart

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'rules_list/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart';
2121
import 'rules_list/avoid_passing_async_when_sync_expected/avoid_passing_async_when_sync_expected_rule.dart';
2222
import 'rules_list/avoid_preserve_whitespace_false/avoid_preserve_whitespace_false_rule.dart';
2323
import 'rules_list/avoid_redundant_async/avoid_redundant_async_rule.dart';
24+
import 'rules_list/avoid_redundant_async_on_load/avoid_redundant_async_on_load_rule.dart';
2425
import 'rules_list/avoid_returning_widgets/avoid_returning_widgets_rule.dart';
2526
import 'rules_list/avoid_shrink_wrap_in_lists/avoid_shrink_wrap_in_lists_rule.dart';
2627
import 'rules_list/avoid_throw_in_catch_block/avoid_throw_in_catch_block_rule.dart';
@@ -103,6 +104,7 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
103104
AvoidPassingAsyncWhenSyncExpectedRule.new,
104105
AvoidPreserveWhitespaceFalseRule.ruleId: AvoidPreserveWhitespaceFalseRule.new,
105106
AvoidRedundantAsyncRule.ruleId: AvoidRedundantAsyncRule.new,
107+
AvoidRedundantAsyncOnLoadRule.ruleId: AvoidRedundantAsyncOnLoadRule.new,
106108
AvoidReturningWidgetsRule.ruleId: AvoidReturningWidgetsRule.new,
107109
AvoidShrinkWrapInListsRule.ruleId: AvoidShrinkWrapInListsRule.new,
108110
AvoidThrowInCatchBlockRule.ruleId: AvoidThrowInCatchBlockRule.new,

lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/avoid_redundant_async_rule.dart

+1-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ class AvoidRedundantAsyncRule extends CommonRule {
3838
return visitor.nodes.map(
3939
(node) => createIssue(
4040
rule: this,
41-
location: nodeLocation(
42-
node: node,
43-
source: source,
44-
),
41+
location: nodeLocation(node: node, source: source),
4542
message: _warningMessage,
4643
),
4744
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// ignore_for_file: public_member_api_docs
2+
3+
import 'package:analyzer/dart/ast/ast.dart';
4+
import 'package:analyzer/dart/ast/visitor.dart';
5+
import 'package:analyzer/dart/element/type.dart';
6+
import 'package:collection/collection.dart';
7+
8+
import '../../../../../utils/flame_type_utils.dart';
9+
import '../../../../../utils/node_utils.dart';
10+
import '../../../lint_utils.dart';
11+
import '../../../models/internal_resolved_unit_result.dart';
12+
import '../../../models/issue.dart';
13+
import '../../../models/severity.dart';
14+
import '../../models/flame_rule.dart';
15+
import '../../rule_utils.dart';
16+
17+
part 'visitor.dart';
18+
19+
class AvoidRedundantAsyncOnLoadRule extends FlameRule {
20+
static const String ruleId = 'avoid-redundant-async-on-load';
21+
22+
static const _warningMessage = "Avoid unnecessary async 'onLoad'.";
23+
24+
AvoidRedundantAsyncOnLoadRule([Map<String, Object> config = const {}])
25+
: super(
26+
id: ruleId,
27+
severity: readSeverity(config, Severity.warning),
28+
excludes: readExcludes(config),
29+
includes: readIncludes(config),
30+
);
31+
32+
@override
33+
Iterable<Issue> check(InternalResolvedUnitResult source) {
34+
final visitor = _Visitor();
35+
36+
source.unit.visitChildren(visitor);
37+
38+
return visitor.declarations
39+
.map((declaration) => createIssue(
40+
rule: this,
41+
location: nodeLocation(node: declaration, source: source),
42+
message: _warningMessage,
43+
))
44+
.toList(growable: false);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
part of 'avoid_redundant_async_on_load_rule.dart';
2+
3+
class _Visitor extends SimpleAstVisitor<void> {
4+
final _declarations = <MethodDeclaration>[];
5+
6+
Iterable<MethodDeclaration> get declarations => _declarations;
7+
8+
@override
9+
void visitClassDeclaration(ClassDeclaration node) {
10+
super.visitClassDeclaration(node);
11+
12+
final type = node.extendsClause?.superclass.type;
13+
if (type == null || !isComponentOrSubclass(type)) {
14+
return;
15+
}
16+
17+
final onLoadMethod = node.members.firstWhereOrNull((member) =>
18+
member is MethodDeclaration &&
19+
member.name.lexeme == 'onLoad' &&
20+
isOverride(member.metadata));
21+
22+
if (onLoadMethod is MethodDeclaration) {
23+
if (_hasFutureType(onLoadMethod.returnType?.type) &&
24+
_hasRedundantAsync(onLoadMethod.body)) {
25+
_declarations.add(onLoadMethod);
26+
}
27+
}
28+
}
29+
30+
bool _hasFutureType(DartType? type) =>
31+
type != null && (type.isDartAsyncFuture || type.isDartAsyncFutureOr);
32+
33+
bool _hasRedundantAsync(FunctionBody body) {
34+
if (body is ExpressionFunctionBody) {
35+
final type = body.expression.staticType;
36+
37+
if (type != null &&
38+
(type.isDartAsyncFuture || type.isDartAsyncFutureOr)) {
39+
return false;
40+
}
41+
}
42+
43+
final asyncVisitor = _AsyncVisitor(body);
44+
body.parent?.visitChildren(asyncVisitor);
45+
46+
return !asyncVisitor.hasValidAsync;
47+
}
48+
}
49+
50+
class _AsyncVisitor extends RecursiveAstVisitor<void> {
51+
final FunctionBody body;
52+
53+
bool hasValidAsync = false;
54+
55+
_AsyncVisitor(this.body);
56+
57+
@override
58+
void visitReturnStatement(ReturnStatement node) {
59+
super.visitReturnStatement(node);
60+
61+
hasValidAsync = true;
62+
}
63+
64+
@override
65+
void visitAwaitExpression(AwaitExpression node) {
66+
super.visitAwaitExpression(node);
67+
68+
hasValidAsync = true;
69+
}
70+
}

test/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/avoid_redundant_async_rule_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void main() {
1919
);
2020
});
2121

22-
test('reports about found issues with the default config', () async {
22+
test('reports about found issues', () async {
2323
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
2424
final issues = AvoidRedundantAsyncRule().check(unit);
2525

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/models/severity.dart';
2+
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async_on_load/avoid_redundant_async_on_load_rule.dart';
3+
import 'package:test/test.dart';
4+
5+
import '../../../../../helpers/rule_test_helper.dart';
6+
7+
const _examplePath = 'avoid_redundant_async_on_load/examples/example.dart';
8+
9+
void main() {
10+
group('AvoidRedundantAsyncOnLoadRule', () {
11+
test('initialization', () async {
12+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
13+
final issues = AvoidRedundantAsyncOnLoadRule().check(unit);
14+
15+
RuleTestHelper.verifyInitialization(
16+
issues: issues,
17+
ruleId: 'avoid-redundant-async-on-load',
18+
severity: Severity.warning,
19+
);
20+
});
21+
22+
test('reports about found issues', () async {
23+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
24+
final issues = AvoidRedundantAsyncOnLoadRule().check(unit);
25+
26+
RuleTestHelper.verifyIssues(
27+
issues: issues,
28+
startLines: [6, 14, 22],
29+
startColumns: [3, 3, 3],
30+
locationTexts: [
31+
'Future<void> onLoad() {\n'
32+
" print('hello');\n"
33+
' }',
34+
'FutureOr<void> onLoad() {\n'
35+
" print('hello');\n"
36+
' }',
37+
'FutureOr<void> onLoad() async {\n'
38+
" print('hello');\n"
39+
' }',
40+
],
41+
messages: [
42+
"Avoid unnecessary async 'onLoad'.",
43+
"Avoid unnecessary async 'onLoad'.",
44+
"Avoid unnecessary async 'onLoad'.",
45+
],
46+
);
47+
});
48+
});
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import 'dart:async';
2+
3+
class MyComponent extends Component {
4+
// LINT
5+
@override
6+
Future<void> onLoad() {
7+
print('hello');
8+
}
9+
}
10+
11+
class MyComponent extends Component {
12+
// LINT
13+
@override
14+
FutureOr<void> onLoad() {
15+
print('hello');
16+
}
17+
}
18+
19+
class MyComponent extends Component {
20+
// LINT
21+
@override
22+
FutureOr<void> onLoad() async {
23+
print('hello');
24+
}
25+
}
26+
27+
class MyComponent extends Component {
28+
@override
29+
Future<void> onLoad() async {
30+
await someAsyncMethod();
31+
}
32+
33+
Future<void> someAsyncMethod() => Future.value(null);
34+
}
35+
36+
class MyComponent extends Component {
37+
@override
38+
Future<void> onLoad() async {
39+
return someAsyncMethod();
40+
}
41+
42+
Future<void> someAsyncMethod() => Future.value(null);
43+
}
44+
45+
class MyComponent extends Component {
46+
@override
47+
Future<void> onLoad() => someAsyncMethod();
48+
49+
Future<void> someAsyncMethod() => Future.value(null);
50+
}
51+
52+
class MyComponent extends Component {
53+
@override
54+
void onLoad() {
55+
print('hello');
56+
}
57+
}
58+
59+
class Component {
60+
FutureOr<void> onLoad() {}
61+
}

0 commit comments

Comments
 (0)