Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/solid_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import 'package:solid_lints/src/lints/prefer_first/prefer_first_rule.dart';
import 'package:solid_lints/src/lints/prefer_last/prefer_last_rule.dart';
import 'package:solid_lints/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart';
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';
import 'package:solid_lints/src/lints/use_descriptive_names_for_type_parameters/use_descriptive_names_for_type_parameters_rule.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';

/// Creates a plugin for our custom linter
Expand Down Expand Up @@ -67,6 +68,7 @@ class _SolidLints extends PluginBase {
PreferEarlyReturnRule.createRule(configs),
AvoidFinalWithGetterRule.createRule(configs),
NamedParametersOrderingRule.createRule(configs),
UseDescriptiveNamesForTypeParametersRule.createRule(configs),
];

// Return only enabled rules
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/src/lints/use_descriptive_names_for_type_parameters/visitors/use_descriptive_names_for_type_parameters_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';

/// A `use_descriptive_names_for_type_parameters` rule which
/// warns about single-letter type parameter names when there are
/// three or more type parameters.
class UseDescriptiveNamesForTypeParametersRule extends SolidLintRule {
/// The lint rule name.
static const lintName = 'use_descriptive_names_for_type_parameters';

UseDescriptiveNamesForTypeParametersRule._(super.config);

/// Creates a new instance of [UseDescriptiveNamesForTypeParametersRule]
/// based on the lint configuration.
factory UseDescriptiveNamesForTypeParametersRule.createRule(
CustomLintConfigs configs,
) {
final rule = RuleConfig(
configs: configs,
name: lintName,
problemMessage: (_) =>
'Type parameters should have descriptive names instead '
'of single letters when there are three or more type parameters.',
);

return UseDescriptiveNamesForTypeParametersRule._(rule);
}

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addClassDeclaration((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitClassDeclaration(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});

context.registry.addFunctionDeclaration((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitFunctionDeclaration(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});

context.registry.addMethodDeclaration((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitMethodDeclaration(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});

context.registry.addGenericTypeAlias((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitGenericTypeAlias(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});

context.registry.addExtensionDeclaration((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitExtensionDeclaration(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});

context.registry.addMixinDeclaration((node) {
final visitor = UseDescriptiveNamesForTypeParametersVisitor();
visitor.visitMixinDeclaration(node);
_reportViolations(reporter, visitor.singleLetterTypeParameters);
});
}

void _reportViolations(
ErrorReporter reporter,
List<TypeParameter> singleLetterTypeParameters,
) {
for (final param in singleLetterTypeParameters) {
reporter.atNode(param, code);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

/// AST Visitor which finds type parameters with single-letter names
/// in declarations with three or more type parameters.
class UseDescriptiveNamesForTypeParametersVisitor
extends RecursiveAstVisitor<void> {
/// List of type parameters with single-letter names.
final singleLetterTypeParameters = <TypeParameter>[];

@override
void visitClassDeclaration(ClassDeclaration node) {
super.visitClassDeclaration(node);
_checkTypeParameters(node.typeParameters);
}

@override
void visitFunctionDeclaration(FunctionDeclaration node) {
super.visitFunctionDeclaration(node);
final function = node.functionExpression;
_checkTypeParameters(function.typeParameters);
}

@override
void visitMethodDeclaration(MethodDeclaration node) {
super.visitMethodDeclaration(node);
_checkTypeParameters(node.typeParameters);
}

@override
void visitGenericTypeAlias(GenericTypeAlias node) {
super.visitGenericTypeAlias(node);
_checkTypeParameters(node.typeParameters);
}

@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
super.visitExtensionDeclaration(node);
_checkTypeParameters(node.typeParameters);
}

@override
void visitMixinDeclaration(MixinDeclaration node) {
super.visitMixinDeclaration(node);
_checkTypeParameters(node.typeParameters);
}

void _checkTypeParameters(TypeParameterList? typeParameters) {
if (typeParameters == null || typeParameters.typeParameters.length < 3) {
return;
}

for (final param in typeParameters.typeParameters) {
final name = param.name.lexeme;
if (name.length == 1) {
singleLetterTypeParameters.add(param);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
custom_lint:
rules:
- use_descriptive_names_for_type_parameters
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/// Check the `use_descriptive_names_for_type_parameters` rule

// expect_lint: use_descriptive_names_for_type_parameters
class SomeClass<T, U, K> {}

class ValidClass<Type, Data, Context> {}

class TwoParams<T, U> {}

// expect_lint: use_descriptive_names_for_type_parameters
class AnotherClass<A, B, C, D> {}

// expect_lint: use_descriptive_names_for_type_parameters
void functionWithTypes<T, U, K>(T t, U u, K k) {}

void validFunction<Type, Data, Context>(Type t, Data d, Context c) {}

void twoTypeParams<T, U>(T t, U u) {}

// expect_lint: use_descriptive_names_for_type_parameters
class ComplexClass<X, Y, Z, W> {
// expect_lint: use_descriptive_names_for_type_parameters
void method<T, U, K>() {}
}

class ValidComplexClass<Type, Data, Context, State> {
void validMethod<Key, Value, Result>() {}
}

// expect_lint: use_descriptive_names_for_type_parameters
typedef SomeAlias<T, U, K> = Map<T, Map<U, K>>;

typedef ValidAlias<Type, Data, Context> = Map<Type, Map<Data, Context>>;

typedef TwoParamAlias<T, U> = Map<T, U>;

// expect_lint: use_descriptive_names_for_type_parameters
extension Ext<T, U, K> on List<T> {}

extension ValidExt<Type, Data, Context> on List<Type> {}

// expect_lint: use_descriptive_names_for_type_parameters
mixin Mixin<T, U, K> {}

mixin ValidMixin<Type, Data, Context> {}