Skip to content
6 changes: 2 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexit
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/function_lines_of_code/function_lines_of_code_rule.dart';
import 'package:solid_lints/src/lints/member_ordering/member_ordering_rule.dart';
import 'package:solid_lints/src/lints/no_empty_block/no_empty_block_rule.dart';
import 'package:solid_lints/src/lints/no_magic_number/no_magic_number_rule.dart';
import 'package:solid_lints/src/lints/number_of_parameters/number_of_parameters_rule.dart';
Expand Down Expand Up @@ -58,13 +59,10 @@ class SolidLintsPlugin extends Plugin {
doubleLiteralFormatRule,
ProperSuperCallsRule(),
AvoidReturningWidgetsRule(analysisOptionsLoader: analysisLoader),
FunctionLinesOfCodeRule(analysisOptionsLoader: analysisLoader),
MemberOrderingRule(analysisOptionsLoader: analysisLoader),
AvoidUnusedParametersRule(analysisOptionsLoader: analysisLoader),
CyclomaticComplexityRule(analysisOptionsLoader: analysisLoader),
NumberOfParametersRule(analysisOptionsLoader: analysisLoader),
AvoidReturningWidgetsRule(analysisOptionsLoader: analysisLoader),
FunctionLinesOfCodeRule(analysisOptionsLoader: analysisLoader),
AvoidUnusedParametersRule(analysisOptionsLoader: analysisLoader),
CyclomaticComplexityRule(analysisOptionsLoader: analysisLoader),
NoEmptyBlockRule(analysisOptionsLoader: analysisLoader),
UseNearestContextRule(),
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
Loading