Skip to content

Commit 330eb89

Browse files
feat: add named_parameters_ordering (#204)
* feat: add named_parameters_ordering * change default named parameters ordering in rule config and test extract models from visitor into their separate files rename some ParameterType enums to avoid using dart keywords add ParameterType.fromType remove unnecessary base class from documentation text perf(rule): make visitor a class member to avoid recreating new instance update test with new default ordering * Update CHANGELOG.md --------- Co-authored-by: vladimir-beloded <[email protected]>
1 parent 6992bc3 commit 330eb89

12 files changed

+582
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.2
2+
3+
- Added `named_parameters_ordering` rule.
4+
15
## 0.3.1
26

37
- Added `allow_with_comments` parameter for `no_empty_block` lint.

lib/solid_lints.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexit
1717
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
1818
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_rule.dart';
1919
import 'package:solid_lints/src/lints/member_ordering/member_ordering_rule.dart';
20+
import 'package:solid_lints/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart';
2021
import 'package:solid_lints/src/lints/newline_before_return/newline_before_return_rule.dart';
2122
import 'package:solid_lints/src/lints/no_empty_block/no_empty_block_rule.dart';
2223
import 'package:solid_lints/src/lints/no_equal_then_else/no_equal_then_else_rule.dart';
@@ -65,6 +66,7 @@ class _SolidLints extends PluginBase {
6566
AvoidDebugPrintInReleaseRule.createRule(configs),
6667
PreferEarlyReturnRule.createRule(configs),
6768
AvoidFinalWithGetterRule.createRule(configs),
69+
NamedParametersOrderingRule.createRule(configs),
6870
];
6971

7072
// Return only enabled rules
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// MIT License
2+
//
3+
// Copyright (c) 2020-2021 Dart Code Checker team
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
24+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/parameter_type.dart';
25+
26+
/// Helper class to parse member_ordering rule config
27+
class NamedParametersConfigParser {
28+
static const _defaultOrderList = [
29+
'required_super',
30+
'super',
31+
'required',
32+
'nullable',
33+
'default',
34+
];
35+
36+
/// Parse rule config for regular class order rules
37+
static List<ParameterType> parseOrder(Object? orderConfig) {
38+
final order = orderConfig is Iterable
39+
? List<String>.from(orderConfig)
40+
: _defaultOrderList;
41+
42+
return order.map(ParameterType.fromType).nonNulls.toList();
43+
}
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// MIT License
2+
//
3+
// Copyright (c) 2020-2021 Dart Code Checker team
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
24+
import 'package:solid_lints/src/lints/named_parameters_ordering/config_parser.dart';
25+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/parameter_type.dart';
26+
27+
/// A data model class that represents the parameter ordering input
28+
/// parameters.
29+
class NamedParametersOrderingParameters {
30+
/// Config keys
31+
static const _orderConfig = 'order';
32+
33+
/// Config used for order of named parameters
34+
final List<ParameterType> order;
35+
36+
/// Constructor for [NamedParametersOrderingParameters] model
37+
const NamedParametersOrderingParameters({required this.order});
38+
39+
/// Method for creating from json data
40+
factory NamedParametersOrderingParameters.fromJson(
41+
Map<String, Object?> json,
42+
) =>
43+
NamedParametersOrderingParameters(
44+
order: NamedParametersConfigParser.parseOrder(
45+
json[_orderConfig],
46+
),
47+
);
48+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/parameter_ordering_info.dart';
3+
4+
/// Data class that holds AST function parameter and it's order info
5+
class ParameterInfo {
6+
/// AST instance of an [FormalParameter]
7+
final FormalParameter formalParameter;
8+
9+
/// Function parameter order info
10+
final ParameterOrderingInfo parameterOrderingInfo;
11+
12+
/// Creates instance of an [ParameterInfo]
13+
const ParameterInfo({
14+
required this.formalParameter,
15+
required this.parameterOrderingInfo,
16+
});
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/parameter_type.dart';
2+
3+
/// Data class holds information about parameter order info
4+
class ParameterOrderingInfo {
5+
/// Indicates if order is wrong
6+
final bool isWrong;
7+
8+
/// Info about current parameter parameter type
9+
final ParameterType parameterType;
10+
11+
/// Info about previous parameter parameter type
12+
final ParameterType? previousParameterType;
13+
14+
/// Creates instance of [ParameterOrderingInfo]
15+
const ParameterOrderingInfo({
16+
required this.isWrong,
17+
required this.parameterType,
18+
required this.previousParameterType,
19+
});
20+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:collection/collection.dart';
2+
3+
/// Represents a function parameter type
4+
enum ParameterType {
5+
/// Inherited (super) parameter type (super.parameterName)
6+
inherited('super'),
7+
8+
/// Required inherited (super) parameter type (required super.parameterName)
9+
requiredInherited('required_super'),
10+
11+
/// Required parameter type (required String parameterName)
12+
required('required'),
13+
14+
/// Nullable parameter type (String? parameterName)
15+
nullable('nullable'),
16+
17+
/// Default value parameter type (String parameterName = 'defaultValue')
18+
defaultValue('default');
19+
20+
/// Returns [ParameterType] from type or null if not found
21+
static ParameterType? fromType(String type) {
22+
return values.firstWhereOrNull((o) => o.type == type);
23+
}
24+
25+
/// String representation of the parameter type
26+
final String type;
27+
28+
/// Display name of the parameter type
29+
String get displayName => type.replaceAll('_', ' ');
30+
31+
const ParameterType(this.type);
32+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import 'package:analyzer/error/listener.dart';
2+
import 'package:custom_lint_builder/custom_lint_builder.dart';
3+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/named_parameters_ordering_parameters.dart';
4+
import 'package:solid_lints/src/lints/named_parameters_ordering/models/parameter_ordering_info.dart';
5+
import 'package:solid_lints/src/lints/named_parameters_ordering/visitors/named_parameters_ordering_visitor.dart';
6+
import 'package:solid_lints/src/models/rule_config.dart';
7+
import 'package:solid_lints/src/models/solid_lint_rule.dart';
8+
9+
/// A lint which allows to enforce a particular named parameter ordering
10+
/// conventions.
11+
///
12+
/// ### Configuration format
13+
/// ```yaml
14+
/// - named_parameters_ordering:
15+
/// order:
16+
/// - (parameterType)
17+
/// ```
18+
/// Where parameterType can be one of:
19+
///
20+
/// - `super`
21+
/// - `required_super`
22+
/// - `required`
23+
/// - `nullable`
24+
/// - `default`
25+
///
26+
/// ### Example:
27+
///
28+
/// Assuming config:
29+
///
30+
/// ```yaml
31+
/// custom_lint:
32+
/// rules:
33+
/// - named_parameters_ordering:
34+
/// order:
35+
/// - required
36+
/// - required_super
37+
/// - default
38+
/// - nullable
39+
/// - super
40+
/// ```
41+
///
42+
/// #### BAD:
43+
///
44+
/// ```dart
45+
/// class UserProfile extends User {
46+
/// final String? age;
47+
/// final String? country;
48+
/// final String email;
49+
/// final bool isActive;
50+
/// final String name;
51+
///
52+
/// UserProfile({
53+
/// this.age,
54+
/// required super.accountType, // LINT, required super named parameters should be before nullable named parameters
55+
/// required this.name, // LINT, required named parameters should be before super named parameters
56+
/// super.userId,
57+
/// this.country, // LINT, nullable named parameters should be before super named parameters
58+
/// this.isActive = true, // LINT, default named parameters should be before nullable named parameters
59+
/// required this.email, // LINT, required named parameters should be before default named parameters
60+
/// });
61+
///
62+
/// void doSomething({
63+
/// required String name,
64+
/// int? age,
65+
/// bool isActive = true, // LINT, default named parameters should be before nullable named parameters
66+
/// required String email, // LINT, required named parameters should be before default named parameters
67+
/// }) {
68+
/// return;
69+
/// }
70+
/// }
71+
/// ```
72+
///
73+
/// #### GOOD:
74+
///
75+
/// ```dart
76+
/// class UserProfile extends User {
77+
/// final String? age;
78+
/// final String? country;
79+
/// final String email;
80+
/// final bool isActive;
81+
/// final String name;
82+
///
83+
/// UserProfile({
84+
/// required this.name,
85+
/// required this.email,
86+
/// required super.accountType,
87+
/// this.isActive = true,
88+
/// this.age,
89+
/// this.country,
90+
/// super.userId,
91+
/// });
92+
///
93+
/// void doSomething({
94+
/// required String name,
95+
/// required String email,
96+
/// bool isActive = true,
97+
/// int? age,
98+
/// }) {
99+
/// return;
100+
/// }
101+
/// }
102+
/// ```
103+
class NamedParametersOrderingRule
104+
extends SolidLintRule<NamedParametersOrderingParameters> {
105+
/// The name of this lint rule.
106+
static const lintName = 'named_parameters_ordering';
107+
108+
late final _visitor = NamedParametersOrderingVisitor(config.parameters.order);
109+
110+
NamedParametersOrderingRule._(super.config);
111+
112+
/// Creates a new instance of [NamedParametersOrderingRule]
113+
/// based on the lint configuration.
114+
factory NamedParametersOrderingRule.createRule(CustomLintConfigs configs) {
115+
final config = RuleConfig<NamedParametersOrderingParameters>(
116+
configs: configs,
117+
name: lintName,
118+
paramsParser: NamedParametersOrderingParameters.fromJson,
119+
problemMessage: (_) => "Order of named parameter is wrong",
120+
);
121+
122+
return NamedParametersOrderingRule._(config);
123+
}
124+
125+
@override
126+
void run(
127+
CustomLintResolver resolver,
128+
ErrorReporter reporter,
129+
CustomLintContext context,
130+
) {
131+
context.registry.addFormalParameterList((node) {
132+
final parametersInfo = _visitor.visitFormalParameterList(node);
133+
134+
final wrongOrderParameters = parametersInfo.where(
135+
(info) => info.parameterOrderingInfo.isWrong,
136+
);
137+
138+
for (final parameterInfo in wrongOrderParameters) {
139+
reporter.atNode(
140+
parameterInfo.formalParameter,
141+
_createWrongOrderLintCode(parameterInfo.parameterOrderingInfo),
142+
);
143+
}
144+
});
145+
}
146+
147+
LintCode _createWrongOrderLintCode(ParameterOrderingInfo info) {
148+
final parameterOrdering = info.parameterType;
149+
final previousParameterOrdering = info.previousParameterType;
150+
151+
return LintCode(
152+
name: lintName,
153+
problemMessage: "${parameterOrdering.displayName} named parameters"
154+
" should be before "
155+
"${previousParameterOrdering!.displayName} named parameters.",
156+
);
157+
}
158+
}

0 commit comments

Comments
 (0)