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

Commit d49d7a4

Browse files
authored
Merge branch 'master' into avoid-redundant-async-normal-completion
2 parents 430eb8f + 7f9e642 commit d49d7a4

File tree

19 files changed

+222
-7
lines changed

19 files changed

+222
-7
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
## Unreleased
44

55
* fix: partially handle normal completion function body for [`avoid-redundant-async`](https://dartcodemetrics.dev/docs/rules/common/avoid-redundant-async).
6+
* fix: ignore enum constant arguments for [`no-magic-number`](https://dartcodemetrics.dev/docs/rules/common/no-magic-number).
7+
* fix: correctly handle prefixed enums and static instance fields for [`prefer-moving-to-variable`](https://dartcodemetrics.dev/docs/rules/common/prefer-moving-to-variable).
68
* feat: add static code diagnostic [`prefer-provide-intl-description`](https://dartcodemetrics.dev/docs/rules/intl/prefer-provide-intl-description).
79
* feat: exclude `.freezed.dart` files by default.
810
* fix: handle try and switch statements for [`use-setstate-synchronously`](https://dartcodemetrics.dev/docs/rules/flutter/use-setstate-synchronously)
911
* chore: restrict `analyzer` version to `>=5.1.0 <5.4.0`.
12+
* fix: ignore method invocations in a variable declaration for [`prefer-moving-to-variable`](https://dartcodemetrics.dev/docs/rules/common/prefer-moving-to-variable).
13+
* feat: add `allow-initialized` option to [`avoid-late-keyword`](https://dartcodemetrics.dev/docs/rules/common/avoid-late-keyword).
14+
* feat: add `ignored-types` option to [`avoid-late-keyword`](https://dartcodemetrics.dev/docs/rules/common/avoid-late-keyword).
1015

1116
## 5.4.0
1217

lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_late_keyword/avoid_late_keyword_rule.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,39 @@ import '../../../models/severity.dart';
1111
import '../../models/common_rule.dart';
1212
import '../../rule_utils.dart';
1313

14+
part 'config_parser.dart';
1415
part 'visitor.dart';
1516

1617
class AvoidLateKeywordRule extends CommonRule {
1718
static const String ruleId = 'avoid-late-keyword';
1819

1920
static const _warning = "Avoid using 'late' keyword.";
2021

22+
final bool _allowInitialized;
23+
final Iterable<String> _ignoredTypes;
24+
2125
AvoidLateKeywordRule([Map<String, Object> config = const {}])
22-
: super(
26+
: _allowInitialized = _ConfigParser.parseAllowInitialized(config),
27+
_ignoredTypes = _ConfigParser.parseIgnoredTypes(config),
28+
super(
2329
id: ruleId,
2430
severity: readSeverity(config, Severity.warning),
2531
excludes: readExcludes(config),
2632
includes: readIncludes(config),
2733
);
2834

35+
@override
36+
Map<String, Object?> toJson() {
37+
final json = super.toJson();
38+
json[_ConfigParser._allowInitializedConfig] = _allowInitialized;
39+
json[_ConfigParser._ignoredTypesConfig] = _ignoredTypes.toList();
40+
41+
return json;
42+
}
43+
2944
@override
3045
Iterable<Issue> check(InternalResolvedUnitResult source) {
31-
final visitor = _Visitor();
46+
final visitor = _Visitor(_allowInitialized, _ignoredTypes);
3247

3348
source.unit.visitChildren(visitor);
3449

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
part of 'avoid_late_keyword_rule.dart';
2+
3+
class _ConfigParser {
4+
static const _allowInitializedConfig = 'allow-initialized';
5+
static const _ignoredTypesConfig = 'ignored-types';
6+
7+
static bool parseAllowInitialized(Map<String, Object> config) =>
8+
config[_allowInitializedConfig] as bool? ?? false;
9+
10+
static Iterable<String> parseIgnoredTypes(Map<String, Object> config) =>
11+
config.containsKey(_ignoredTypesConfig) &&
12+
config[_ignoredTypesConfig] is Iterable
13+
? List<String>.from(config[_ignoredTypesConfig] as Iterable)
14+
: <String>['AnimationController'];
15+
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
part of 'avoid_late_keyword_rule.dart';
22

33
class _Visitor extends RecursiveAstVisitor<void> {
4+
final bool allowInitialized;
5+
final Iterable<String> ignoredTypes;
6+
47
final _declarations = <AstNode>[];
58

69
Iterable<AstNode> get declarations => _declarations;
710

11+
// ignore: avoid_positional_boolean_parameters
12+
_Visitor(this.allowInitialized, this.ignoredTypes);
13+
814
@override
915
void visitVariableDeclaration(VariableDeclaration node) {
1016
super.visitVariableDeclaration(node);
1117

1218
if (node.isLate && node.parent != null) {
1319
final parent = node.parent;
1420

15-
_declarations.add(parent ?? node);
21+
if (!_allowsInitialized(node) && !_hasIgnoredType(node)) {
22+
_declarations.add(parent ?? node);
23+
}
1624
}
1725
}
26+
27+
bool _allowsInitialized(VariableDeclaration node) =>
28+
allowInitialized && node.initializer != null;
29+
30+
bool _hasIgnoredType(VariableDeclaration node) => ignoredTypes.contains(
31+
// ignore: deprecated_member_use
32+
node.declaredElement2?.type.getDisplayString(withNullability: false),
33+
);
1834
}

lib/src/analyzers/lint_analyzer/rules/rules_list/no_magic_number/no_magic_number_rule.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class NoMagicNumberRule extends CommonRule {
5353
.where(_isNotInsideConstConstructor)
5454
.where(_isNotInDateTime)
5555
.where(_isNotInsideIndexExpression)
56+
.where(_isNotInsideEnumConstantArguments)
5657
.map((lit) => createIssue(
5758
rule: this,
5859
location: nodeLocation(node: lit, source: source),
@@ -80,6 +81,14 @@ class NoMagicNumberRule extends CommonRule {
8081
) ==
8182
null;
8283

84+
bool _isNotInsideEnumConstantArguments(Literal l) {
85+
final node = l.thisOrAncestorMatching(
86+
(ancestor) => ancestor is EnumConstantArguments,
87+
);
88+
89+
return node == null;
90+
}
91+
8392
bool _isNotInsideCollectionLiteral(Literal l) => l.parent is! TypedLiteral;
8493

8594
bool _isNotInsideConstMap(Literal l) {

lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_moving_to_variable/prefer_moving_to_variable_rule.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import 'package:analyzer/dart/ast/ast.dart';
44
import 'package:analyzer/dart/ast/visitor.dart';
5+
import 'package:analyzer/dart/element/element.dart';
56

67
import '../../../../../utils/node_utils.dart';
78
import '../../../lint_utils.dart';

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ class _BlockVisitor extends RecursiveAstVisitor<void> {
4040

4141
@override
4242
void visitPropertyAccess(PropertyAccess node) {
43-
if (node.target == null) {
43+
final target = node.target;
44+
if (target == null) {
4445
return;
4546
}
4647

48+
if (target is PrefixedIdentifier) {
49+
final element = target.identifier.staticElement;
50+
if (element is EnumElement || element is ClassElement) {
51+
return;
52+
}
53+
}
54+
4755
final hasDuplicates = _checkForDuplicates(node, node.target);
4856
if (!hasDuplicates) {
4957
super.visitPropertyAccess(node);
@@ -53,6 +61,7 @@ class _BlockVisitor extends RecursiveAstVisitor<void> {
5361
@override
5462
void visitMethodInvocation(MethodInvocation node) {
5563
if (node.parent is CascadeExpression ||
64+
node.parent is VariableDeclaration ||
5665
(node.staticType?.isVoid ?? false)) {
5766
return;
5867
}

test/src/analyzers/lint_analyzer/rules/rules_list/avoid_late_keyword/avoid_late_keyword_rule_test.dart

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void main() {
2929
startColumns: [3, 3, 5, 5, 1, 1],
3030
locationTexts: [
3131
"late final field = 'string'",
32-
'late String uninitializedField',
32+
'late int uninitializedField',
3333
"late final variable = 'string'",
3434
'late String uninitializedVariable',
3535
"late final topLevelVariable = 'string'",
@@ -45,5 +45,46 @@ void main() {
4545
],
4646
);
4747
});
48+
49+
test('reports about found issues with allow initialized config', () async {
50+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
51+
final issues =
52+
AvoidLateKeywordRule({'allow-initialized': true}).check(unit);
53+
54+
RuleTestHelper.verifyIssues(
55+
issues: issues,
56+
startLines: [8, 17, 23],
57+
startColumns: [3, 5, 1],
58+
locationTexts: [
59+
'late int uninitializedField',
60+
'late String uninitializedVariable',
61+
'late String topLevelUninitializedVariable',
62+
],
63+
messages: [
64+
"Avoid using 'late' keyword.",
65+
"Avoid using 'late' keyword.",
66+
"Avoid using 'late' keyword.",
67+
],
68+
);
69+
});
70+
71+
test('reports about found issues with ignored types config', () async {
72+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
73+
final issues = AvoidLateKeywordRule({
74+
'ignored-types': ['String'],
75+
}).check(unit);
76+
77+
RuleTestHelper.verifyIssues(
78+
issues: issues,
79+
startLines: [8],
80+
startColumns: [3],
81+
locationTexts: [
82+
'late int uninitializedField',
83+
],
84+
messages: [
85+
"Avoid using 'late' keyword.",
86+
],
87+
);
88+
});
4889
});
4990
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class Test {
55

66
String? nullableField;
77

8-
late String uninitializedField; // LINT
8+
late int uninitializedField; // LINT
99

1010
void method() {
1111
late final variable = 'string'; // LINT
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
enum ExampleMagicNumbers {
2+
second(2),
3+
third(3);
4+
5+
final int value;
6+
7+
const ExampleMagicNumbers(this.value);
8+
}
9+
10+
enum ExampleNamedMagicNumbers {
11+
second(value: 2),
12+
third(value: 3);
13+
14+
final int value;
15+
16+
const ExampleNamedMagicNumbers({required this.value});
17+
}

test/src/analyzers/lint_analyzer/rules/rules_list/no_magic_number/no_magic_number_rule_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const _incorrectExamplePath = 'no_magic_number/examples/incorrect_example.dart';
99
const _exceptionsExamplePath =
1010
'no_magic_number/examples/exceptions_example.dart';
1111
const _arrayExamplePath = 'no_magic_number/examples/array_example.dart';
12+
const _enumExamplePath = 'no_magic_number/examples/enum_example.dart';
1213

1314
void main() {
1415
group('NoMagicNumberRule', () {
@@ -42,6 +43,13 @@ void main() {
4243
RuleTestHelper.verifyNoIssues(issues);
4344
});
4445

46+
test("doesn't report enum arguments", () async {
47+
final unit = await RuleTestHelper.resolveFromFile(_enumExamplePath);
48+
final issues = NoMagicNumberRule().check(unit);
49+
50+
RuleTestHelper.verifyNoIssues(issues);
51+
});
52+
4553
test("doesn't report exceptional code", () async {
4654
final unit = await RuleTestHelper.resolveFromFile(_exceptionsExamplePath);
4755
final issues = NoMagicNumberRule().check(unit);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
void main() {
2+
final var1 = someFunction();
3+
final var2 = someFunction();
4+
}
5+
6+
String someFunction() {
7+
return 'qwe';
8+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,18 @@ class Theme {
5656
}
5757

5858
String getValue() => 'hello';
59+
60+
enum SomeValue {
61+
firstValue,
62+
secondValue,
63+
entry1,
64+
entry2,
65+
}
66+
67+
class SomeClass {
68+
static final value = '10';
69+
70+
final field = 11;
71+
}
72+
73+
final instance = SomeClass();

test/src/analyzers/lint_analyzer/rules/rules_list/prefer_moving_to_variable/examples/generics_example.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ class GetIt {
1212

1313
T call<T extends Object>() => get<T>;
1414
}
15+
16+
enum AnotherEnum {
17+
firstValue,
18+
anotherValue,
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import 'example.dart' as prefix;
2+
import 'generics_example.dart';
3+
4+
void main() {
5+
AnotherEnum.anotherValue;
6+
AnotherEnum.anotherValue;
7+
AnotherEnum.firstValue;
8+
9+
prefix.SomeValue.firstValue;
10+
prefix.SomeValue.firstValue;
11+
prefix.SomeValue.secondValue;
12+
13+
prefix.SomeClass.value;
14+
prefix.SomeClass.value;
15+
prefix.instance.field;
16+
17+
print(prefix.SomeValue.entry1);
18+
print(prefix.SomeValue.entry2);
19+
}

test/src/analyzers/lint_analyzer/rules/rules_list/prefer_moving_to_variable/prefer_moving_to_variable_rule_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const _cascadeExamplePath =
1515
'prefer_moving_to_variable/examples/cascade_example.dart';
1616
const _genericsExamplePath =
1717
'prefer_moving_to_variable/examples/generics_example.dart';
18+
const _assignmentExamplePath =
19+
'prefer_moving_to_variable/examples/assignment_example.dart';
20+
const _prefixExamplePath =
21+
'prefer_moving_to_variable/examples/prefix_example.dart';
1822

1923
void main() {
2024
group('PreferMovingToVariableRule', () {
@@ -215,13 +219,27 @@ void main() {
215219
RuleTestHelper.verifyNoIssues(issues);
216220
});
217221

222+
test('reports no issues for assignments', () async {
223+
final unit = await RuleTestHelper.resolveFromFile(_assignmentExamplePath);
224+
final issues = PreferMovingToVariableRule().check(unit);
225+
226+
RuleTestHelper.verifyNoIssues(issues);
227+
});
228+
218229
test('reports no issues for generics', () async {
219230
final unit = await RuleTestHelper.resolveFromFile(_genericsExamplePath);
220231
final issues = PreferMovingToVariableRule().check(unit);
221232

222233
RuleTestHelper.verifyNoIssues(issues);
223234
});
224235

236+
test('reports no issues for prefix imports', () async {
237+
final unit = await RuleTestHelper.resolveFromFile(_prefixExamplePath);
238+
final issues = PreferMovingToVariableRule().check(unit);
239+
240+
RuleTestHelper.verifyNoIssues(issues);
241+
});
242+
225243
test('reports issues with custom config', () async {
226244
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
227245
final config = {

website/docs/rules/common/avoid-late-keyword.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ Use this rule if you want to avoid unexpected runtime exceptions.
1212

1313
:::
1414

15+
Use `allow-initialized` configuration (default is `false`), if you want to allow initialized late variable declarations.
16+
17+
### ⚙️ Config example {#config-example}
18+
19+
```yaml
20+
dart_code_metrics:
21+
...
22+
rules:
23+
...
24+
- avoid-late-keyword:
25+
allow-initialized: true
26+
```
27+
1528
### Example {#example}
1629
1730
**❌ Bad:**

website/docs/rules/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
112112
type="common"
113113
severity="warning"
114114
version="3.2.0"
115+
hasConfig
115116
>
116117
Warns when a field or variable is declared with a <code>late</code> keyword.
117118
</RuleEntry>

0 commit comments

Comments
 (0)