Analysis re-work to use analyzer APIs.

Introduces a new Dart analysis wrapper that works directly with the analyzer API (in favor of shelling out to a separate process).

Some consequences:

  * we no longer need to fear parts (simplifying our dart file gathering)
  * we can filter by error code (when needed), rather than by error strings
  * no more IO scraping
  * no need to generate `main()` or to run with `--package-warnings`
  * we now specify an analyzer (and linter) version in the pubspec (we’ll want to make sure this doesn’t diverge too far from the analyzer shipped with the SDK but it does give us some room to play with experimental builds)
  * no more (re)scanning of error source files (and so no more source cache)
  * should generally be a bit simpler and easier to maintain
  * runs a bit faster :)
This commit is contained in:
pq 2016-05-05 12:08:58 -07:00
parent 2fbf11a77b
commit a59a713f75
4 changed files with 357 additions and 285 deletions

View file

@ -10,7 +10,7 @@ dependencies:
# We don't actually depend on 'analyzer', but 'test' does. We pin the
# version of analyzer we depend on to avoid version skew across our
# packages.
analyzer: 0.27.2
analyzer: 0.27.4-alpha.1
flutter:
path: ../flutter

View file

@ -11,101 +11,19 @@ import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart' as yaml;
import '../artifacts.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../base/utils.dart';
import '../build_configuration.dart';
import '../dart/analysis.dart';
import '../dart/sdk.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
bool isDartFile(FileSystemEntity entry) => entry is File && entry.path.endsWith('.dart');
bool isDartTestFile(FileSystemEntity entry) => entry is File && entry.path.endsWith('_test.dart');
bool isDartBenchmarkFile(FileSystemEntity entry) => entry is File && entry.path.endsWith('_bench.dart');
RegExp _testFileParser = new RegExp(r'^(.+)_test(\.dart)$');
void _addDriverTest(FileSystemEntity entry, List<String> dartFiles) {
if (isDartTestFile(entry)) {
final String testFileName = entry.path;
dartFiles.add(testFileName);
Match groups = _testFileParser.firstMatch(testFileName);
assert(groups.groupCount == 2);
final String hostFileName = '${groups[1]}${groups[2]}';
File hostFile = new File(hostFileName);
if (hostFile.existsSync()) {
assert(isDartFile(hostFile));
dartFiles.add(hostFileName);
}
}
}
void _addPackage(String directoryPath, List<String> dartFiles, Set<String> pubSpecDirectories) {
final int originalDartFilesCount = dartFiles.length;
// .../directoryPath/*/bin/*.dart
// .../directoryPath/*/lib/main.dart
// .../directoryPath/*/test/*_test.dart
// .../directoryPath/*/test/*/*_test.dart
// .../directoryPath/*/benchmark/*/*_bench.dart
Directory binDirectory = new Directory(path.join(directoryPath, 'bin'));
if (binDirectory.existsSync()) {
for (FileSystemEntity subentry in binDirectory.listSync()) {
if (isDartFile(subentry))
dartFiles.add(subentry.path);
}
}
String mainPath = path.join(directoryPath, 'lib', 'main.dart');
if (FileSystemEntity.isFileSync(mainPath))
dartFiles.add(mainPath);
Directory testDirectory = new Directory(path.join(directoryPath, 'test'));
if (testDirectory.existsSync()) {
for (FileSystemEntity entry in testDirectory.listSync()) {
if (entry is Directory) {
for (FileSystemEntity subentry in entry.listSync()) {
if (isDartTestFile(subentry))
dartFiles.add(subentry.path);
}
} else if (isDartTestFile(entry)) {
dartFiles.add(entry.path);
}
}
}
Directory testDriverDirectory = new Directory(path.join(directoryPath, 'test_driver'));
if (testDriverDirectory.existsSync()) {
for (FileSystemEntity entry in testDriverDirectory.listSync()) {
if (entry is Directory) {
for (FileSystemEntity subentry in entry.listSync())
_addDriverTest(subentry, dartFiles);
} else if (isDartTestFile(entry)) {
_addDriverTest(entry, dartFiles);
}
}
}
Directory benchmarkDirectory = new Directory(path.join(directoryPath, 'benchmark'));
if (benchmarkDirectory.existsSync()) {
for (FileSystemEntity entry in benchmarkDirectory.listSync()) {
if (entry is Directory) {
for (FileSystemEntity subentry in entry.listSync()) {
if (isDartBenchmarkFile(subentry))
dartFiles.add(subentry.path);
}
} else if (isDartBenchmarkFile(entry)) {
dartFiles.add(entry.path);
}
}
}
if (originalDartFilesCount != dartFiles.length)
pubSpecDirectories.add(directoryPath);
}
class FileChanged { }
typedef bool FileFilter(FileSystemEntity entity);
class AnalyzeCommand extends FlutterCommand {
AnalyzeCommand() {
@ -179,16 +97,17 @@ class AnalyzeCommand extends FlutterCommand {
Future<int> _analyzeOnce() async {
Stopwatch stopwatch = new Stopwatch()..start();
Set<String> pubSpecDirectories = new HashSet<String>();
List<String> dartFiles = argResults.rest.toList();
Set<Directory> pubSpecDirectories = new HashSet<Directory>();
List<File> dartFiles = <File>[];
for (String file in dartFiles) {
for (String file in argResults.rest.toList()) {
file = path.normalize(path.absolute(file));
String root = path.rootPrefix(file);
dartFiles.add(new File(file));
while (file != root) {
file = path.dirname(file);
if (FileSystemEntity.isFileSync(path.join(file, 'pubspec.yaml'))) {
pubSpecDirectories.add(file);
pubSpecDirectories.add(new Directory(file));
break;
}
}
@ -196,77 +115,41 @@ class AnalyzeCommand extends FlutterCommand {
if (argResults['current-directory']) {
// ./*.dart
Directory currentDirectory = new Directory('.');
Directory cwd = new Directory('.');
bool foundOne = false;
for (FileSystemEntity entry in currentDirectory.listSync()) {
for (FileSystemEntity entry in cwd.listSync()) {
if (isDartFile(entry)) {
dartFiles.add(entry.path);
dartFiles.add(entry);
foundOne = true;
}
}
if (foundOne)
pubSpecDirectories.add('.');
if (foundOne) {
pubSpecDirectories.add(cwd);
}
}
if (argResults['current-package'])
_addPackage('.', dartFiles, pubSpecDirectories);
if (argResults['current-package']) {
// **/.*dart
Directory cwd = new Directory('.');
_collectDartFiles(cwd, dartFiles);
pubSpecDirectories.add(cwd);
}
//TODO(ianh): Fix the intl package resource generator
//TODO: extract this regexp from the exclude in options.
RegExp stockExampleFiles = new RegExp('examples/stocks/lib/.*\.dart\$');
if (argResults['flutter-repo']) {
//examples/*/ as package
//examples/layers/*/ as files
//dev/manual_tests/*/ as package
//dev/manual_tests/*/ as files
for (Directory dir in runner.getRepoPackages())
_addPackage(dir.path, dartFiles, pubSpecDirectories);
Directory subdirectory;
subdirectory = new Directory(path.join(ArtifactStore.flutterRoot, 'examples', 'layers'));
if (subdirectory.existsSync()) {
bool foundOne = false;
for (FileSystemEntity entry in subdirectory.listSync()) {
if (entry is Directory) {
for (FileSystemEntity subentry in entry.listSync()) {
if (isDartFile(subentry)) {
dartFiles.add(subentry.path);
foundOne = true;
}
}
}
}
if (foundOne)
pubSpecDirectories.add(subdirectory.path);
}
subdirectory = new Directory(path.join(ArtifactStore.flutterRoot, 'dev', 'manual_tests'));
if (subdirectory.existsSync()) {
bool foundOne = false;
for (FileSystemEntity entry in subdirectory.listSync()) {
if (entry is Directory) {
_addPackage(entry.path, dartFiles, pubSpecDirectories);
} else if (isDartFile(entry)) {
dartFiles.add(entry.path);
foundOne = true;
}
}
if (foundOne)
pubSpecDirectories.add(subdirectory.path);
for (Directory dir in runner.getRepoPackages()) {
_collectDartFiles(dir, dartFiles,
exclude: (FileSystemEntity entity) => stockExampleFiles.hasMatch(entity.path));
pubSpecDirectories.add(dir);
}
}
dartFiles = dartFiles.map((String directory) => path.normalize(path.absolute(directory))).toSet().toList();
dartFiles.sort();
// prepare a Dart file that references all the above Dart files
StringBuffer mainBody = new StringBuffer();
for (int index = 0; index < dartFiles.length; index += 1)
mainBody.writeln('import \'${dartFiles[index]}\' as file$index;');
mainBody.writeln('void main() { }');
// determine what all the various .packages files depend on
PackageDependencyTracker dependencies = new PackageDependencyTracker();
for (Directory directory in pubSpecDirectories.map((String path) => new Directory(path))) {
for (Directory directory in pubSpecDirectories) {
String pubSpecYamlPath = path.join(directory.path, 'pubspec.yaml');
File pubSpecYamlFile = new File(pubSpecYamlPath);
if (pubSpecYamlFile.existsSync()) {
@ -316,150 +199,66 @@ class AnalyzeCommand extends FlutterCommand {
for (String package in packages.keys)
packagesBody.writeln('$package:${path.toUri(packages[package])}');
// save the Dart file and the .packages file to disk
// save the .packages file to disk
//TODO(pq): consider passing package info via a data URI
Directory host = Directory.systemTemp.createTempSync('flutter-analyze-');
File mainFile = new File(path.join(host.path, 'main.dart'))..writeAsStringSync(mainBody.toString());
File optionsFile = new File(path.join(ArtifactStore.flutterRoot, 'packages', 'flutter_tools', 'flutter_analysis_options'));
File packagesFile = new File(path.join(host.path, '.packages'))..writeAsStringSync(packagesBody.toString());
String packagesFilePath = path.join(host.path, '.packages');
new File(packagesFilePath).writeAsStringSync(packagesBody.toString());
List<String> cmd = <String>[
sdkBinaryName('dartanalyzer', sdkLocation: argResults['dart-sdk']),
// do not set '--warnings', since that will include the entire Dart SDK
'--ignore-unrecognized-flags',
'--enable_type_checks',
'--package-warnings',
'--fatal-warnings',
'--fatal-hints',
// defines lints
'--options', optionsFile.path,
'--packages', packagesFile.path,
mainFile.path
];
Status status;
if (argResults['preamble']) {
if (dartFiles.length == 1) {
status = logger.startProgress('Analyzing ${path.relative(dartFiles.first)}...');
logger.printStatus('Analyzing ${path.relative(dartFiles.first.path)}...');
} else {
status = logger.startProgress('Analyzing ${dartFiles.length} entry points...');
logger.printStatus('Analyzing ${dartFiles.length} files...');
}
for (String file in dartFiles)
printTrace(file);
}
DriverOptions options = new DriverOptions();
options.dartSdkPath = argResults['dart-sdk'];
options.packageConfigPath = packagesFilePath;
options.analysisOptionsFile = path.join(ArtifactStore.flutterRoot, 'packages', 'flutter_tools', 'flutter_analysis_options');
AnalysisDriver analyzer = new AnalysisDriver(options);
//TODO:(pq): consider error handling
List<AnalysisErrorDescription> errors = analyzer.analyze(dartFiles);
printTrace(cmd.join(' '));
Process process = await Process.start(
cmd[0],
cmd.sublist(1),
workingDirectory: host.path
);
int errorCount = 0;
StringBuffer output = new StringBuffer();
process.stdout.transform(UTF8.decoder).listen((String data) {
output.write(data);
});
process.stderr.transform(UTF8.decoder).listen((String data) {
// dartanalyzer doesn't seem to ever output anything on stderr
errorCount += 1;
printError(data);
});
int exitCode = await process.exitCode;
status?.stop(showElapsedTime: true);
List<Pattern> patternsToSkip = <Pattern>[
'Analyzing [${mainFile.path}]...',
new RegExp('^\\[(hint|error)\\] Unused import \\(${mainFile.path},'),
new RegExp(r'^\[.+\] .+ \(.+/\.pub-cache/.+'),
new RegExp(r'[0-9]+ (error|warning|hint|lint).+found\.'),
new RegExp(r'^$'),
];
RegExp generalPattern = new RegExp(r'^\[(error|warning|hint|lint)\] (.+) \(([^(),]+), line ([0-9]+), col ([0-9]+)\)$');
RegExp conflictingNamesPattern = new RegExp('^The imported libraries \'([^\']+)\' and \'([^\']+)\' cannot have the same name \'([^\']+)\'\$');
RegExp missingFilePattern = new RegExp('^Target of URI does not exist: \'([^\')]+)\'\$');
RegExp documentAllMembersPattern = new RegExp('^Document all public members\$');
Set<String> changedFiles = new Set<String>(); // files about which we've complained that they changed
_SourceCache cache = new _SourceCache(10);
int membersMissingDocumentation = 0;
List<String> errorLines = output.toString().split('\n');
for (String errorLine in errorLines) {
if (patternsToSkip.every((Pattern pattern) => pattern.allMatches(errorLine).isEmpty)) {
Match groups = generalPattern.firstMatch(errorLine);
if (groups != null) {
String level = groups[1];
String filename = groups[3];
String errorMessage = groups[2];
int lineNumber = int.parse(groups[4]);
int colNumber = int.parse(groups[5]);
try {
File source = new File(filename);
List<String> sourceLines = cache.getSourceFor(source);
if (lineNumber > sourceLines.length)
throw new FileChanged();
String sourceLine = sourceLines[lineNumber-1];
if (colNumber > sourceLine.length)
throw new FileChanged();
bool shouldIgnore = false;
if (documentAllMembersPattern.firstMatch(errorMessage) != null) {
// https://github.com/dart-lang/linter/issues/207
// https://github.com/dart-lang/linter/issues/208
if (isFlutterLibrary(filename)) {
if (!argResults['dartdocs']) {
membersMissingDocumentation += 1;
shouldIgnore = true;
}
} else {
shouldIgnore = true;
}
} else if (filename == mainFile.path) {
Match libs = conflictingNamesPattern.firstMatch(errorMessage);
Match missing = missingFilePattern.firstMatch(errorMessage);
if (libs != null) {
errorLine = '[$level] $errorMessage (${dartFiles[lineNumber-1]})'; // strip the reference to the generated main.dart
} else if (missing != null) {
errorLine = '[$level] File does not exist (${missing[1]})';
} else {
errorLine += ' (Please file a bug on the "flutter analyze" command saying that you saw this message.)';
}
} else if (filename.endsWith('.mojom.dart')) {
// autogenerated code - TODO(ianh): Fix the Dart mojom compiler
shouldIgnore = true;
} else if (sourceLines.first.startsWith('// DO NOT EDIT. This is code generated')) {
// autogenerated code - TODO(ianh): Fix the intl package resource generator
shouldIgnore = true;
}
if (shouldIgnore)
continue;
} on FileSystemException catch (exception) {
if (changedFiles.add(filename))
printError('[warning] Could not read file (${exception.message}${ exception.osError != null ? "; ${exception.osError}" : ""}) ($filename)');
} on FileChanged {
if (changedFiles.add(filename))
printError('[warning] File shrank during analysis ($filename)');
for (AnalysisErrorDescription error in errors) {
bool shouldIgnore = false;
if (error.errorCode.name == 'public_member_api_docs') {
// https://github.com/dart-lang/linter/issues/207
// https://github.com/dart-lang/linter/issues/208
if (isFlutterLibrary(error.source.fullName)) {
if (!argResults['dartdocs']) {
membersMissingDocumentation += 1;
shouldIgnore = true;
}
} else {
shouldIgnore = true;
}
printError(errorLine);
errorCount += 1;
}
//TODO(ianh): Fix the Dart mojom compiler
if (error.source.fullName.endsWith('.mojom.dart'))
shouldIgnore = true;
if (shouldIgnore)
continue;
printError(error.asString());
errorCount += 1;
}
stopwatch.stop();
String elapsed = (stopwatch.elapsedMilliseconds / 1000.0).toStringAsFixed(1);
host.deleteSync(recursive: true);
if (exitCode < 0 || exitCode > 3) // analyzer exit codes: 0 = nothing, 1 = hints, 2 = warnings, 3 = errors
return exitCode;
if (_isBenchmarking)
_writeBenchmark(stopwatch, errorCount);
if (errorCount > 0) {
if (membersMissingDocumentation > 0 && argResults['flutter-repo'])
printError('[lint] $membersMissingDocumentation public ${ membersMissingDocumentation == 1 ? "member lacks" : "members lack" } documentation');
printError('[lint] $membersMissingDocumentation public ${ membersMissingDocumentation == 1 ? "member lacks" : "members lack" } documentation (ran in ${elapsed}s)');
else
print('(Ran in ${elapsed}s)');
return 1; // we consider any level of error to be an error exit (we don't report different levels)
}
if (argResults['congratulate']) {
@ -472,6 +271,20 @@ class AnalyzeCommand extends FlutterCommand {
return 0;
}
List<File> _collectDartFiles(Directory dir, List<File> collected, {FileFilter exclude}) {
for (FileSystemEntity entity in dir.listSync(recursive: false, followLinks: false)) {
if (isDartFile(entity) && (exclude == null || !exclude(entity))) {
collected.add(entity);
}
if (entity is Directory) {
String name = path.basename(entity.path);
if (!name.startsWith('.') && name != 'packages')
_collectDartFiles(entity, collected, exclude: exclude);
}
}
return collected;
}
Future<int> _analyzeWatch() async {
List<String> directories;
@ -843,20 +656,3 @@ class AnalysisError implements Comparable<AnalysisError> {
return '${severity.toLowerCase().padLeft(7)}$message$relativePath:$startLine:$startColumn';
}
}
class _SourceCache {
_SourceCache(this.cacheSize);
final int cacheSize;
final Map<String, List<String>> _lines = new LinkedHashMap<String, List<String>>();
List<String> getSourceFor(File file) {
if (!_lines.containsKey(file.path)) {
if (_lines.length >= cacheSize)
_lines.remove(_lines.keys.first);
_lines[file.path] = file.readAsLinesSync();
}
return _lines[file.path];
}
}

View file

@ -0,0 +1,275 @@
// Copyright 2016 The Chromium Authors. 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:collection';
import 'dart:io';
import 'package:analyzer/file_system/file_system.dart' as file_system;
import 'package:analyzer/file_system/file_system.dart' show Folder;
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/plugin/options.dart';
import 'package:analyzer/source/analysis_options_provider.dart';
import 'package:analyzer/source/embedder.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/source/pub_package_map_provider.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/java_io.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/sdk_io.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:cli_util/cli_util.dart' as cli_util;
import 'package:linter/src/plugin/linter_plugin.dart';
import 'package:package_config/packages.dart' show Packages;
import 'package:package_config/packages_file.dart' as pkgfile show parse;
import 'package:package_config/src/packages_impl.dart' show MapPackages;
import 'package:path/path.dart' as path;
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
class AnalysisDriver {
Set<Source> _analyzedSources = new HashSet<Source>();
AnalysisOptionsProvider analysisOptionsProvider =
new AnalysisOptionsProvider();
AnalysisContext context;
DriverOptions options;
AnalysisDriver(this.options) {
AnalysisEngine.instance.logger =
new _StdLogger(outSink: options.outSink, errorSink: options.errorSink);
_processPlugins();
}
String get sdkDir => options.dartSdkPath ?? cli_util.getSdkDir().path;
List<AnalysisErrorDescription> analyze(Iterable<File> files) {
List<AnalysisErrorInfo> infos = _analyze(files);
List<AnalysisErrorDescription> errors = <AnalysisErrorDescription>[];
for (AnalysisErrorInfo info in infos) {
for (AnalysisError error in info.errors) {
if (!_isFiltered(error)) {
errors.add(new AnalysisErrorDescription(error, info.lineInfo));
}
}
}
return errors;
}
List<AnalysisErrorInfo> _analyze(Iterable<File> files) {
context = AnalysisEngine.instance.createAnalysisContext();
_processAnalysisOptions(context, options);
Packages packages = _getPackageConfig();
context.sourceFactory =
new SourceFactory(_getResolvers(context, packages), packages);
List<Source> sources = <Source>[];
ChangeSet changeSet = new ChangeSet();
for (File file in files) {
JavaFile sourceFile = new JavaFile(path.normalize(file.absolute.path));
Source source = new FileBasedSource(sourceFile, sourceFile.toURI());
Uri uri = context.sourceFactory.restoreUri(source);
if (uri != null) {
source = new FileBasedSource(sourceFile, uri);
}
sources.add(source);
changeSet.addedSource(source);
}
context.applyChanges(changeSet);
List<AnalysisErrorInfo> infos = <AnalysisErrorInfo>[];
for (Source source in sources) {
context.computeErrors(source);
infos.add(context.getErrors(source));
_analyzedSources.add(source);
}
return infos;
}
Packages _getPackageConfig() {
if (options.packageConfigPath != null) {
String packageConfigPath = options.packageConfigPath;
Uri fileUri = new Uri.file(packageConfigPath);
try {
File configFile = new File.fromUri(fileUri).absolute;
List<int> bytes = configFile.readAsBytesSync();
Map<String, Uri> map = pkgfile.parse(bytes, configFile.uri);
return new MapPackages(map);
} catch (e) {
throw new AnalysisDriverException('Unable to create package map.');
}
}
return null;
}
Map<String, List<file_system.Folder>> _getPackageMap(Packages packages) {
if (packages == null) return null;
Map<String, List<file_system.Folder>> folderMap =
new Map<String, List<file_system.Folder>>();
packages.asMap().forEach((String packagePath, Uri uri) {
folderMap[packagePath] = <file_system.Folder>[
PhysicalResourceProvider.INSTANCE.getFolder(path.fromUri(uri))
];
});
return folderMap;
}
List<UriResolver> _getResolvers(
InternalAnalysisContext context, Packages packages) {
DartSdk sdk = new DirectoryBasedDartSdk(new JavaFile(sdkDir));
List<UriResolver> resolvers = <UriResolver>[];
Map<String, List<file_system.Folder>> packageMap = _getPackageMap(packages);
EmbedderYamlLocator yamlLocator = context.embedderYamlLocator;
yamlLocator.refresh(packageMap);
EmbedderUriResolver embedderUriResolver =
new EmbedderUriResolver(yamlLocator.embedderYamls);
if (embedderUriResolver.length == 0) {
resolvers.add(new DartUriResolver(sdk));
} else {
resolvers.add(embedderUriResolver);
}
if (options.packageRootPath != null) {
JavaFile packageDirectory = new JavaFile(options.packageRootPath);
resolvers.add(new PackageUriResolver(<JavaFile>[packageDirectory]));
} else {
PubPackageMapProvider pubPackageMapProvider =
new PubPackageMapProvider(PhysicalResourceProvider.INSTANCE, sdk);
PackageMapInfo packageMapInfo = pubPackageMapProvider.computePackageMap(
PhysicalResourceProvider.INSTANCE.getResource('.'));
Map<String, List<Folder>> packageMap = packageMapInfo.packageMap;
if (packageMap != null) {
resolvers.add(new PackageMapUriResolver(
PhysicalResourceProvider.INSTANCE, packageMap));
}
}
resolvers.add(new FileUriResolver());
return resolvers;
}
bool _isFiltered(AnalysisError error) {
ErrorProcessor processor = ErrorProcessor.getProcessor(context, error);
// Filtered errors are processed to a severity of `null`.
return processor != null && processor.severity == null;
}
void _processAnalysisOptions(
AnalysisContext context, AnalysisOptions analysisOptions) {
List<OptionsProcessor> optionsProcessors =
AnalysisEngine.instance.optionsPlugin.optionsProcessors;
try {
String optionsPath = options.analysisOptionsFile;
if (optionsPath != null) {
file_system.File file =
PhysicalResourceProvider.INSTANCE.getFile(optionsPath);
Map<Object, Object> optionMap =
analysisOptionsProvider.getOptionsFromFile(file);
optionsProcessors.forEach(
(OptionsProcessor p) => p.optionsProcessed(context, optionMap));
if (optionMap != null) {
configureContextOptions(context, optionMap);
}
}
} on Exception catch (e) {
optionsProcessors.forEach((OptionsProcessor p) => p.onError(e));
}
}
void _processPlugins() {
List<Plugin> plugins = <Plugin>[];
plugins.addAll(AnalysisEngine.instance.requiredPlugins);
plugins.add(AnalysisEngine.instance.commandLinePlugin);
plugins.add(AnalysisEngine.instance.optionsPlugin);
plugins.add(linterPlugin);
ExtensionManager manager = new ExtensionManager();
manager.processPlugins(plugins);
}
}
class AnalysisDriverException implements Exception {
final String message;
AnalysisDriverException([this.message]);
@override
String toString() => message == null ? 'Exception' : 'Exception: $message';
}
class AnalysisErrorDescription {
static Directory cwd = Directory.current.absolute;
final AnalysisError error;
final LineInfo line;
AnalysisErrorDescription(this.error, this.line);
ErrorCode get errorCode => error.errorCode;
String get errorType => error.errorCode.type.displayName;
LineInfo_Location get location => line.getLocation(error.offset);
String get path => _shorten(cwd.path, error.source.fullName);
Source get source => error.source;
String asString() => '[$errorType] ${error.message} ($path, '
'line ${location.lineNumber}, col ${location.columnNumber})';
static String _shorten(String root, String path) =>
path.startsWith(root) ? path.substring(root.length + 1) : path;
}
class DriverOptions extends AnalysisOptionsImpl {
@override
int cacheSize = 512;
/// The path to the dart SDK.
String dartSdkPath;
/// The path to a `.packages` configuration file
String packageConfigPath;
/// The path to the package root.
String packageRootPath;
/// The path to analysis options.
String analysisOptionsFile;
@override
bool generateSdkErrors = false;
/// Analysis options map.
Map<Object, Object> analysisOptions;
@override
bool lint = true;
/// Out sink for logging.
IOSink outSink = stdout;
/// Error sink for logging.
IOSink errorSink = stderr;
}
class _StdLogger extends Logger {
final IOSink outSink;
final IOSink errorSink;
_StdLogger({this.outSink, this.errorSink});
@override
void logError(String message, [Exception exception]) =>
errorSink.writeln(message);
@override
void logInformation(String message, [Exception exception]) =>
outSink.writeln(message);
}

View file

@ -15,6 +15,7 @@ dependencies:
http: ^0.11.3
json_rpc_2: ^2.0.0
json_schema: ^1.0.3
linter: ^0.1.15
mustache4dart: ^1.0.0
package_config: ^0.1.3
path: ^1.3.0
@ -36,7 +37,7 @@ dependencies:
# We don't actually depend on 'analyzer', but 'test' does. We pin the
# version of analyzer we depend on to avoid version skew across our
# packages.
analyzer: 0.27.2
analyzer: 0.27.4-alpha.1
dev_dependencies:
mockito: ^0.11.0