blob: 35c95f31227d34c76f767e6ca2c8b8fbe5766852 [file] [log] [blame]
// Copyright (c) 2024, 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 'dart:io';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart' as file_system;
import 'package:analyzer/file_system/physical_file_system.dart' as file_system;
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/source/source.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;
import 'analysis_error_info.dart';
import 'lint_driver.dart';
import 'linter_options.dart';
Source _createSource(Uri uri) {
var filePath = uri.toFilePath();
var file = file_system.PhysicalResourceProvider.INSTANCE.getFile(filePath);
return FileSource(file, uri);
}
/// Dart source linter, only for package:linter's tools and tests.
class TestLinter implements AnalysisErrorListener {
final errors = <Diagnostic>[];
final LinterOptions options;
final file_system.ResourceProvider _resourceProvider;
TestLinter(this.options, {file_system.ResourceProvider? resourceProvider})
: _resourceProvider =
resourceProvider ?? file_system.PhysicalResourceProvider.INSTANCE;
Future<List<DiagnosticInfo>> lintFiles(List<File> files) async {
var lintDriver = LintDriver(options, _resourceProvider);
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),
);
}
return errors;
}
@visibleForTesting
void lintPubspecSource({required String contents, String? sourcePath}) {
var sourceUrl = sourcePath == null ? null : path.toUri(sourcePath);
var spec = Pubspec.parse(contents, sourceUrl: sourceUrl);
for (var rule in options.enabledRules) {
var visitor = rule.pubspecVisitor;
if (visitor != null) {
// Analyzer sets reporters; if this file is not being analyzed,
// we need to set one ourselves. (Needless to say, when pubspec
// processing gets pushed down, this hack can go away.)
if (sourceUrl != null) {
var source = _createSource(sourceUrl);
rule.reporter = ErrorReporter(this, source);
}
try {
spec.accept(visitor);
} on Exception catch (_) {
// TODO(srawlins): Report the exception somewhere?
}
}
}
}
@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;
}