diff --git a/CHANGELOG.md b/CHANGELOG.md index 262717d1cc..5d6c174ead 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * feat: make CliRunner a part of public API in order to support transitive executable calls use-case. * feat: add static code diagnostic [`avoid-cascade-after-if-null`](https://dartcodemetrics.dev/docs/rules/common/avoid-cascade-after-if-null). * feat: **Breaking change** handle widget members order separately in [`member-ordering`](https://dartcodemetrics.dev/docs/rules/common/member-ordering). +* feat: support dynamic method names for [`member-ordering`](https://dartcodemetrics.dev/docs/rules/common/member-ordering). ## 4.21.2 diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/config_parser.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/config_parser.dart index e47aa39c33..ff80ec3624 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/config_parser.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/config_parser.dart @@ -29,9 +29,7 @@ class _ConfigParser { static final _regExp = RegExp( '(overridden-|protected-)?(private-|public-)?(static-)?(late-)?' - '(var-|final-|const-)?(nullable-)?(named-)?(factory-)?(build-)?' - '(init-state-)?(did-change-dependencies-)?(did-update-widget-)?' - '(dispose-)?', + '(var-|final-|const-)?(nullable-)?(named-)?(factory-)?', ); static List<_MemberGroup> parseOrder(Map config) { @@ -64,6 +62,17 @@ class _ConfigParser { final type = _MemberType.parse(lastGroup); final result = _regExp.allMatches(group.toLowerCase()); + final isNamedMethod = group.endsWith('-method'); + if (isNamedMethod) { + final name = group.split('-method').first.replaceAll('-', ''); + + return _MethodMemberGroup.named( + name: name, + memberType: _MemberType.method, + rawRepresentation: group, + ); + } + final hasGroups = result.isNotEmpty && result.first.groupCount > 0; if (hasGroups && type != null) { final match = result.first; @@ -76,11 +85,6 @@ class _ConfigParser { final isNullable = match.group(6) != null; final isNamed = match.group(7) != null; final isFactory = match.group(8) != null; - final isBuild = match.group(9) != null; - final isInitState = match.group(10) != null; - final isDidChangeDependencies = match.group(11) != null; - final isDidUpdateWidget = match.group(12) != null; - final isDispose = match.group(13) != null; switch (type) { case _MemberType.field: @@ -97,13 +101,9 @@ class _ConfigParser { case _MemberType.method: return _MethodMemberGroup._( + name: null, isNullable: isNullable, isStatic: isStatic, - isBuild: isBuild, - isInitState: isInitState, - isDidChangeDependencies: isDidChangeDependencies, - isDidUpdateWidget: isDidUpdateWidget, - isDispose: isDispose, annotation: annotation, memberType: type, modifier: modifier, diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/models/member_group.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/models/member_group.dart index fb2813f903..7cb469e27c 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/models/member_group.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/models/member_group.dart @@ -7,12 +7,12 @@ abstract class _MemberGroup { final String rawRepresentation; - const _MemberGroup( - this.annotation, - this.memberType, - this.modifier, - this.rawRepresentation, - ); + const _MemberGroup({ + required this.annotation, + required this.memberType, + required this.modifier, + required this.rawRepresentation, + }); int getSortingCoefficient(); } @@ -28,16 +28,11 @@ class _FieldMemberGroup extends _MemberGroup { required this.isNullable, required this.isStatic, required this.keyword, - required _Annotation annotation, - required _MemberType memberType, - required _Modifier modifier, - required String rawRepresentation, - }) : super( - annotation, - memberType, - modifier, - rawRepresentation, - ); + required super.annotation, + required super.memberType, + required super.modifier, + required super.rawRepresentation, + }); factory _FieldMemberGroup.parse(FieldDeclaration declaration) { final annotation = declaration.metadata @@ -89,30 +84,32 @@ class _FieldMemberGroup extends _MemberGroup { class _MethodMemberGroup extends _MemberGroup { final bool isStatic; final bool isNullable; - final bool isBuild; - final bool isInitState; - final bool isDidChangeDependencies; - final bool isDidUpdateWidget; - final bool isDispose; + final String? name; const _MethodMemberGroup._({ required this.isNullable, required this.isStatic, - required this.isBuild, - required this.isInitState, - required this.isDidChangeDependencies, - required this.isDidUpdateWidget, - required this.isDispose, - required _Annotation annotation, + required this.name, + required super.annotation, + required super.memberType, + required super.modifier, + required super.rawRepresentation, + }); + + factory _MethodMemberGroup.named({ + required String name, required _MemberType memberType, - required _Modifier modifier, required String rawRepresentation, - }) : super( - annotation, - memberType, - modifier, - rawRepresentation, - ); + }) => + _MethodMemberGroup._( + name: name, + isNullable: false, + isStatic: false, + modifier: _Modifier.unset, + annotation: _Annotation.unset, + memberType: memberType, + rawRepresentation: rawRepresentation, + ); factory _MethodMemberGroup.parse(MethodDeclaration declaration) { final methodName = declaration.name.lexeme; @@ -124,24 +121,11 @@ class _MethodMemberGroup extends _MemberGroup { ? _Modifier.private : _Modifier.public; - final hasOverrideAnnotation = annotation == _Annotation.override; - final isBuild = methodName == 'build' && hasOverrideAnnotation; - final isInitState = methodName == 'initState' && hasOverrideAnnotation; - final isDidChangeDependencies = - methodName == 'didChangeDependencies' && hasOverrideAnnotation; - final isDidUpdateWidget = - methodName == 'didUpdateWidget' && hasOverrideAnnotation; - final isDispose = methodName == 'dispose' && hasOverrideAnnotation; - return _MethodMemberGroup._( + name: methodName.toLowerCase(), annotation: annotation ?? _Annotation.unset, isStatic: declaration.isStatic, isNullable: declaration.returnType?.question != null, - isBuild: isBuild, - isInitState: isInitState, - isDidChangeDependencies: isDidChangeDependencies, - isDidUpdateWidget: isDidUpdateWidget, - isDispose: isDispose, memberType: _MemberType.method, modifier: modifier, rawRepresentation: '', @@ -156,11 +140,7 @@ class _MethodMemberGroup extends _MemberGroup { coefficient += isNullable ? 1 : 0; coefficient += annotation != _Annotation.unset ? 1 : 0; coefficient += modifier != _Modifier.unset ? 1 : 0; - coefficient += isBuild ? 10 : 0; - coefficient += isInitState ? 10 : 0; - coefficient += isDidChangeDependencies ? 10 : 0; - coefficient += isDidUpdateWidget ? 10 : 0; - coefficient += isDispose ? 10 : 0; + coefficient += name != null ? 10 : 0; return coefficient; } @@ -176,16 +156,11 @@ class _ConstructorMemberGroup extends _MemberGroup { const _ConstructorMemberGroup._({ required this.isNamed, required this.isFactory, - required _Annotation annotation, - required _MemberType memberType, - required _Modifier modifier, - required String rawRepresentation, - }) : super( - annotation, - memberType, - modifier, - rawRepresentation, - ); + required super.annotation, + required super.memberType, + required super.modifier, + required super.rawRepresentation, + }); factory _ConstructorMemberGroup.parse(ConstructorDeclaration declaration) { final annotation = declaration.metadata @@ -235,11 +210,11 @@ class _GetSetMemberGroup extends _MemberGroup { const _GetSetMemberGroup._({ required this.isNullable, required this.isStatic, - required _Annotation annotation, - required _MemberType memberType, - required _Modifier modifier, - required String rawRepresentation, - }) : super(annotation, memberType, modifier, rawRepresentation); + required super.annotation, + required super.memberType, + required super.modifier, + required super.rawRepresentation, + }); factory _GetSetMemberGroup.parse(MethodDeclaration declaration) { final annotation = declaration.metadata diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/visitor.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/visitor.dart index d08550818e..1ffe030726 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/visitor.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/visitor.dart @@ -124,7 +124,6 @@ class _Visitor extends RecursiveAstVisitor> { _isConstructorGroup(group, parsedGroup) || _isFieldGroup(group, parsedGroup) || _isGetSetGroup(group, parsedGroup) || - _isFlutterMethodGroup(group, parsedGroup) || _isMethodGroup(group, parsedGroup), ) .sorted( @@ -211,28 +210,12 @@ class _Visitor extends RecursiveAstVisitor> { parsedGroup is _MethodMemberGroup && (!group.isStatic || group.isStatic == parsedGroup.isStatic) && (!group.isNullable || group.isNullable == parsedGroup.isNullable) && - !group.isBuild && - !group.isDidChangeDependencies && - !group.isDidUpdateWidget && - !group.isInitState && - !group.isDispose && + (group.name == null || group.name == parsedGroup.name) && (group.modifier == _Modifier.unset || group.modifier == parsedGroup.modifier) && (group.annotation == _Annotation.unset || group.annotation == parsedGroup.annotation); - bool _isFlutterMethodGroup(_MemberGroup group, _MemberGroup parsedGroup) => - group is _MethodMemberGroup && - parsedGroup is _MethodMemberGroup && - ((group.isBuild && group.isBuild == parsedGroup.isBuild) || - (group.isInitState && group.isInitState == parsedGroup.isInitState) || - (group.isDidChangeDependencies && - group.isDidChangeDependencies == - parsedGroup.isDidChangeDependencies) || - (group.isDidUpdateWidget && - group.isDidUpdateWidget == parsedGroup.isDidUpdateWidget) || - (group.isDispose && group.isDispose == parsedGroup.isDispose)); - bool _isGetSetGroup(_MemberGroup group, _MemberGroup parsedGroup) => group is _GetSetMemberGroup && parsedGroup is _GetSetMemberGroup && diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/examples/flutter_widget_example.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/examples/flutter_widget_example.dart index 995a6bae9f..95b26af075 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/examples/flutter_widget_example.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/examples/flutter_widget_example.dart @@ -30,13 +30,13 @@ class Widget {} class StatelessWidget extends Widget { Widget build(BuildContext context); - void initState(); + void initState(); // LINT - void didChangeDependencies(); + void didChangeDependencies(); // LINT - void didUpdateWidget(); + void didUpdateWidget(); // LINT - void dispose(); + void dispose(); // LINT void someOtherMethod(); } diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/member_ordering_rule_test.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/member_ordering_rule_test.dart index 90c4b494ac..67fcd67135 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/member_ordering_rule_test.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/member_ordering/member_ordering_rule_test.dart @@ -74,14 +74,16 @@ void main() { RuleTestHelper.verifyIssues( issues: issues, - startLines: [12], - startColumns: [3], + startLines: [12, 33], + startColumns: [3, 3], locationTexts: [ '@override\n' ' void initState() {}', + 'void initState();', ], messages: [ 'init-state-method should be before build-method.', + 'init-state-method should be before build-method.', ], ); }); @@ -251,8 +253,8 @@ void main() { RuleTestHelper.verifyIssues( issues: issues, - startLines: [7, 12, 15, 18, 21, 24], - startColumns: [3, 3, 3, 3, 3, 3], + startLines: [7, 12, 15, 18, 21, 24, 33, 35, 37, 39], + startColumns: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], locationTexts: [ '@override\n' ' Widget build(BuildContext context) {\n' @@ -268,6 +270,10 @@ void main() { ' void dispose() {}', '@override\n' ' void someOtherMethod() {}', + 'void initState();', + 'void didChangeDependencies();', + 'void didUpdateWidget();', + 'void dispose();', ], messages: [ 'build-method should be before public-methods.', @@ -276,6 +282,10 @@ void main() { 'did-update-widget-method should be before did-change-dependencies-method.', 'dispose-method should be before did-update-widget-method.', 'overridden-methods should be before dispose-method.', + 'init-state-method should be before build-method.', + 'did-change-dependencies-method should be before init-state-method.', + 'did-update-widget-method should be before did-change-dependencies-method.', + 'dispose-method should be before did-update-widget-method.', ], ); }); diff --git a/website/docs/rules/common/member-ordering.mdx b/website/docs/rules/common/member-ordering.mdx index 19f2a95dca..93622713c8 100644 --- a/website/docs/rules/common/member-ordering.mdx +++ b/website/docs/rules/common/member-ordering.mdx @@ -12,6 +12,13 @@ The value for the `order` or `widgets-order` entries should match the following where values in the `<>` are optional, values in the `()` are interchangeable and the last part of the pattern which represents a class member type is **REQUIRED**. +You can also apply order to a separate method by listing its name like: + +- build-method +- dispose-method +- init-state-method +- my-castom-cool-thing-method + :::info Not all of the pattern parts are applicable for every case, for example, `late-constructors` are not expected, since they are not supported by the language itself. @@ -106,6 +113,22 @@ dart_code_metrics: **OR** Flutter specific: +```yaml +dart_code_metrics: + ... + rules: + ... + - member-ordering: + widgets-order: + - build-method + - init-state-method + - did-change-dependencies-method + - did-update-widget-method + - dispose-method +``` + +**OR** both custom and Flutter specific: + ```yaml dart_code_metrics: ... @@ -113,6 +136,13 @@ dart_code_metrics: ... - member-ordering: order: + - public-fields + - private-fields + - constructors + - close-method + - dispose-method + widgets-order: + - constructor - build-method - init-state-method - did-change-dependencies-method