Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:solid_lints/src/lints/avoid_unnecessary_type_assertions/fixes/av
import 'package:solid_lints/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart';
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
import 'package:solid_lints/src/lints/double_literal_format/fixes/double_literal_format_fix.dart';
import 'package:solid_lints/src/lints/member_ordering/member_ordering_rule.dart';
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';

/// The entry point for the Solid Lints analyser server plugin.
Expand Down Expand Up @@ -49,6 +50,9 @@ class SolidLintsPlugin extends Plugin {
AvoidUnusedParametersRule(
analysisOptionsLoader: analysisLoader,
),
MemberOrderingRule(
analysisOptionsLoader: analysisLoader,
),
];

for (final lintRule in lintRules) {
Expand Down
1 change: 0 additions & 1 deletion lib/src/lints/member_ordering/config_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class MemberOrderingConfigParser {
'final_fields',
'init_state_method',
'var_fields',
'init_state_method',
'private_methods',
'overridden_public_methods',
'build_method',
Expand Down
172 changes: 60 additions & 112 deletions lib/src/lints/member_ordering/member_ordering_rule.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:analyzer/analysis_rule/rule_context.dart';
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
import 'package:analyzer/error/error.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_ordering_parameters.dart';
import 'package:solid_lints/src/lints/member_ordering/visitors/member_ordering_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';
import 'package:solid_lints/src/models/solid_multi_lint_rule.dart';

/// A lint which allows to enforce a particular class member ordering
/// conventions.
Expand Down Expand Up @@ -45,14 +45,15 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
/// Assuming config:
///
/// ```yaml
/// custom_lint:
/// rules:
/// - member_ordering:
/// alphabetize: true
/// order:
/// - fields
/// - getters_setters
/// - methods
/// plugins:
/// solid_lints:
/// diagnostics:
/// member_ordering:
/// alphabetize: true
/// order:
/// - fields
/// - getters_setters
/// - methods
/// ```
///
/// #### BAD:
Expand Down Expand Up @@ -82,113 +83,60 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
/// void method() {}
/// }
/// ```
class MemberOrderingRule extends SolidLintRule<MemberOrderingParameters> {
/// This lint rule represents
/// the error whether we use bad formatted double literals.
class MemberOrderingRule extends SolidMultiLintRule<MemberOrderingParameters> {
/// The name of this lint rule.
static const lintName = 'member_ordering';

static const _warningMessage = 'should be before';
static const _warningAlphabeticalMessage = 'should be alphabetically before';
static const _warningTypeAlphabeticalMessage =
'type name should be alphabetically before';
/// Reported when a class member is declared out of order.
static const wrongOrderCode = LintCode(
lintName,
'{0} should be before {1}.',
uniqueName: 'wrong_order',
);

/// Reported when class members in the same group are not sorted
/// alphabetically.
static const alphabeticalOrderCode = LintCode(
lintName,
'{0} should be alphabetically before {1}.',
uniqueName: 'alphabetical_order',
);

/// Reported when class members in the same group are not sorted
/// alphabetically by type name.
static const alphabeticalByTypeOrderCode = LintCode(
lintName,
'{0} type name should be alphabetically before {1}.',
uniqueName: 'alphabetical_by_type_order',
);

/// Creates a new instance of [MemberOrderingRule].
MemberOrderingRule({
required super.analysisOptionsLoader,
}) : super(
name: lintName,
description: 'Enforces a particular class member ordering convention.',
parametersParser: MemberOrderingParameters.fromJson,
);

MemberOrderingRule._(super.config);

/// Creates a new instance of [MemberOrderingRule]
/// based on the lint configuration.
factory MemberOrderingRule.createRule(CustomLintConfigs configs) {
final config = RuleConfig<MemberOrderingParameters>(
configs: configs,
name: lintName,
paramsParser: MemberOrderingParameters.fromJson,
problemMessage: (_) => "Order of class member is wrong",
);

return MemberOrderingRule._(config);
}
@override
List<DiagnosticCode> get diagnosticCodes => [
wrongOrderCode,
alphabeticalOrderCode,
alphabeticalByTypeOrderCode,
];

@override
void run(
CustomLintResolver resolver,
DiagnosticReporter reporter,
CustomLintContext context,
void registerNodeProcessors(
RuleVisitorRegistry registry,
RuleContext context,
) {
context.registry.addClassDeclaration((node) {
final visitor = MemberOrderingVisitor(
config.parameters.groupsOrder,
config.parameters.widgetsGroupsOrder,
);

final membersInfo = visitor.visitClassDeclaration(node);
final wrongOrderMembers = membersInfo.where(
(info) => info.memberOrder.isWrong,
);

for (final memberInfo in wrongOrderMembers) {
reporter.atNode(
memberInfo.classMember,
_createWrongOrderLintCode(memberInfo),
);
}

if (config.parameters.alphabetize) {
final alphabeticallyWrongOrderMembers = membersInfo.where(
(info) => info.memberOrder.isAlphabeticallyWrong,
);

for (final memberInfo in alphabeticallyWrongOrderMembers) {
reporter.atNode(
memberInfo.classMember,
_createAlphabeticallyWrongOrderLintCode(memberInfo),
);
}
}

if (!config.parameters.alphabetize &&
config.parameters.alphabetizeByType) {
final alphabeticallyByTypeWrongOrderMembers = membersInfo.where(
(info) => info.memberOrder.isByTypeWrong,
);

for (final memberInfo in alphabeticallyByTypeWrongOrderMembers) {
reporter.atNode(
memberInfo.classMember,
_createAlphabeticallyByTypeWrongOrderLintCode(memberInfo),
);
}
}
});
}

LintCode _createWrongOrderLintCode(MemberInfo info) {
final memberGroup = info.memberOrder.memberGroup;
final previousMemberGroup = info.memberOrder.previousMemberGroup;

return LintCode(
name: lintName,
problemMessage: "$memberGroup $_warningMessage $previousMemberGroup.",
);
}

LintCode _createAlphabeticallyWrongOrderLintCode(MemberInfo info) {
final names = info.memberOrder.memberNames;
final current = names.currentName;
final previous = names.previousName;

return LintCode(
name: lintName,
problemMessage: "$current $_warningAlphabeticalMessage $previous.",
);
}
super.registerNodeProcessors(registry, context);

LintCode _createAlphabeticallyByTypeWrongOrderLintCode(MemberInfo info) {
final names = info.memberOrder.memberNames;
final current = names.currentName;
final previous = names.previousName;
final parameters =
getParametersForContext(context) ?? MemberOrderingParameters.empty();
final visitor = MemberOrderingVisitor(this, parameters);

return LintCode(
name: lintName,
problemMessage: "$current $_warningTypeAlphabeticalMessage $previous",
);
registry.addClassDeclaration(this, visitor);
}
}
17 changes: 17 additions & 0 deletions lib/src/lints/member_ordering/models/member_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_order.dart';

/// Data class that holds AST class member and it's order info
class MemberInfo {
/// AST instance of an [ClassMember]
final ClassMember classMember;

/// Class member order info
final MemberOrder memberOrder;

/// Creates instance of an [MemberInfo]
const MemberInfo({
required this.classMember,
required this.memberOrder,
});
}
22 changes: 22 additions & 0 deletions lib/src/lints/member_ordering/models/member_names.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// Data class contains info about current and previous class member names
class MemberNames {
/// Name of current class member
final String currentName;

/// Name of previous class member
final String? previousName;

/// Type name of current class member
final String currentTypeName;

/// Type name of previous class member
final String? previousTypeName;

/// Creates instance of [MemberNames]
const MemberNames({
required this.currentName,
required this.currentTypeName,
this.previousName,
this.previousTypeName,
});
}
33 changes: 33 additions & 0 deletions lib/src/lints/member_ordering/models/member_order.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:solid_lints/src/lints/member_ordering/models/member_group/member_group.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_names.dart';

/// Data class holds information about class member order info
class MemberOrder {
/// Indicates if order is wrong
final bool isWrong;

/// Indicates if order is wrong alphabetically
final bool isAlphabeticallyWrong;

/// Indicates if order is wrong alphabetically by type
final bool isByTypeWrong;

/// Info about current and previous class member name
final MemberNames memberNames;

/// Info about current member member group
final MemberGroup memberGroup;

/// Info about previous member member group
final MemberGroup? previousMemberGroup;

/// Creates instance of [MemberOrder]
const MemberOrder({
required this.isWrong,
required this.isAlphabeticallyWrong,
required this.isByTypeWrong,
required this.memberNames,
required this.memberGroup,
this.previousMemberGroup,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ class MemberOrderingParameters {
required this.alphabetizeByType,
});

/// Factory for creating empty/default parameters.
factory MemberOrderingParameters.empty() => MemberOrderingParameters(
groupsOrder: MemberOrderingConfigParser.parseOrder(null),
widgetsGroupsOrder: MemberOrderingConfigParser.parseWidgetsOrder(null),
alphabetize: false,
alphabetizeByType: false,
);

/// Method for creating from json data
factory MemberOrderingParameters.fromJson(Map<String, Object?> json) =>
MemberOrderingParameters(
Expand Down
2 changes: 1 addition & 1 deletion lib/src/lints/member_ordering/models/member_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ enum MemberType {
method('methods', typeAlias: 'method'),

/// Indicates constructor affiliation
constructor('constructors'),
constructor('constructors', typeAlias: 'constructor'),

/// Indicates getters affiliation
getter('getters'),
Expand Down
Loading