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

Commit 834cafb

Browse files
authored
Merge d51593f into 1f71eba
2 parents 1f71eba + d51593f commit 834cafb

File tree

6 files changed

+350
-0
lines changed

6 files changed

+350
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
* feat: add static code diagnostic [prefer-define-hero-tag](https://github.com/dart-code-checker/dart-code-metrics/issues/1027).
56
* chore: restrict `analyzer` version to `>=5.1.0 <5.6.0`.
67

78
## 5.6.0

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

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import 'rules_list/prefer_correct_edge_insets_constructor/prefer_correct_edge_in
5959
import 'rules_list/prefer_correct_identifier_length/prefer_correct_identifier_length_rule.dart';
6060
import 'rules_list/prefer_correct_test_file_name/prefer_correct_test_file_name_rule.dart';
6161
import 'rules_list/prefer_correct_type_name/prefer_correct_type_name_rule.dart';
62+
import 'rules_list/prefer_define_hero_tag/prefer_define_hero_tag_rule.dart';
6263
import 'rules_list/prefer_enums_by_name/prefer_enums_by_name_rule.dart';
6364
import 'rules_list/prefer_extracting_callbacks/prefer_extracting_callbacks_rule.dart';
6465
import 'rules_list/prefer_first/prefer_first_rule.dart';
@@ -149,6 +150,7 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
149150
PreferCorrectIdentifierLengthRule.new,
150151
PreferCorrectTestFileNameRule.ruleId: PreferCorrectTestFileNameRule.new,
151152
PreferCorrectTypeNameRule.ruleId: PreferCorrectTypeNameRule.new,
153+
PreferDefineHeroTagRule.ruleId: PreferDefineHeroTagRule.new,
152154
PreferEnumsByNameRule.ruleId: PreferEnumsByNameRule.new,
153155
PreferExtractingCallbacksRule.ruleId: PreferExtractingCallbacksRule.new,
154156
PreferFirstRule.ruleId: PreferFirstRule.new,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
6+
import '../../../../../utils/node_utils.dart';
7+
import '../../../lint_utils.dart';
8+
import '../../../models/internal_resolved_unit_result.dart';
9+
import '../../../models/issue.dart';
10+
import '../../../models/severity.dart';
11+
import '../../models/flutter_rule.dart';
12+
import '../../rule_utils.dart';
13+
14+
part 'visitor.dart';
15+
16+
class PreferDefineHeroTagRule extends FlutterRule {
17+
static const ruleId = 'prefer-define-hero-tag';
18+
static const _issueMessage = 'Prefer define heroTag property.';
19+
20+
PreferDefineHeroTagRule([Map<String, Object> config = const {}])
21+
: super(
22+
id: ruleId,
23+
severity: readSeverity(config, Severity.warning),
24+
excludes: readExcludes(config),
25+
includes: readIncludes(config),
26+
);
27+
28+
@override
29+
Iterable<Issue> check(InternalResolvedUnitResult source) {
30+
final visitor = _Visitor();
31+
source.unit.visitChildren(visitor);
32+
33+
return visitor.invocations
34+
.map((invocation) => createIssue(
35+
rule: this,
36+
location: nodeLocation(node: invocation, source: source),
37+
message: _issueMessage,
38+
))
39+
.toList(growable: false);
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
part of 'prefer_define_hero_tag_rule.dart';
2+
3+
const _cupertinoNavigationBarClassName = 'CupertinoNavigationBar';
4+
const _cupertinoSliverNavigationBarClassName = 'CupertinoSliverNavigationBar';
5+
const _floatingActionButtonClassName = 'FloatingActionButton';
6+
const _constructorExtendedName = 'extended';
7+
const _constructorLargeName = 'large';
8+
const _constructorSmallName = 'small';
9+
const _heroTagPropertyName = 'heroTag';
10+
11+
class _Visitor extends RecursiveAstVisitor<void> {
12+
final _invocations = <MethodInvocation>[];
13+
14+
Iterable<MethodInvocation> get invocations => _invocations;
15+
16+
@override
17+
void visitMethodInvocation(MethodInvocation node) {
18+
super.visitMethodInvocation(node);
19+
20+
final methodName = node.methodName.name;
21+
if (methodName == _cupertinoNavigationBarClassName ||
22+
methodName == _cupertinoSliverNavigationBarClassName ||
23+
methodName == _floatingActionButtonClassName ||
24+
node.beginToken.lexeme == _floatingActionButtonClassName &&
25+
(methodName == _constructorExtendedName ||
26+
methodName == _constructorLargeName ||
27+
methodName == _constructorSmallName)) {
28+
if (!node.argumentList.arguments.any((arg) =>
29+
arg is NamedExpression &&
30+
arg.name.label.name == _heroTagPropertyName)) {
31+
_invocations.add(node);
32+
}
33+
}
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
class MyWidget extends StatelessWidget {
2+
const MyWidget({super.key});
3+
4+
@override
5+
Widget build(BuildContext context) => Scaffold(
6+
floatingActionButton: FloatingActionButton(
7+
onPressed: () {},
8+
),
9+
);
10+
}
11+
12+
class MyWidget2 extends StatelessWidget {
13+
const MyWidget2({super.key});
14+
15+
@override
16+
Widget build(BuildContext context) => Scaffold(
17+
floatingActionButton: FloatingActionButton.extended(
18+
label: const Text('label'),
19+
onPressed: () {},
20+
),
21+
);
22+
}
23+
24+
class MyWidget3 extends StatelessWidget {
25+
const MyWidget3({super.key});
26+
27+
@override
28+
Widget build(BuildContext context) => Scaffold(
29+
floatingActionButton: FloatingActionButton.large(
30+
onPressed: () {},
31+
),
32+
);
33+
}
34+
35+
class MyWidget4 extends StatelessWidget {
36+
const MyWidget4({super.key});
37+
38+
@override
39+
Widget build(BuildContext context) => Scaffold(
40+
floatingActionButton: FloatingActionButton.small(
41+
onPressed: () {},
42+
),
43+
);
44+
}
45+
46+
class MyWidget6 extends StatelessWidget {
47+
const MyWidget6({super.key});
48+
49+
@override
50+
Widget build(BuildContext context) => Scaffold(
51+
floatingActionButton: FloatingActionButton(
52+
heroTag: 'heroTag',
53+
onPressed: () {},
54+
),
55+
);
56+
}
57+
58+
class MyWidget7 extends StatelessWidget {
59+
const MyWidget7({super.key});
60+
61+
@override
62+
Widget build(BuildContext context) => Scaffold(
63+
floatingActionButton: FloatingActionButton.extended(
64+
heroTag: 'heroTag',
65+
label: const Text('label'),
66+
onPressed: () {},
67+
),
68+
);
69+
}
70+
71+
class MyWidget8 extends StatelessWidget {
72+
const MyWidget8({super.key});
73+
74+
@override
75+
Widget build(BuildContext context) => Scaffold(
76+
floatingActionButton: FloatingActionButton.large(
77+
heroTag: 'heroTag',
78+
onPressed: () {},
79+
),
80+
);
81+
}
82+
83+
class MyWidget9 extends StatelessWidget {
84+
const MyWidget9({super.key});
85+
86+
@override
87+
Widget build(BuildContext context) => Scaffold(
88+
floatingActionButton: FloatingActionButton.small(
89+
heroTag: 'heroTag',
90+
onPressed: () {},
91+
),
92+
);
93+
}
94+
95+
class SliverNavBarExample extends StatelessWidget {
96+
const SliverNavBarExample({super.key});
97+
98+
@override
99+
Widget build(BuildContext context) => const CupertinoPageScaffold(
100+
child: CustomScrollView(
101+
slivers: <Widget>[
102+
CupertinoSliverNavigationBar(
103+
largeTitle: Text('Contacts'),
104+
),
105+
],
106+
),
107+
);
108+
}
109+
110+
class SliverNavBarExample2 extends StatelessWidget {
111+
const SliverNavBarExample2({super.key});
112+
113+
@override
114+
Widget build(BuildContext context) => const CupertinoPageScaffold(
115+
child: CustomScrollView(
116+
slivers: <Widget>[
117+
CupertinoSliverNavigationBar(
118+
largeTitle: Text('Contacts'),
119+
heroTag: 'heroTag',
120+
),
121+
],
122+
),
123+
);
124+
}
125+
126+
class NavBarExample extends StatelessWidget {
127+
const NavBarExample({super.key});
128+
129+
@override
130+
Widget build(BuildContext context) => const CupertinoPageScaffold(
131+
navigationBar: CupertinoNavigationBar(
132+
middle: Text('CupertinoNavigationBar Sample'),
133+
),
134+
child: Text('data'),
135+
);
136+
}
137+
138+
class NavBarExample2 extends StatelessWidget {
139+
const NavBarExample2({super.key});
140+
141+
@override
142+
Widget build(BuildContext context) => const CupertinoPageScaffold(
143+
navigationBar: CupertinoNavigationBar(
144+
heroTag: 'heroTag',
145+
middle: Text('CupertinoNavigationBar Sample'),
146+
),
147+
child: Text('data'),
148+
);
149+
}
150+
151+
class BuildContext {}
152+
153+
class Key {}
154+
155+
class Widget {
156+
final Key? key;
157+
158+
const Widget(this.key);
159+
}
160+
161+
class StatelessWidget extends Widget {
162+
const StatelessWidget(super.key);
163+
}
164+
165+
class Scaffold extends Widget {
166+
final Widget? floatingActionButton;
167+
168+
Scaffold({
169+
this.floatingActionButton,
170+
});
171+
}
172+
173+
class CupertinoPageScaffold extends Widget {
174+
final Widget child;
175+
final Widget? navigationBar;
176+
177+
const CupertinoPageScaffold({
178+
super.key,
179+
required this.child,
180+
this.navigationBar,
181+
});
182+
}
183+
184+
class CustomScrollView extends Widget {
185+
final List<Widget> slivers;
186+
187+
const CustomScrollView({
188+
super.key,
189+
required this.slivers,
190+
});
191+
}
192+
193+
class Text extends Widget {
194+
final String data;
195+
196+
const Text(
197+
this.data, {
198+
super.key,
199+
});
200+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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/prefer_define_hero_tag/prefer_define_hero_tag_rule.dart';
3+
import 'package:test/test.dart';
4+
5+
import '../../../../../helpers/rule_test_helper.dart';
6+
7+
const _examplePath = 'prefer_define_hero_tag/examples/example.dart';
8+
9+
void main() {
10+
group(
11+
'PreferDefineHeroTagRule',
12+
() {
13+
test('initialization', () async {
14+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
15+
final issues = PreferDefineHeroTagRule().check(unit);
16+
17+
RuleTestHelper.verifyInitialization(
18+
issues: issues,
19+
ruleId: 'prefer-define-hero-tag',
20+
severity: Severity.warning,
21+
);
22+
});
23+
24+
test('reports about found issues', () async {
25+
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
26+
final issues = PreferDefineHeroTagRule().check(unit);
27+
28+
RuleTestHelper.verifyIssues(
29+
issues: issues,
30+
startLines: [6, 17, 29, 40, 102, 131],
31+
startColumns: [31, 31, 31, 31, 13, 24],
32+
messages: [
33+
'Prefer define heroTag property.',
34+
'Prefer define heroTag property.',
35+
'Prefer define heroTag property.',
36+
'Prefer define heroTag property.',
37+
'Prefer define heroTag property.',
38+
'Prefer define heroTag property.',
39+
],
40+
locationTexts: [
41+
'''
42+
FloatingActionButton(
43+
onPressed: () {},
44+
)''',
45+
'''
46+
FloatingActionButton.extended(
47+
label: const Text('label'),
48+
onPressed: () {},
49+
)''',
50+
'''
51+
FloatingActionButton.large(
52+
onPressed: () {},
53+
)''',
54+
'''
55+
FloatingActionButton.small(
56+
onPressed: () {},
57+
)''',
58+
'''
59+
CupertinoSliverNavigationBar(
60+
largeTitle: Text('Contacts'),
61+
)''',
62+
'''
63+
CupertinoNavigationBar(
64+
middle: Text('CupertinoNavigationBar Sample'),
65+
)''',
66+
],
67+
);
68+
});
69+
},
70+
);
71+
}

0 commit comments

Comments
 (0)