Commit 11a54dee authored by Phil Quitslund's avatar Phil Quitslund

Merge pull request #3752 from pq/analysis_rework

Analysis re-work to use analyzer APIs.
parents 4d9fdda7 2df43d50
......@@ -7,10 +7,10 @@ dependencies:
# here we pin it precisely to avoid version skew across our packages.
test: 0.12.13+1
# 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
# We don't actually depend on 'analyzer', but 'test' and 'flutter_tools' do.
# 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
......@@ -11,101 +11,17 @@ 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);
}
bool isDartFile(FileSystemEntity entry) => entry is File && entry.path.endsWith('.dart');
class FileChanged { }
typedef bool FileFilter(FileSystemEntity entity);
class AnalyzeCommand extends FlutterCommand {
AnalyzeCommand() {
......@@ -172,94 +88,60 @@ 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;
}
}
}
if (argResults['current-directory']) {
//TODO (pq): revisit package and directory defaults
if (argResults['current-directory'] && !argResults['flutter-repo']) {
// ./*.dart
Directory currentDirectory = new Directory('.');
bool foundOne = false;
for (FileSystemEntity entry in currentDirectory.listSync()) {
if (isDartFile(entry)) {
dartFiles.add(entry.path);
dartFiles.add(entry);
foundOne = true;
}
}
if (foundOne)
pubSpecDirectories.add('.');
pubSpecDirectories.add(currentDirectory);
}
if (argResults['current-package'])
_addPackage('.', dartFiles, pubSpecDirectories);
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);
if (argResults['current-package'] && !argResults['flutter-repo']) {
// **/.*dart
Directory currentDirectory = new Directory('.');
_collectDartFiles(currentDirectory, dartFiles);
pubSpecDirectories.add(currentDirectory);
}
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);
//TODO (ianh): Fix the intl package resource generator
//TODO (pq): extract this regexp from the exclude in options
RegExp stockExampleFiles = new RegExp('examples/stocks/lib/.*\.dart\$');
if (argResults['flutter-repo']) {
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()) {
......@@ -309,98 +191,36 @@ 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());
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;
String packagesFilePath = path.join(host.path, '.packages');
new File(packagesFilePath).writeAsStringSync(packagesBody.toString());
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);
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);
//TODO (pq): consider error handling
List<AnalysisErrorDescription> errors = analyzer.analyze(dartFiles);
int errorCount = 0;
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();
for (AnalysisErrorDescription error in errors) {
bool shouldIgnore = false;
if (documentAllMembersPattern.firstMatch(errorMessage) != null) {
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(filename)) {
if (isFlutterLibrary(error.source.fullName)) {
if (!argResults['dartdocs']) {
membersMissingDocumentation += 1;
shouldIgnore = true;
......@@ -408,51 +228,29 @@ class AnalyzeCommand extends FlutterCommand {
} 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
//TODO(ianh): Fix the Dart mojom compiler
if (error.source.fullName.endsWith('.mojom.dart'))
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)');
}
}
printError(errorLine);
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']) {
......@@ -465,6 +263,24 @@ class AnalyzeCommand extends FlutterCommand {
return 0;
}
List<File> _collectDartFiles(Directory dir, List<File> collected, {FileFilter exclude}) {
// Bail out in case of a .dartignore.
if (FileSystemEntity.isFileSync(path.join(path.dirname(dir.path), '.dartignore')))
return collected;
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;
......@@ -829,20 +645,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];
}
}
// 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);
}
......@@ -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
......@@ -33,10 +34,8 @@ dependencies:
# precisely.
test: 0.12.13+1
# 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
# Pinned in flutter_test as well.
analyzer: 0.27.4-alpha.1
dev_dependencies:
mockito: ^0.11.0
......
......@@ -38,9 +38,8 @@ void main() {
return createTestCommandRunner(command).run(
<String>['analyze', '--no-current-package', '--no-current-directory', dartFileA.path, dartFileB.path]
).then((int code) {
expect(code, 1);
expect(testLogger.errorText, '[warning] The imported libraries \'a.dart\' and \'b.dart\' cannot have the same name \'test\' (${dartFileB.path})\n');
expect(testLogger.statusText, 'Analyzing 2 entry points...\n');
expect(code, 0);
expect(testLogger.statusText, startsWith('Analyzing 2 files...\nNo analyzer warnings!'));
});
}, overrides: <Type, dynamic>{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment