analyzer: Tidy up Spelunker API
Work towards https://github.com/dart-lang/sdk/issues/55660
Spelunker will move to the analyzer_testing package, so we must first
clean it up.
Spelunker had two subclasses, but their only differentiation was the
calculation of a getter, which could be a final field. So I remove the
subclasses, move the "File"-based one to the spelunk utility script,
and change the Spelunker constructor to take in a source string.
Additionally:
* Make all of the fields private.
* Remove isDartFileName and isPubspecFileName. These calculations can
be inlined into their call sites and make use of the `file_paths`
library.
Change-Id: I4b97ed03a2845440dabd3a9ab0aa3b3ba4af5959
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428083
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/lib/src/lint/io.dart b/pkg/analyzer/lib/src/lint/io.dart
index 6886b02..84ff615 100644
--- a/pkg/analyzer/lib/src/lint/io.dart
+++ b/pkg/analyzer/lib/src/lint/io.dart
@@ -4,21 +4,11 @@
import 'dart:io';
-import 'package:analyzer/src/lint/util.dart';
-import 'package:path/path.dart' as p;
-
/// A shared sink for standard error reporting.
StringSink errorSink = stderr;
/// A shared sink for standard out reporting.
StringSink outSink = stdout;
-/// Returns `true` if this [entry] is a Dart file.
-bool isDartFile(FileSystemEntity entry) => isDartFileName(entry.path);
-
-/// Returns `true` if this [entry] is a pubspec file.
-bool isPubspecFile(FileSystemEntity entry) =>
- isPubspecFileName(p.basename(entry.path));
-
/// Synchronously read the contents of the file at the given [path] as a string.
String readFile(String path) => File(path).readAsStringSync();
diff --git a/pkg/analyzer/lib/src/lint/util.dart b/pkg/analyzer/lib/src/lint/util.dart
index 0025ebe..ccef175 100644
--- a/pkg/analyzer/lib/src/lint/util.dart
+++ b/pkg/analyzer/lib/src/lint/util.dart
@@ -9,71 +9,20 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:path/path.dart' as path;
-final _pubspec = RegExp(r'^[_]?pubspec\.yaml$');
+final class Spelunker {
+ final StringSink _sink;
+ final FeatureSet _featureSet;
+ final String _source;
-/// Create a library name prefix based on [libraryPath], [projectRoot] and
-/// current [packageName].
-String createLibraryNamePrefix({
- required String libraryPath,
- String? projectRoot,
- String? packageName,
-}) {
- // Use the posix context to canonicalize separators (`\`).
- var libraryDirectory = path.posix.dirname(libraryPath);
- var relativePath = path.posix.relative(libraryDirectory, from: projectRoot);
- // Drop 'lib/'.
- var segments = path.split(relativePath);
- if (segments[0] == 'lib') {
- relativePath = path.posix.joinAll(segments.sublist(1));
- }
- // Replace separators.
- relativePath = relativePath.replaceAll('/', '.');
- // Add separator if needed.
- if (relativePath.isNotEmpty) {
- relativePath = '.$relativePath';
- }
-
- return '$packageName$relativePath';
-}
-
-/// Returns `true` if this [fileName] is a Dart file.
-bool isDartFileName(String fileName) => fileName.endsWith('.dart');
-
-/// Returns `true` if this [fileName] is a Pubspec file.
-bool isPubspecFileName(String fileName) => _pubspec.hasMatch(fileName);
-
-class FileSpelunker extends _AbstractSpelunker {
- final String path;
- FileSpelunker(this.path, {super.sink, super.featureSet});
- @override
- String getSource() => File(path).readAsStringSync();
-}
-
-class StringSpelunker extends _AbstractSpelunker {
- final String source;
- StringSpelunker(this.source, {super.sink, super.featureSet});
- @override
- String getSource() => source;
-}
-
-abstract class _AbstractSpelunker {
- final StringSink sink;
- FeatureSet featureSet;
-
- _AbstractSpelunker({StringSink? sink, FeatureSet? featureSet})
- : sink = sink ?? stdout,
- featureSet = featureSet ?? FeatureSet.latestLanguageVersion();
-
- String getSource();
+ Spelunker(this._source, {StringSink? sink, FeatureSet? featureSet})
+ : _sink = sink ?? stdout,
+ _featureSet = featureSet ?? FeatureSet.latestLanguageVersion();
void spelunk() {
- var contents = getSource();
+ var parseResult = parseString(content: _source, featureSet: _featureSet);
- var parseResult = parseString(content: contents, featureSet: featureSet);
-
- var visitor = _SourceVisitor(sink);
+ var visitor = _SourceVisitor(_sink);
parseResult.unit.accept(visitor);
}
}
@@ -85,8 +34,7 @@
_SourceVisitor(this.sink);
- String asString(AstNode node) =>
- '${typeInfo(node.runtimeType)} [${node.toString()}]';
+ String asString(AstNode node) => '${typeInfo(node.runtimeType)} [$node]';
List<CommentToken> getPrecedingComments(Token token) {
var comments = <CommentToken>[];
diff --git a/pkg/linter/lib/src/test_utilities/test_linter.dart b/pkg/linter/lib/src/test_utilities/test_linter.dart
index 8c453a6..8be4f5c 100644
--- a/pkg/linter/lib/src/test_utilities/test_linter.dart
+++ b/pkg/linter/lib/src/test_utilities/test_linter.dart
@@ -11,9 +11,9 @@
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/source/source.dart';
// ignore: implementation_imports
-import 'package:analyzer/src/lint/io.dart';
-// ignore: implementation_imports
import 'package:analyzer/src/lint/pub.dart';
+// ignore: implementation_imports
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
@@ -39,10 +39,11 @@
resourceProvider ?? file_system.PhysicalResourceProvider.INSTANCE;
Future<List<DiagnosticInfo>> lintFiles(List<File> files) async {
- var errors = <DiagnosticInfo>[];
var lintDriver = LintDriver(options, _resourceProvider);
- errors.addAll(await lintDriver.analyze(files.where(isDartFile)));
- for (var file in files.where(isPubspecFile)) {
+ var errors = await lintDriver.analyze(
+ files.where((f) => f.path.endsWith('.dart')),
+ );
+ for (var file in files.where(_isPubspecFile)) {
lintPubspecSource(
contents: file.readAsStringSync(),
sourcePath: _resourceProvider.pathContext.normalize(file.absolute.path),
@@ -77,4 +78,8 @@
@override
void onError(Diagnostic error) => errors.add(error);
+
+ /// Returns whether this [entry] is a pubspec file.
+ bool _isPubspecFile(FileSystemEntity entry) =>
+ path.basename(entry.path) == file_paths.pubspecYaml;
}
diff --git a/pkg/linter/test/rule_test_support.dart b/pkg/linter/test/rule_test_support.dart
index 0e818de..09e1ba4 100644
--- a/pkg/linter/test/rule_test_support.dart
+++ b/pkg/linter/test/rule_test_support.dart
@@ -446,7 +446,7 @@
try {
var astSink = StringBuffer();
- StringSpelunker(
+ Spelunker(
result.unit.toSource(),
sink: astSink,
featureSet: result.unit.featureSet,
diff --git a/pkg/linter/tool/benchmark.dart b/pkg/linter/tool/benchmark.dart
index b18f886..b9bf93f 100644
--- a/pkg/linter/tool/benchmark.dart
+++ b/pkg/linter/tool/benchmark.dart
@@ -9,7 +9,7 @@
import 'package:analyzer/src/lint/config.dart';
import 'package:analyzer/src/lint/io.dart';
import 'package:analyzer/src/lint/registry.dart';
-import 'package:analyzer/src/lint/util.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:args/args.dart';
import 'package:linter/src/analyzer.dart';
import 'package:linter/src/extensions.dart';
@@ -266,7 +266,7 @@
/// Whether this path is a Dart file or a Pubspec file.
bool get isLintable =>
- isDartFileName(this) || isPubspecFileName(path.basename(this));
+ endsWith('.dart') || path.basename(this) == file_paths.pubspecYaml;
}
extension on StringSink {
diff --git a/pkg/linter/tool/spelunk.dart b/pkg/linter/tool/spelunk.dart
index 0d20bd0..6b6ff15 100644
--- a/pkg/linter/tool/spelunk.dart
+++ b/pkg/linter/tool/spelunk.dart
@@ -2,7 +2,9 @@
// 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/src/lint/util.dart' show FileSpelunker;
+import 'dart:io';
+
+import 'package:analyzer/src/lint/util.dart' show Spelunker;
import 'package:args/args.dart';
/// AST Spelunker
@@ -11,6 +13,7 @@
var options = parser.parse(args);
for (var path in options.rest) {
- FileSpelunker(path).spelunk();
+ var source = File(path).readAsStringSync();
+ Spelunker(source).spelunk();
}
}