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

Commit 588a34d

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

File tree

7 files changed

+205
-0
lines changed

7 files changed

+205
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* docs: remove old website
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).
8+
* feat: add static code diagnostic [`avoid-creating-vector-in-update`](https://dcm.dev/docs/individuals/rules/flame/avoid-creating-vector-in-update).
89

910
## 5.5.1
1011

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

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'rules_list/avoid_banned_imports/avoid_banned_imports_rule.dart';
55
import 'rules_list/avoid_border_all/avoid_border_all_rule.dart';
66
import 'rules_list/avoid_cascade_after_if_null/avoid_cascade_after_if_null_rule.dart';
77
import 'rules_list/avoid_collection_methods_with_unrelated_types/avoid_collection_methods_with_unrelated_types_rule.dart';
8+
import 'rules_list/avoid_creating_vector_in_update/avoid_creating_vector_in_update_rule.dart';
89
import 'rules_list/avoid_double_slash_imports/avoid_double_slash_imports_rule.dart';
910
import 'rules_list/avoid_duplicate_exports/avoid_duplicate_exports_rule.dart';
1011
import 'rules_list/avoid_dynamic/avoid_dynamic_rule.dart';
@@ -84,6 +85,7 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
8485
AvoidCascadeAfterIfNullRule.ruleId: AvoidCascadeAfterIfNullRule.new,
8586
AvoidCollectionMethodsWithUnrelatedTypesRule.ruleId:
8687
AvoidCollectionMethodsWithUnrelatedTypesRule.new,
88+
AvoidCreatingVectorInUpdateRule.ruleId: AvoidCreatingVectorInUpdateRule.new,
8789
AvoidDoubleSlashImportsRule.ruleId: AvoidDoubleSlashImportsRule.new,
8890
AvoidDuplicateExportsRule.ruleId: AvoidDuplicateExportsRule.new,
8991
AvoidDynamicRule.ruleId: AvoidDynamicRule.new,
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+
// ignore: implementation_imports
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 AvoidCreatingVectorInUpdateRule extends FlameRule {
20+
static const String ruleId = 'avoid-creating-vector-in-update';
21+
22+
static const _warningMessage = "Avoid creating Vector2 in 'update' method.";
23+
24+
AvoidCreatingVectorInUpdateRule([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.expressions
39+
.map((expression) => createIssue(
40+
rule: this,
41+
location: nodeLocation(node: expression, source: source),
42+
message: _warningMessage,
43+
))
44+
.toList(growable: false);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
part of 'avoid_creating_vector_in_update_rule.dart';
2+
3+
class _Visitor extends SimpleAstVisitor<void> {
4+
final _expressions = <Expression>[];
5+
6+
Iterable<Expression> get expressions => _expressions;
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 updateMethod = node.members.firstWhereOrNull((member) =>
18+
member is MethodDeclaration &&
19+
member.name.lexeme == 'update' &&
20+
isOverride(member.metadata));
21+
22+
if (updateMethod is MethodDeclaration) {
23+
final visitor = _VectorVisitor();
24+
updateMethod.visitChildren(visitor);
25+
26+
_expressions.addAll(visitor.wrongExpressions);
27+
}
28+
}
29+
}
30+
31+
class _VectorVisitor extends RecursiveAstVisitor<void> {
32+
final wrongExpressions = <Expression>{};
33+
34+
@override
35+
void visitInstanceCreationExpression(InstanceCreationExpression node) {
36+
super.visitInstanceCreationExpression(node);
37+
38+
if (isVector(node.staticType)) {
39+
wrongExpressions.add(node);
40+
}
41+
}
42+
43+
@override
44+
void visitBinaryExpression(BinaryExpression node) {
45+
super.visitBinaryExpression(node);
46+
47+
if (isVector(node.leftOperand.staticType) &&
48+
isVector(node.rightOperand.staticType)) {
49+
wrongExpressions.add(node);
50+
}
51+
}
52+
}

lib/src/utils/flame_type_utils.dart

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import 'package:analyzer/dart/element/type.dart';
33
bool isComponentOrSubclass(DartType? type) =>
44
_isComponent(type) || _isSubclassOfComponent(type);
55

6+
bool isVector(DartType? type) =>
7+
type?.getDisplayString(withNullability: false) == 'Vector2';
8+
69
bool _isComponent(DartType? type) =>
710
type?.getDisplayString(withNullability: false) == 'Component';
811

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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_creating_vector_in_update/avoid_creating_vector_in_update_rule.dart';
3+
import 'package:test/test.dart';
4+
5+
import '../../../../../helpers/rule_test_helper.dart';
6+
7+
const _examplePath = 'avoid_creating_vector_in_update/examples/example.dart';
8+
9+
void main() {
10+
group('AvoidCreatingVectorInUpdateRule', () {
11+
test('initialization', () async {
12+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
13+
final issues = AvoidCreatingVectorInUpdateRule().check(unit);
14+
15+
RuleTestHelper.verifyInitialization(
16+
issues: issues,
17+
ruleId: 'avoid-creating-vector-in-update',
18+
severity: Severity.warning,
19+
);
20+
});
21+
22+
test('reports about found issues', () async {
23+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
24+
final issues = AvoidCreatingVectorInUpdateRule().check(unit);
25+
26+
RuleTestHelper.verifyIssues(
27+
issues: issues,
28+
startLines: [4, 23, 24],
29+
startColumns: [23, 23, 23],
30+
locationTexts: [
31+
'Vector2(10, 10)',
32+
'vector1 + vector2',
33+
'vector1 - vector2',
34+
],
35+
messages: [
36+
"Avoid creating Vector2 in 'update' method.",
37+
"Avoid creating Vector2 in 'update' method.",
38+
"Avoid creating Vector2 in 'update' method.",
39+
],
40+
);
41+
});
42+
});
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
class MyComponent extends Component {
2+
@override
3+
void update(double dt) {
4+
final newVector = Vector2(10, 10); // LINT
5+
}
6+
}
7+
8+
class MyComponent extends Component {
9+
final _temporaryVector = Vector2.zero();
10+
11+
@override
12+
void update(double dt) {
13+
_temporaryVector.setValues(10, 10);
14+
}
15+
}
16+
17+
class MyComponent extends Component {
18+
final vector1 = Vector2(10, 10);
19+
final vector2 = Vector2(20, 20);
20+
21+
@override
22+
void update(double dt) {
23+
final addVector = vector1 + vector2; // LINT
24+
final subVector = vector1 - vector2; // LINT
25+
}
26+
}
27+
28+
class Component {
29+
void onMount() {}
30+
}
31+
32+
class Vector2 {
33+
final double x;
34+
final double y;
35+
36+
const Vector2(this.x, this.y);
37+
38+
const Vector2.zero()
39+
: x = 0,
40+
y = 0;
41+
42+
void setValues(double x, double y) {
43+
this.x = x;
44+
this.y = y;
45+
}
46+
47+
@override
48+
Vector2 operator /(double scale) => this;
49+
50+
@override
51+
Vector2 operator +(double scale) => this;
52+
53+
@override
54+
Vector2 operator -(double scale) => this;
55+
56+
@override
57+
Vector2 operator *(double scale) => this;
58+
}

0 commit comments

Comments
 (0)