// 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 'package:analyzer/error/error.dart'; import 'package:analyzer/file_system/file_system.dart' as file_system; import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:analyzer/source/analysis_options_provider.dart'; import 'package:analyzer/source/error_processor.dart'; import 'package:analyzer/source/package_map_resolver.dart'; import 'package:analyzer/src/context/builder.dart'; // ignore: implementation_imports import 'package:analyzer/src/dart/sdk/sdk.dart'; // ignore: implementation_imports import 'package:analyzer/src/generated/engine.dart'; // ignore: implementation_imports import 'package:analyzer/src/generated/java_io.dart'; // ignore: implementation_imports import 'package:analyzer/src/generated/source.dart'; // ignore: implementation_imports import 'package:analyzer/src/generated/source_io.dart'; // ignore: implementation_imports import 'package:analyzer/src/task/options.dart'; // ignore: implementation_imports import 'package:linter/src/rules.dart' as linter; // ignore: implementation_imports import 'package:cli_util/cli_util.dart' as cli_util; import 'package:package_config/packages.dart' show Packages; import 'package:package_config/src/packages_impl.dart' show MapPackages; // ignore: implementation_imports import 'package:plugin/manager.dart'; import 'package:plugin/plugin.dart'; import '../base/file_system.dart' hide IOSink; import '../base/io.dart'; class AnalysisDriver { AnalysisDriver(this.options) { AnalysisEngine.instance.logger = new _StdLogger(outSink: options.outSink, errorSink: options.errorSink); _processPlugins(); } final Set<Source> _analyzedSources = new HashSet<Source>(); AnalysisOptionsProvider analysisOptionsProvider = new AnalysisOptionsProvider(); file_system.ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; AnalysisContext context; DriverOptions options; String get sdkDir => options.dartSdkPath ?? cli_util.getSdkPath(); List<AnalysisErrorDescription> analyze(Iterable<File> files) { final List<AnalysisErrorInfo> infos = _analyze(files); final 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.analysisOptions = options; final PackageInfo packageInfo = new PackageInfo(options.packageMap); final List<UriResolver> resolvers = _getResolvers(context, packageInfo.asMap()); context.sourceFactory = new SourceFactory(resolvers, packageInfo.asPackages()); final List<Source> sources = <Source>[]; final ChangeSet changeSet = new ChangeSet(); for (File file in files) { final JavaFile sourceFile = new JavaFile(fs.path.normalize(file.absolute.path)); Source source = new FileBasedSource(sourceFile, sourceFile.toURI()); final Uri uri = context.sourceFactory.restoreUri(source); if (uri != null) { source = new FileBasedSource(sourceFile, uri); } sources.add(source); changeSet.addedSource(source); } context.applyChanges(changeSet); final List<AnalysisErrorInfo> infos = <AnalysisErrorInfo>[]; for (Source source in sources) { context.computeErrors(source); infos.add(context.getErrors(source)); _analyzedSources.add(source); } return infos; } List<UriResolver> _getResolvers(InternalAnalysisContext context, Map<String, List<file_system.Folder>> packageMap) { // Create our list of resolvers. final List<UriResolver> resolvers = <UriResolver>[]; // Look for an embedder. final EmbedderYamlLocator locator = new EmbedderYamlLocator(packageMap); if (locator.embedderYamls.isNotEmpty) { // Create and configure an embedded SDK. final EmbedderSdk sdk = new EmbedderSdk(PhysicalResourceProvider.INSTANCE, locator.embedderYamls); // Fail fast if no URI mappings are found. assert(sdk.libraryMap.size() > 0); sdk.analysisOptions = context.analysisOptions; resolvers.add(new DartUriResolver(sdk)); } else { // Fall back to a standard SDK if no embedder is found. final FolderBasedDartSdk sdk = new FolderBasedDartSdk(resourceProvider, PhysicalResourceProvider.INSTANCE.getFolder(sdkDir)); sdk.analysisOptions = context.analysisOptions; resolvers.add(new DartUriResolver(sdk)); } if (options.packageRootPath != null) { final ContextBuilderOptions builderOptions = new ContextBuilderOptions(); builderOptions.defaultPackagesDirectoryPath = options.packageRootPath; final ContextBuilder builder = new ContextBuilder(resourceProvider, null, null, options: builderOptions); final PackageMapUriResolver packageUriResolver = new PackageMapUriResolver(resourceProvider, builder.convertPackagesToMap(builder.createPackageMap(''))); resolvers.add(packageUriResolver); } resolvers.add(new file_system.ResourceUriResolver(resourceProvider)); return resolvers; } bool _isFiltered(AnalysisError error) { final ErrorProcessor processor = ErrorProcessor.getProcessor(context.analysisOptions, error); // Filtered errors are processed to a severity of null. return processor != null && processor.severity == null; } void _processAnalysisOptions() { final String optionsPath = options.analysisOptionsFile; if (optionsPath != null) { final file_system.File file = PhysicalResourceProvider.INSTANCE.getFile(optionsPath); final Map<Object, Object> optionMap = analysisOptionsProvider.getOptionsFromFile(file); if (optionMap != null) applyToAnalysisOptions(options, optionMap); } } void _processPlugins() { final List<Plugin> plugins = <Plugin>[]; plugins.addAll(AnalysisEngine.instance.requiredPlugins); final ExtensionManager manager = new ExtensionManager(); manager.processPlugins(plugins); linter.registerLintRules(); } } class AnalysisDriverException implements Exception { AnalysisDriverException([this.message]); final String message; @override String toString() => message == null ? 'Exception' : 'Exception: $message'; } class AnalysisErrorDescription { AnalysisErrorDescription(this.error, this.line); static Directory cwd = fs.currentDirectory.absolute; final AnalysisError error; final LineInfo line; ErrorCode get errorCode => error.errorCode; String get errorType { final ErrorSeverity severity = errorCode.errorSeverity; if (severity == ErrorSeverity.INFO) { if (errorCode.type == ErrorType.HINT || errorCode.type == ErrorType.LINT) return errorCode.type.displayName; } return severity.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 { DriverOptions() { // Set defaults. lint = true; generateSdkErrors = false; trackCacheDependencies = false; } /// The path to the dart SDK. String dartSdkPath; /// Map of packages to folder paths. Map<String, String> packageMap; /// The path to the package root. String packageRootPath; /// The path to analysis options. String analysisOptionsFile; /// Out sink for logging. IOSink outSink = stdout; /// Error sink for logging. IOSink errorSink = stderr; } class PackageInfo { PackageInfo(Map<String, String> packageMap) { final Map<String, Uri> packages = new HashMap<String, Uri>(); for (String package in packageMap.keys) { final String path = packageMap[package]; packages[package] = new Uri.directory(path); _map[package] = <file_system.Folder>[ PhysicalResourceProvider.INSTANCE.getFolder(path) ]; } _packages = new MapPackages(packages); } Packages _packages; Map<String, List<file_system.Folder>> asMap() => _map; final HashMap<String, List<file_system.Folder>> _map = new HashMap<String, List<file_system.Folder>>(); Packages asPackages() => _packages; } class _StdLogger extends Logger { _StdLogger({this.outSink, this.errorSink}); final IOSink outSink; final IOSink errorSink; @override void logError(String message, [Exception exception]) => errorSink.writeln(message); @override void logInformation(String message, [Exception exception]) { // TODO(pq): remove once addressed in analyzer (http://dartbug.com/28285) if (message != 'No definition of type FutureOr') outSink.writeln(message); } }