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);
}
}
19 changes: 14 additions & 5 deletions lib/src/lints/member_ordering/models/annotation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import 'package:solid_lints/src/utils/implies.dart';

/// A data model enum represents annotation
enum Annotation {
/// override annotation
Expand All @@ -43,9 +45,16 @@ enum Annotation {

/// Parses a String name and returns instance of [Annotation]
static Annotation parse(String? name) => values.firstWhere(
(annotation) =>
annotation.name == name ||
(annotation.publicName != null && annotation.publicName == name),
orElse: () => Annotation.unset,
);
(annotation) =>
annotation.name == name ||
(annotation.publicName != null && annotation.publicName == name),
orElse: () => Annotation.unset,
);
}

/// Logical implication operation for annotation
extension AnnotationImplies on Annotation {
/// Logical implication operation.
bool implies(Annotation other) =>
objectImplies(this, other, Annotation.unset);
}
15 changes: 12 additions & 3 deletions lib/src/lints/member_ordering/models/field_keyword.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import 'package:solid_lints/src/utils/implies.dart';

/// A data model enum represents field type keyword
enum FieldKeyword {
/// final keyword
Expand All @@ -43,7 +45,14 @@ enum FieldKeyword {

/// Parses a String field type and returns instance of [FieldKeyword]
static FieldKeyword parse(String? name) => values.firstWhere(
(type) => type.type == name,
orElse: () => FieldKeyword.unset,
);
(type) => type.type == name,
orElse: () => FieldKeyword.unset,
);
}

/// Logical implication operation for field keyword
extension FieldKeywordImplies on FieldKeyword {
/// Logical implication operation.
bool implies(FieldKeyword other) =>
objectImplies(this, other, FieldKeyword.unset);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:solid_lints/src/lints/member_ordering/models/annotation.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_group/member_group.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_type.dart';
import 'package:solid_lints/src/lints/member_ordering/models/modifier.dart';
import 'package:solid_lints/src/utils/implies.dart';

/// Data class represents class constructor
class ConstructorMemberGroup extends MemberGroup {
Expand Down Expand Up @@ -80,4 +81,11 @@ class ConstructorMemberGroup extends MemberGroup {

@override
String toString() => rawRepresentation;

@override
bool implies(MemberGroup other) =>
other is ConstructorMemberGroup &&
super.implies(other) &&
isFactory.implies(other.isFactory) &&
isNamed.implies(other.isNamed);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import 'package:solid_lints/src/lints/member_ordering/models/field_keyword.dart'
import 'package:solid_lints/src/lints/member_ordering/models/member_group/member_group.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_type.dart';
import 'package:solid_lints/src/lints/member_ordering/models/modifier.dart';
import 'package:solid_lints/src/utils/implies.dart';

/// Data class represents class field
class FieldMemberGroup extends MemberGroup {
Expand Down Expand Up @@ -62,7 +63,8 @@ class FieldMemberGroup extends MemberGroup {
final modifier = parseModifier(
declaration.fields.variables.first.name.lexeme,
);
final isNullable = declaration.fields.type?.type?.nullabilitySuffix ==
final isNullable =
declaration.fields.type?.type?.nullabilitySuffix ==
NullabilitySuffix.question;
final keyword = _FieldMemberGroupUtils.parseKeyWord(declaration);

Expand Down Expand Up @@ -94,14 +96,22 @@ class FieldMemberGroup extends MemberGroup {

@override
String toString() => rawRepresentation;

@override
bool implies(MemberGroup other) =>
other is FieldMemberGroup &&
super.implies(other) &&
isLate.implies(other.isLate) &&
isStatic.implies(other.isStatic) &&
isNullable.implies(other.isNullable) &&
keyword.implies(other.keyword);
}

class _FieldMemberGroupUtils {
static FieldKeyword parseKeyWord(FieldDeclaration declaration) {
return declaration.fields.isConst
? FieldKeyword.isConst
: declaration.fields.isFinal
? FieldKeyword.isFinal
: FieldKeyword.unset;
}
static FieldKeyword parseKeyWord(FieldDeclaration declaration) =>
switch ((declaration.fields.isConst, declaration.fields.isFinal)) {
(true, _) => FieldKeyword.isConst,
(_, true) => FieldKeyword.isFinal,
_ => FieldKeyword.unset,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:solid_lints/src/lints/member_ordering/models/annotation.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_group/member_group.dart';
import 'package:solid_lints/src/lints/member_ordering/models/member_type.dart';
import 'package:solid_lints/src/lints/member_ordering/models/modifier.dart';
import 'package:solid_lints/src/utils/implies.dart';

/// Data class represents class getter or setter
class GetSetMemberGroup extends MemberGroup {
Expand Down Expand Up @@ -76,4 +77,12 @@ class GetSetMemberGroup extends MemberGroup {

@override
String toString() => rawRepresentation;

@override
bool implies(MemberGroup other) =>
other is GetSetMemberGroup &&
super.implies(other) &&
isStatic.implies(other.isStatic) &&
isNullable.implies(other.isNullable) &&
memberType.implies(other.memberType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ abstract class MemberGroup {

/// Method to get sorting coefficient of the member group
int getSortingCoefficient();

/// Logical implication operation.
bool implies(MemberGroup other) =>
modifier.implies(other.modifier) && annotation.implies(other.annotation);
}
Loading