Skip to content

Commit babf88e

Browse files
committed
feat: add new rule use_descriptive_names_for_type_parameters
1 parent b6ce57a commit babf88e

File tree

5 files changed

+194
-0
lines changed

5 files changed

+194
-0
lines changed

lib/solid_lints.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import 'package:solid_lints/src/lints/prefer_first/prefer_first_rule.dart';
2929
import 'package:solid_lints/src/lints/prefer_last/prefer_last_rule.dart';
3030
import 'package:solid_lints/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart';
3131
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';
32+
import 'package:solid_lints/src/lints/use_descriptive_names_for_type_parameters/use_descriptive_names_for_type_parameters_rule.dart';
3233
import 'package:solid_lints/src/models/solid_lint_rule.dart';
3334

3435
/// Creates a plugin for our custom linter
@@ -67,6 +68,7 @@ class _SolidLints extends PluginBase {
6768
PreferEarlyReturnRule.createRule(configs),
6869
AvoidFinalWithGetterRule.createRule(configs),
6970
NamedParametersOrderingRule.createRule(configs),
71+
UseDescriptiveNamesForTypeParametersRule.createRule(configs),
7072
];
7173

7274
// Return only enabled rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/error/listener.dart';
3+
import 'package:custom_lint_builder/custom_lint_builder.dart';
4+
import 'package:solid_lints/src/lints/use_descriptive_names_for_type_parameters/visitors/use_descriptive_names_for_type_parameters_visitor.dart';
5+
import 'package:solid_lints/src/models/rule_config.dart';
6+
import 'package:solid_lints/src/models/solid_lint_rule.dart';
7+
8+
/// A `use_descriptive_names_for_type_parameters` rule which
9+
/// warns about single-letter type parameter names when there are
10+
/// three or more type parameters.
11+
class UseDescriptiveNamesForTypeParametersRule extends SolidLintRule {
12+
/// The lint rule name.
13+
static const lintName = 'use_descriptive_names_for_type_parameters';
14+
15+
UseDescriptiveNamesForTypeParametersRule._(super.config);
16+
17+
/// Creates a new instance of [UseDescriptiveNamesForTypeParametersRule]
18+
/// based on the lint configuration.
19+
factory UseDescriptiveNamesForTypeParametersRule.createRule(
20+
CustomLintConfigs configs,
21+
) {
22+
final rule = RuleConfig(
23+
configs: configs,
24+
name: lintName,
25+
problemMessage: (_) =>
26+
'Type parameters should have descriptive names instead '
27+
'of single letters when there are three or more type parameters.',
28+
);
29+
30+
return UseDescriptiveNamesForTypeParametersRule._(rule);
31+
}
32+
33+
@override
34+
void run(
35+
CustomLintResolver resolver,
36+
ErrorReporter reporter,
37+
CustomLintContext context,
38+
) {
39+
context.registry.addClassDeclaration((node) {
40+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
41+
visitor.visitClassDeclaration(node);
42+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
43+
});
44+
45+
context.registry.addFunctionDeclaration((node) {
46+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
47+
visitor.visitFunctionDeclaration(node);
48+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
49+
});
50+
51+
context.registry.addMethodDeclaration((node) {
52+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
53+
visitor.visitMethodDeclaration(node);
54+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
55+
});
56+
57+
context.registry.addGenericTypeAlias((node) {
58+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
59+
visitor.visitGenericTypeAlias(node);
60+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
61+
});
62+
63+
context.registry.addExtensionDeclaration((node) {
64+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
65+
visitor.visitExtensionDeclaration(node);
66+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
67+
});
68+
69+
context.registry.addMixinDeclaration((node) {
70+
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
71+
visitor.visitMixinDeclaration(node);
72+
_reportViolations(reporter, visitor.singleLetterTypeParameters);
73+
});
74+
}
75+
76+
void _reportViolations(
77+
ErrorReporter reporter,
78+
List<TypeParameter> singleLetterTypeParameters,
79+
) {
80+
for (final param in singleLetterTypeParameters) {
81+
reporter.atNode(param, code);
82+
}
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/dart/ast/visitor.dart';
3+
4+
/// AST Visitor which finds type parameters with single-letter names
5+
/// in declarations with three or more type parameters.
6+
class UseDescriptiveNamesForTypeParametersVisitor
7+
extends RecursiveAstVisitor<void> {
8+
/// List of type parameters with single-letter names.
9+
final singleLetterTypeParameters = <TypeParameter>[];
10+
11+
@override
12+
void visitClassDeclaration(ClassDeclaration node) {
13+
super.visitClassDeclaration(node);
14+
_checkTypeParameters(node.typeParameters);
15+
}
16+
17+
@override
18+
void visitFunctionDeclaration(FunctionDeclaration node) {
19+
super.visitFunctionDeclaration(node);
20+
final function = node.functionExpression;
21+
_checkTypeParameters(function.typeParameters);
22+
}
23+
24+
@override
25+
void visitMethodDeclaration(MethodDeclaration node) {
26+
super.visitMethodDeclaration(node);
27+
_checkTypeParameters(node.typeParameters);
28+
}
29+
30+
@override
31+
void visitGenericTypeAlias(GenericTypeAlias node) {
32+
super.visitGenericTypeAlias(node);
33+
_checkTypeParameters(node.typeParameters);
34+
}
35+
36+
@override
37+
void visitExtensionDeclaration(ExtensionDeclaration node) {
38+
super.visitExtensionDeclaration(node);
39+
_checkTypeParameters(node.typeParameters);
40+
}
41+
42+
@override
43+
void visitMixinDeclaration(MixinDeclaration node) {
44+
super.visitMixinDeclaration(node);
45+
_checkTypeParameters(node.typeParameters);
46+
}
47+
48+
void _checkTypeParameters(TypeParameterList? typeParameters) {
49+
if (typeParameters == null || typeParameters.typeParameters.length < 3) {
50+
return;
51+
}
52+
53+
for (final param in typeParameters.typeParameters) {
54+
final name = param.name.lexeme;
55+
if (name.length == 1) {
56+
singleLetterTypeParameters.add(param);
57+
}
58+
}
59+
}
60+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
custom_lint:
2+
rules:
3+
- use_descriptive_names_for_type_parameters
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/// Check the `use_descriptive_names_for_type_parameters` rule
2+
3+
// expect_lint: use_descriptive_names_for_type_parameters
4+
class SomeClass<T, U, K> {}
5+
6+
class ValidClass<Type, Data, Context> {}
7+
8+
class TwoParams<T, U> {}
9+
10+
// expect_lint: use_descriptive_names_for_type_parameters
11+
class AnotherClass<A, B, C, D> {}
12+
13+
// expect_lint: use_descriptive_names_for_type_parameters
14+
void functionWithTypes<T, U, K>(T t, U u, K k) {}
15+
16+
void validFunction<Type, Data, Context>(Type t, Data d, Context c) {}
17+
18+
void twoTypeParams<T, U>(T t, U u) {}
19+
20+
// expect_lint: use_descriptive_names_for_type_parameters
21+
class ComplexClass<X, Y, Z, W> {
22+
// expect_lint: use_descriptive_names_for_type_parameters
23+
void method<T, U, K>() {}
24+
}
25+
26+
class ValidComplexClass<Type, Data, Context, State> {
27+
void validMethod<Key, Value, Result>() {}
28+
}
29+
30+
// expect_lint: use_descriptive_names_for_type_parameters
31+
typedef SomeAlias<T, U, K> = Map<T, Map<U, K>>;
32+
33+
typedef ValidAlias<Type, Data, Context> = Map<Type, Map<Data, Context>>;
34+
35+
typedef TwoParamAlias<T, U> = Map<T, U>;
36+
37+
// expect_lint: use_descriptive_names_for_type_parameters
38+
extension Ext<T, U, K> on List<T> {}
39+
40+
extension ValidExt<Type, Data, Context> on List<Type> {}
41+
42+
// expect_lint: use_descriptive_names_for_type_parameters
43+
mixin Mixin<T, U, K> {}
44+
45+
mixin ValidMixin<Type, Data, Context> {}

0 commit comments

Comments
 (0)