This repository was archived by the owner on Jul 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 271
feat: add prefer-define-hero-tag rule #1184
Merged
incendial
merged 5 commits into
dart-code-checker:master
from
san-smith:prefer-define-hero-tag
Mar 17, 2023
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
be6a8f5
feat: add prefer-define-hero-tag rule
san-smith 8195904
fix: review comments
san-smith cfc8467
Merge branch 'master' of github.com:san-smith/dart-code-metrics into …
san-smith d51593f
Merge branch 'master' of github.com:san-smith/dart-code-metrics into …
san-smith 810779f
Merge branch 'master' into prefer-define-hero-tag
incendial File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
...rs/lint_analyzer/rules/rules_list/prefer_define_hero_tag/prefer_define_hero_tag_rule.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// ignore_for_file: public_member_api_docs | ||
|
||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
|
||
import '../../../../../utils/node_utils.dart'; | ||
import '../../../lint_utils.dart'; | ||
import '../../../models/internal_resolved_unit_result.dart'; | ||
import '../../../models/issue.dart'; | ||
import '../../../models/severity.dart'; | ||
import '../../models/flutter_rule.dart'; | ||
import '../../rule_utils.dart'; | ||
|
||
part 'visitor.dart'; | ||
|
||
class PreferDefineHeroTagRule extends FlutterRule { | ||
static const ruleId = 'prefer-define-hero-tag'; | ||
static const _issueMessage = 'Prefer define heroTag property.'; | ||
|
||
PreferDefineHeroTagRule([Map<String, Object> config = const {}]) | ||
: super( | ||
id: ruleId, | ||
severity: readSeverity(config, Severity.warning), | ||
excludes: readExcludes(config), | ||
includes: readIncludes(config), | ||
); | ||
|
||
@override | ||
Iterable<Issue> check(InternalResolvedUnitResult source) { | ||
final visitor = _Visitor(); | ||
source.unit.visitChildren(visitor); | ||
|
||
return visitor.invocations | ||
.map((invocation) => createIssue( | ||
rule: this, | ||
location: nodeLocation(node: invocation, source: source), | ||
message: _issueMessage, | ||
)) | ||
.toList(growable: false); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_define_hero_tag/visitor.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
part of 'prefer_define_hero_tag_rule.dart'; | ||
|
||
const _cupertinoNavigationBarClassName = 'CupertinoNavigationBar'; | ||
const _cupertinoSliverNavigationBarClassName = 'CupertinoSliverNavigationBar'; | ||
const _floatingActionButtonClassName = 'FloatingActionButton'; | ||
const _constructorExtendedName = 'extended'; | ||
const _constructorLargeName = 'large'; | ||
const _constructorSmallName = 'small'; | ||
const _heroTagPropertyName = 'heroTag'; | ||
|
||
class _Visitor extends RecursiveAstVisitor<void> { | ||
final _invocations = <MethodInvocation>[]; | ||
|
||
Iterable<MethodInvocation> get invocations => _invocations; | ||
|
||
@override | ||
void visitMethodInvocation(MethodInvocation node) { | ||
super.visitMethodInvocation(node); | ||
|
||
final methodName = node.methodName.name; | ||
if (methodName == _cupertinoNavigationBarClassName || | ||
methodName == _cupertinoSliverNavigationBarClassName || | ||
methodName == _floatingActionButtonClassName || | ||
node.beginToken.lexeme == _floatingActionButtonClassName && | ||
(methodName == _constructorExtendedName || | ||
methodName == _constructorLargeName || | ||
methodName == _constructorSmallName)) { | ||
if (!node.argumentList.arguments.any((arg) => | ||
arg is NamedExpression && | ||
arg.name.label.name == _heroTagPropertyName)) { | ||
_invocations.add(node); | ||
} | ||
} | ||
} | ||
} |
200 changes: 200 additions & 0 deletions
200
...src/analyzers/lint_analyzer/rules/rules_list/prefer_define_hero_tag/examples/example.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
class MyWidget extends StatelessWidget { | ||
const MyWidget({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton( | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget2 extends StatelessWidget { | ||
const MyWidget2({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton.extended( | ||
label: const Text('label'), | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget3 extends StatelessWidget { | ||
const MyWidget3({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton.large( | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget4 extends StatelessWidget { | ||
const MyWidget4({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton.small( | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget6 extends StatelessWidget { | ||
const MyWidget6({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton( | ||
heroTag: 'heroTag', | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget7 extends StatelessWidget { | ||
const MyWidget7({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton.extended( | ||
heroTag: 'heroTag', | ||
label: const Text('label'), | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget8 extends StatelessWidget { | ||
const MyWidget8({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
floatingActionButton: FloatingActionButton.large( | ||
heroTag: 'heroTag', | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class MyWidget9 extends StatelessWidget { | ||
const MyWidget9({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => Scaffold( | ||
incendial marked this conversation as resolved.
Show resolved
Hide resolved
|
||
floatingActionButton: FloatingActionButton.small( | ||
heroTag: 'heroTag', | ||
onPressed: () {}, | ||
), | ||
); | ||
} | ||
|
||
class SliverNavBarExample extends StatelessWidget { | ||
const SliverNavBarExample({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => const CupertinoPageScaffold( | ||
child: CustomScrollView( | ||
slivers: <Widget>[ | ||
CupertinoSliverNavigationBar( | ||
largeTitle: Text('Contacts'), | ||
), | ||
], | ||
), | ||
); | ||
} | ||
|
||
class SliverNavBarExample2 extends StatelessWidget { | ||
const SliverNavBarExample2({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => const CupertinoPageScaffold( | ||
child: CustomScrollView( | ||
slivers: <Widget>[ | ||
CupertinoSliverNavigationBar( | ||
largeTitle: Text('Contacts'), | ||
heroTag: 'heroTag', | ||
), | ||
], | ||
), | ||
); | ||
} | ||
|
||
class NavBarExample extends StatelessWidget { | ||
const NavBarExample({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => const CupertinoPageScaffold( | ||
navigationBar: CupertinoNavigationBar( | ||
middle: Text('CupertinoNavigationBar Sample'), | ||
), | ||
child: Text('data'), | ||
); | ||
} | ||
|
||
class NavBarExample2 extends StatelessWidget { | ||
const NavBarExample2({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => const CupertinoPageScaffold( | ||
navigationBar: CupertinoNavigationBar( | ||
heroTag: 'heroTag', | ||
middle: Text('CupertinoNavigationBar Sample'), | ||
), | ||
child: Text('data'), | ||
); | ||
} | ||
|
||
class BuildContext {} | ||
|
||
class Key {} | ||
|
||
class Widget { | ||
final Key? key; | ||
|
||
const Widget(this.key); | ||
} | ||
|
||
class StatelessWidget extends Widget { | ||
const StatelessWidget(super.key); | ||
} | ||
|
||
class Scaffold extends Widget { | ||
final Widget? floatingActionButton; | ||
|
||
Scaffold({ | ||
this.floatingActionButton, | ||
}); | ||
} | ||
|
||
class CupertinoPageScaffold extends Widget { | ||
final Widget child; | ||
final Widget? navigationBar; | ||
|
||
const CupertinoPageScaffold({ | ||
super.key, | ||
required this.child, | ||
this.navigationBar, | ||
}); | ||
} | ||
|
||
class CustomScrollView extends Widget { | ||
final List<Widget> slivers; | ||
|
||
const CustomScrollView({ | ||
super.key, | ||
required this.slivers, | ||
}); | ||
} | ||
|
||
class Text extends Widget { | ||
final String data; | ||
|
||
const Text( | ||
this.data, { | ||
super.key, | ||
}); | ||
} |
71 changes: 71 additions & 0 deletions
71
...nt_analyzer/rules/rules_list/prefer_define_hero_tag/prefer_define_hero_tag_rule_test.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/models/severity.dart'; | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/rules/rules_list/prefer_define_hero_tag/prefer_define_hero_tag_rule.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../../../../../helpers/rule_test_helper.dart'; | ||
|
||
const _examplePath = 'prefer_define_hero_tag/examples/example.dart'; | ||
|
||
void main() { | ||
group( | ||
'PreferDefineHeroTagRule', | ||
() { | ||
test('initialization', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_examplePath); | ||
final issues = PreferDefineHeroTagRule().check(unit); | ||
|
||
RuleTestHelper.verifyInitialization( | ||
issues: issues, | ||
ruleId: 'prefer-define-hero-tag', | ||
severity: Severity.warning, | ||
); | ||
}); | ||
|
||
test('reports about found issues', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_examplePath); | ||
final issues = PreferDefineHeroTagRule().check(unit); | ||
|
||
RuleTestHelper.verifyIssues( | ||
issues: issues, | ||
startLines: [6, 17, 29, 40, 102, 131], | ||
startColumns: [31, 31, 31, 31, 13, 24], | ||
messages: [ | ||
'Prefer define heroTag property.', | ||
'Prefer define heroTag property.', | ||
'Prefer define heroTag property.', | ||
'Prefer define heroTag property.', | ||
'Prefer define heroTag property.', | ||
'Prefer define heroTag property.', | ||
], | ||
locationTexts: [ | ||
''' | ||
FloatingActionButton( | ||
onPressed: () {}, | ||
)''', | ||
''' | ||
FloatingActionButton.extended( | ||
label: const Text('label'), | ||
onPressed: () {}, | ||
)''', | ||
''' | ||
FloatingActionButton.large( | ||
onPressed: () {}, | ||
)''', | ||
''' | ||
FloatingActionButton.small( | ||
onPressed: () {}, | ||
)''', | ||
''' | ||
CupertinoSliverNavigationBar( | ||
largeTitle: Text('Contacts'), | ||
)''', | ||
''' | ||
CupertinoNavigationBar( | ||
middle: Text('CupertinoNavigationBar Sample'), | ||
)''', | ||
], | ||
); | ||
}); | ||
}, | ||
); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.