linter: support doc-directives in directives_ordering
Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try
Change-Id: Ieb3b072a3fbd8d56751c29a39c979807f07cd2c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/376821
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/linter/lib/src/rules/directives_ordering.dart b/pkg/linter/lib/src/rules/directives_ordering.dart
index 9ee40fd..c544430 100644
--- a/pkg/linter/lib/src/rules/directives_ordering.dart
+++ b/pkg/linter/lib/src/rules/directives_ordering.dart
@@ -108,13 +108,15 @@
```
''';
+const _docImportKeyword = '@docImport';
+
const _exportKeyword = 'export';
const _importKeyword = 'import';
-/// Compares directives by package then file in package.
+/// Compares directives by package name, then file name in the package.
///
-/// Package is everything until the first `/`.
+/// The package name is everything until the first '/'.
int compareDirectives(String a, String b) {
if (!a.startsWith('package:') || !b.startsWith('package:')) {
if (!a.startsWith('/') && !b.startsWith('/')) {
@@ -217,23 +219,6 @@
}
}
-// ignore: unused_element
-class _PackageBox {
- final String _packageName;
-
- _PackageBox(this._packageName);
-
- // ignore: unused_element
- bool _isNotOwnPackageDirective(NamespaceDirective node) =>
- _isPackageDirective(node) && !_isOwnPackageDirective(node);
-
- bool _isOwnPackageDirective(NamespaceDirective node) {
- var uriContent = node.uri.stringValue;
- return uriContent != null &&
- uriContent.startsWith('package:$_packageName/');
- }
-}
-
class _Visitor extends SimpleAstVisitor<void> {
final DirectivesOrdering rule;
@@ -250,45 +235,45 @@
void _checkDartDirectiveGoFirst(
Set<AstNode> lintedNodes, CompilationUnit node) {
- void reportImport(NamespaceDirective directive) {
- if (lintedNodes.add(directive)) {
- rule._reportLintWithDartDirectiveGoFirstMessage(
- directive, _importKeyword);
+ for (var import in node.importDirectives.withDartUrisSkippingTheFirstSet) {
+ if (lintedNodes.add(import)) {
+ rule._reportLintWithDartDirectiveGoFirstMessage(import, _importKeyword);
}
}
- void reportExport(NamespaceDirective directive) {
- if (lintedNodes.add(directive)) {
- rule._reportLintWithDartDirectiveGoFirstMessage(
- directive, _exportKeyword);
+ for (var export in node.exportDirectives.withDartUrisSkippingTheFirstSet) {
+ if (lintedNodes.add(export)) {
+ rule._reportLintWithDartDirectiveGoFirstMessage(export, _exportKeyword);
}
}
- Iterable<NamespaceDirective> getNodesToLint(
- Iterable<NamespaceDirective> directives) =>
- directives.skipWhile(_isDartDirective).where(_isDartDirective);
-
- getNodesToLint(_getImportDirectives(node)).forEach(reportImport);
-
- getNodesToLint(_getExportDirectives(node)).forEach(reportExport);
+ for (var import
+ in node.docImportDirectives.withDartUrisSkippingTheFirstSet) {
+ if (lintedNodes.add(import)) {
+ rule._reportLintWithDartDirectiveGoFirstMessage(
+ import, _docImportKeyword);
+ }
+ }
}
void _checkDirectiveSectionOrderedAlphabetically(
Set<AstNode> lintedNodes, CompilationUnit node) {
- var importDirectives = _getImportDirectives(node);
- var exportDirectives = _getExportDirectives(node);
-
- var dartImports = importDirectives.where(_isDartDirective);
- var dartExports = exportDirectives.where(_isDartDirective);
-
- var relativeImports = importDirectives.where(_isRelativeDirective);
- var relativeExports = exportDirectives.where(_isRelativeDirective);
+ var dartImports = node.importDirectives.where(_isDartDirective);
+ var dartExports = node.exportDirectives.where(_isDartDirective);
+ var dartDocImports = node.docImportDirectives.where(_isDartDirective);
_checkSectionInOrder(lintedNodes, dartImports);
_checkSectionInOrder(lintedNodes, dartExports);
+ _checkSectionInOrder(lintedNodes, dartDocImports);
+
+ var relativeImports = node.importDirectives.where(_isRelativeDirective);
+ var relativeExports = node.exportDirectives.where(_isRelativeDirective);
+ var relativeDocImports =
+ node.docImportDirectives.where(_isRelativeDirective);
_checkSectionInOrder(lintedNodes, relativeImports);
_checkSectionInOrder(lintedNodes, relativeExports);
+ _checkSectionInOrder(lintedNodes, relativeDocImports);
// See: https://github.com/dart-lang/linter/issues/3395
// (`DartProject` removal)
@@ -297,11 +282,13 @@
// will have ecosystem impact.
// Not a pub package. Package directives should be sorted in one block.
- var packageImports = importDirectives.where(_isPackageDirective);
- var packageExports = exportDirectives.where(_isPackageDirective);
+ var packageImports = node.importDirectives.where(_isPackageDirective);
+ var packageExports = node.exportDirectives.where(_isPackageDirective);
+ var packageDocImports = node.docImportDirectives.where(_isPackageDirective);
_checkSectionInOrder(lintedNodes, packageImports);
_checkSectionInOrder(lintedNodes, packageExports);
+ _checkSectionInOrder(lintedNodes, packageDocImports);
// The following is relying on projectName which is meant to come from
// a `DartProject` instance (but was not since the project was always null)
@@ -328,75 +315,90 @@
void _checkExportDirectiveAfterImportDirective(
Set<AstNode> lintedNodes, CompilationUnit node) {
- void reportDirective(Directive directive) {
+ for (var directive in node.directives.reversed
+ .skipWhile(_isPartDirective)
+ .skipWhile(_isExportDirective)
+ .where(_isExportDirective)) {
if (lintedNodes.add(directive)) {
rule._reportLintWithExportDirectiveAfterImportDirectiveMessage(
directive);
}
}
-
- node.directives.reversed
- .skipWhile(_isPartDirective)
- .skipWhile(_isExportDirective)
- .where(_isExportDirective)
- .forEach(reportDirective);
}
void _checkPackageDirectiveBeforeRelative(
Set<AstNode> lintedNodes, CompilationUnit node) {
- void reportImport(NamespaceDirective directive) {
- if (lintedNodes.add(directive)) {
+ for (var import
+ in node.importDirectives.withPackageUrisSkippingAbsoluteUris) {
+ if (lintedNodes.add(import)) {
rule._reportLintWithPackageDirectiveBeforeRelativeMessage(
- directive, _importKeyword);
+ import, _importKeyword);
}
}
- void reportExport(NamespaceDirective directive) {
- if (lintedNodes.add(directive)) {
+ for (var export
+ in node.exportDirectives.withPackageUrisSkippingAbsoluteUris) {
+ if (lintedNodes.add(export)) {
rule._reportLintWithPackageDirectiveBeforeRelativeMessage(
- directive, _exportKeyword);
+ export, _exportKeyword);
}
}
- Iterable<NamespaceDirective> getNodesToLint(
- Iterable<NamespaceDirective> directives) =>
- directives
- .where(_isNotDartDirective)
- .skipWhile(_isAbsoluteDirective)
- .where(_isPackageDirective);
-
- getNodesToLint(_getImportDirectives(node)).forEach(reportImport);
-
- getNodesToLint(_getExportDirectives(node)).forEach(reportExport);
+ for (var import
+ in node.docImportDirectives.withPackageUrisSkippingAbsoluteUris) {
+ if (lintedNodes.add(import)) {
+ rule._reportLintWithPackageDirectiveBeforeRelativeMessage(
+ import, _docImportKeyword);
+ }
+ }
}
void _checkSectionInOrder(
Set<AstNode> lintedNodes, Iterable<NamespaceDirective> nodes) {
- void reportDirective(NamespaceDirective directive) {
- if (lintedNodes.add(directive)) {
- rule._reportLintWithDirectiveSectionOrderedAlphabeticallyMessage(
- directive);
- }
- }
+ if (nodes.isEmpty) return;
- NamespaceDirective? previousDirective;
- for (var directive in nodes) {
- if (previousDirective != null) {
- var previousUri = previousDirective.uri.stringValue;
- var directiveUri = directive.uri.stringValue;
- if (previousUri != null &&
- directiveUri != null &&
- compareDirectives(previousUri, directiveUri) > 0) {
- reportDirective(directive);
+ var previousUri = nodes.first.uri.stringValue;
+ for (var directive in nodes.skip(1)) {
+ var directiveUri = directive.uri.stringValue;
+ if (previousUri != null &&
+ directiveUri != null &&
+ compareDirectives(previousUri, directiveUri) > 0) {
+ if (lintedNodes.add(directive)) {
+ rule._reportLintWithDirectiveSectionOrderedAlphabeticallyMessage(
+ directive);
}
}
- previousDirective = directive;
+ previousUri = directive.uri.stringValue;
}
}
+}
- Iterable<ExportDirective> _getExportDirectives(CompilationUnit node) =>
- node.directives.whereType<ExportDirective>();
+extension on CompilationUnit {
+ Iterable<ImportDirective> get docImportDirectives {
+ var libraryDirective = directives.whereType<LibraryDirective>().firstOrNull;
+ if (libraryDirective == null) return const [];
+ var docComment = libraryDirective.documentationComment;
+ if (docComment == null) return const [];
+ return docComment.docImports.map((e) => e.import);
+ }
- Iterable<ImportDirective> _getImportDirectives(CompilationUnit node) =>
- node.directives.whereType<ImportDirective>();
+ Iterable<ExportDirective> get exportDirectives =>
+ directives.whereType<ExportDirective>();
+
+ Iterable<ImportDirective> get importDirectives =>
+ directives.whereType<ImportDirective>();
+}
+
+extension on Iterable<NamespaceDirective> {
+ /// The directives with 'dart:' URIs, skipping the first such set of
+ /// directives.
+ Iterable<NamespaceDirective> get withDartUrisSkippingTheFirstSet =>
+ skipWhile(_isDartDirective).where(_isDartDirective);
+
+ /// The directives with 'package:' URIs, after the first set of directives
+ /// with absolute URIs.
+ Iterable<NamespaceDirective> get withPackageUrisSkippingAbsoluteUris =>
+ where(_isNotDartDirective)
+ .skipWhile(_isAbsoluteDirective)
+ .where(_isPackageDirective);
}
diff --git a/pkg/linter/test/rules/directives_ordering_test.dart b/pkg/linter/test/rules/directives_ordering_test.dart
index 26ef477..aefad87 100644
--- a/pkg/linter/test/rules/directives_ordering_test.dart
+++ b/pkg/linter/test/rules/directives_ordering_test.dart
@@ -26,6 +26,20 @@
@override
String get lintRule => 'directives_ordering';
+ test_dartDirectivesGoFirst_docImports() async {
+ newFile('$testPackageLibPath/a.dart', '');
+ await assertDiagnostics(r'''
+/// @docImport 'dart:math';
+/// @docImport 'a.dart';
+/// @docImport 'dart:html';
+/// @docImport 'dart:isolate';
+library;
+''', [
+ lint(61, 19),
+ lint(89, 22),
+ ]);
+ }
+
test_dartDirectivesGoFirst_exports() async {
newFile('$testPackageLibPath/a.dart', '');
await assertDiagnostics(r'''
@@ -117,6 +131,21 @@
''');
}
+ test_packageDirectivesGoBeforeRelative_docImports() async {
+ newFile('$testPackageLibPath/a.dart', '');
+ newFile('$testPackageLibPath/b.dart', '');
+ await assertDiagnostics(r'''
+/// @docImport 'dart:math';
+/// @docImport 'package:js/js.dart';
+/// @docImport 'a.dart';
+/// @docImport 'package:meta/meta.dart';
+/// @docImport 'b.dart';
+library;
+''', [
+ lint(98, 32),
+ ]);
+ }
+
test_packageDirectivesGoBeforeRelative_exports() async {
newFile('$testPackageLibPath/a.dart', '');
newFile('$testPackageLibPath/b.dart', '');
@@ -179,6 +208,18 @@
]);
}
+ test_sortDirectiveSectionsAlphabetically_dartSchema_docImport() async {
+ await assertDiagnostics(r'''
+/// @docImport 'dart:html';
+/// @docImport 'dart:isolate';
+/// @docImport 'dart:convert';
+/// @docImport 'dart:math';
+library;
+''', [
+ lint(67, 22),
+ ]);
+ }
+
test_sortDirectiveSectionsAlphabetically_dartSchema_export() async {
await assertDiagnostics(r'''
export 'dart:isolate';