diff --git a/example/all.yaml b/example/all.yaml index 24c68149f..99ca53a95 100644 --- a/example/all.yaml +++ b/example/all.yaml @@ -56,6 +56,7 @@ linter: - cascade_invocations - cast_nullable_to_non_nullable - close_sinks + - combinators_ordering - comment_references - conditional_uri_does_not_exist - constant_identifier_names diff --git a/lib/src/rules.dart b/lib/src/rules.dart index 693d31e5f..a81acb725 100644 --- a/lib/src/rules.dart +++ b/lib/src/rules.dart @@ -58,6 +58,7 @@ import 'rules/cancel_subscriptions.dart'; import 'rules/cascade_invocations.dart'; import 'rules/cast_nullable_to_non_nullable.dart'; import 'rules/close_sinks.dart'; +import 'rules/combinators_ordering.dart'; import 'rules/comment_references.dart'; import 'rules/conditional_uri_does_not_exist.dart'; import 'rules/constant_identifier_names.dart'; @@ -271,6 +272,7 @@ void registerLintRules({bool inTestMode = false}) { ..register(CascadeInvocations()) ..register(CastNullableToNonNullable()) ..register(CloseSinks()) + ..register(CombinatorsOrdering()) ..register(CommentReferences()) ..register(ConditionalUriDoesNotExist()) ..register(ConstantIdentifierNames()) diff --git a/lib/src/rules/combinators_ordering.dart b/lib/src/rules/combinators_ordering.dart new file mode 100644 index 000000000..7e94ac841 --- /dev/null +++ b/lib/src/rules/combinators_ordering.dart @@ -0,0 +1,69 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:collection/collection.dart'; + +import '../analyzer.dart'; + +const _desc = r'Sort combinator names alphabetically.'; + +const _details = r''' + +**DO** sort combinator names alphabetically. + +**BAD:** +```dart +import 'a.dart' show B, A hide D, C; +export 'a.dart' show B, A hide D, C; +``` + +**GOOD:** +```dart +import 'a.dart' show A, B hide C, D; +export 'a.dart' show A, B hide C, D; +``` + +'''; + +class CombinatorsOrdering extends LintRule { + CombinatorsOrdering() + : super( + name: 'combinators_ordering', + description: _desc, + details: _details, + group: Group.style, + ); + + @override + void registerNodeProcessors( + NodeLintRegistry registry, + LinterContext context, + ) { + var visitor = _Visitor(this); + registry.addHideCombinator(this, visitor); + registry.addShowCombinator(this, visitor); + } +} + +class _Visitor extends SimpleAstVisitor { + _Visitor(this.rule); + + final LintRule rule; + + @override + void visitHideCombinator(HideCombinator node) { + if (!node.hiddenNames.map((e) => e.name).isSorted()) { + rule.reportLint(node); + } + } + + @override + void visitShowCombinator(ShowCombinator node) { + if (!node.shownNames.map((e) => e.name).isSorted()) { + rule.reportLint(node); + } + } +} diff --git a/test_data/rules/combinators_ordering.dart b/test_data/rules/combinators_ordering.dart new file mode 100644 index 000000000..afd6ee357 --- /dev/null +++ b/test_data/rules/combinators_ordering.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// test w/ `dart test -N combinators_ordering` + +import 'dart:math' as m1 show max, min; // OK +import 'dart:math' as m2 show min, max; // LINT + +export 'dart:math' show max, min; // OK +export 'dart:math' show min, max; // LINT + +import 'dart:math' as m3 hide max, min; // OK +import 'dart:math' as m4 hide min, max; // LINT + +export 'dart:math' hide max, min; // OK +export 'dart:math' hide min, max; // LINT