| Index: pkg/analyzer_cli/lib/src/analyzer_impl.dart
|
| diff --git a/pkg/analyzer_cli/lib/src/analyzer_impl.dart b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..32d5f3792c89ce7552ff5b565028e5f09037a305
|
| --- /dev/null
|
| +++ b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
|
| @@ -0,0 +1,303 @@
|
| +// Copyright (c) 2015, 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.
|
| +
|
| +library analyzer_cli.src.analyzer_impl;
|
| +
|
| +import 'dart:collection';
|
| +import 'dart:io';
|
| +
|
| +import 'package:analyzer/src/generated/element.dart';
|
| +import 'package:analyzer/src/generated/engine.dart';
|
| +import 'package:analyzer/src/generated/error.dart';
|
| +import 'package:analyzer/src/generated/java_engine.dart';
|
| +import 'package:analyzer/src/generated/java_io.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/generated/utilities_general.dart';
|
| +import 'package:analyzer_cli/src/driver.dart';
|
| +import 'package:analyzer_cli/src/error_formatter.dart';
|
| +import 'package:analyzer_cli/src/options.dart';
|
| +
|
| +DirectoryBasedDartSdk sdk;
|
| +
|
| +/// The maximum number of sources for which AST structures should be kept in the cache.
|
| +const int _maxCacheSize = 512;
|
| +
|
| +int currentTimeMillis() => new DateTime.now().millisecondsSinceEpoch;
|
| +
|
| +/// Analyzes single library [File].
|
| +class AnalyzerImpl {
|
| + final CommandLineOptions options;
|
| + final int startTime;
|
| +
|
| + final AnalysisContext context;
|
| + final Source librarySource;
|
| +
|
| + /// All [Source]s references by the analyzed library.
|
| + final Set<Source> sources = new Set<Source>();
|
| +
|
| + /// All [AnalysisErrorInfo]s in the analyzed library.
|
| + final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>();
|
| +
|
| + /// [HashMap] between sources and analysis error infos.
|
| + final HashMap<Source, AnalysisErrorInfo> sourceErrorsMap =
|
| + new HashMap<Source, AnalysisErrorInfo>();
|
| +
|
| + /// If the file specified on the command line is part of a package, the name
|
| + /// of that package. Otherwise `null`. This allows us to analyze the file
|
| + /// specified on the command line as though it is reached via a "package:"
|
| + /// URI, but avoid suppressing its output in the event that the user has not
|
| + /// specified the "--package-warnings" option.
|
| + String _selfPackageName;
|
| +
|
| + AnalyzerImpl(this.context, this.librarySource, this.options, this.startTime);
|
| +
|
| + /// Returns the maximal [ErrorSeverity] of the recorded errors.
|
| + ErrorSeverity get maxErrorSeverity {
|
| + var status = ErrorSeverity.NONE;
|
| + for (AnalysisErrorInfo errorInfo in errorInfos) {
|
| + for (AnalysisError error in errorInfo.errors) {
|
| + if (!_isDesiredError(error)) {
|
| + continue;
|
| + }
|
| + var severity = computeSeverity(error, options);
|
| + status = status.max(severity);
|
| + }
|
| + }
|
| + return status;
|
| + }
|
| +
|
| + void addCompilationUnitSource(CompilationUnitElement unit,
|
| + Set<LibraryElement> libraries, Set<CompilationUnitElement> units) {
|
| + if (unit == null || units.contains(unit)) {
|
| + return;
|
| + }
|
| + units.add(unit);
|
| + sources.add(unit.source);
|
| + }
|
| +
|
| + void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries,
|
| + Set<CompilationUnitElement> units) {
|
| + if (library == null || !libraries.add(library)) {
|
| + return;
|
| + }
|
| + // Maybe skip library.
|
| + {
|
| + UriKind uriKind = library.source.uriKind;
|
| + // Optionally skip package: libraries.
|
| + if (!options.showPackageWarnings && _isOtherPackage(library.source.uri)) {
|
| + return;
|
| + }
|
| + // Optionally skip SDK libraries.
|
| + if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) {
|
| + return;
|
| + }
|
| + }
|
| + // Add compilation units.
|
| + addCompilationUnitSource(library.definingCompilationUnit, libraries, units);
|
| + for (CompilationUnitElement child in library.parts) {
|
| + addCompilationUnitSource(child, libraries, units);
|
| + }
|
| + // Add referenced libraries.
|
| + for (LibraryElement child in library.importedLibraries) {
|
| + addLibrarySources(child, libraries, units);
|
| + }
|
| + for (LibraryElement child in library.exportedLibraries) {
|
| + addLibrarySources(child, libraries, units);
|
| + }
|
| + }
|
| +
|
| + /// Treats the [sourcePath] as the top level library and analyzes it using a
|
| + /// synchronous algorithm over the analysis engine. If [printMode] is `0`,
|
| + /// then no error or performance information is printed. If [printMode] is `1`,
|
| + /// then both will be printed. If [printMode] is `2`, then only performance
|
| + /// information will be printed, and it will be marked as being for a cold VM.
|
| + ErrorSeverity analyzeSync({int printMode: 1}) {
|
| + setupForAnalysis();
|
| + return _analyzeSync(printMode);
|
| + }
|
| +
|
| + /// Fills [errorInfos] using [sources].
|
| + void prepareErrors() {
|
| + for (Source source in sources) {
|
| + context.computeErrors(source);
|
| +
|
| + errorInfos.add(context.getErrors(source));
|
| + }
|
| + }
|
| +
|
| + /// Fills [sources].
|
| + void prepareSources(LibraryElement library) {
|
| + var units = new Set<CompilationUnitElement>();
|
| + var libraries = new Set<LibraryElement>();
|
| + addLibrarySources(library, libraries, units);
|
| + }
|
| +
|
| + /// Setup local fields such as the analysis context for analysis.
|
| + void setupForAnalysis() {
|
| + sources.clear();
|
| + errorInfos.clear();
|
| + Uri libraryUri = librarySource.uri;
|
| + if (libraryUri.scheme == 'package' && libraryUri.pathSegments.length > 0) {
|
| + _selfPackageName = libraryUri.pathSegments[0];
|
| + }
|
| + }
|
| +
|
| + /// The sync version of analysis.
|
| + ErrorSeverity _analyzeSync(int printMode) {
|
| + // Don't try to analyze parts.
|
| + if (context.computeKindOf(librarySource) == SourceKind.PART) {
|
| + stderr.writeln("Only libraries can be analyzed.");
|
| + stderr.writeln(
|
| + "${librarySource.fullName} is a part and can not be analyzed.");
|
| + return ErrorSeverity.ERROR;
|
| + }
|
| + // Resolve library.
|
| + var libraryElement = context.computeLibraryElement(librarySource);
|
| + // Prepare source and errors.
|
| + prepareSources(libraryElement);
|
| + prepareErrors();
|
| +
|
| + // Print errors and performance numbers.
|
| + if (printMode == 1) {
|
| + _printErrorsAndPerf();
|
| + } else if (printMode == 2) {
|
| + _printColdPerf();
|
| + }
|
| +
|
| + // Compute max severity and set exitCode.
|
| + ErrorSeverity status = maxErrorSeverity;
|
| + if (status == ErrorSeverity.WARNING && options.warningsAreFatal) {
|
| + status = ErrorSeverity.ERROR;
|
| + }
|
| + return status;
|
| + }
|
| +
|
| + bool _isDesiredError(AnalysisError error) {
|
| + if (error.errorCode.type == ErrorType.TODO) {
|
| + return false;
|
| + }
|
| + if (computeSeverity(error, options) == ErrorSeverity.INFO &&
|
| + options.disableHints) {
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + /// Determine whether the given URI refers to a package other than the package
|
| + /// being analyzed.
|
| + bool _isOtherPackage(Uri uri) {
|
| + if (uri.scheme != 'package') {
|
| + return false;
|
| + }
|
| + if (_selfPackageName != null &&
|
| + uri.pathSegments.length > 0 &&
|
| + uri.pathSegments[0] == _selfPackageName) {
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + _printColdPerf() {
|
| + // Print cold VM performance numbers.
|
| + int totalTime = currentTimeMillis() - startTime;
|
| + int otherTime = totalTime;
|
| + for (PerformanceTag tag in PerformanceTag.all) {
|
| + if (tag != PerformanceTag.UNKNOWN) {
|
| + int tagTime = tag.elapsedMs;
|
| + outSink.writeln('${tag.label}-cold:$tagTime');
|
| + otherTime -= tagTime;
|
| + }
|
| + }
|
| + outSink.writeln('other-cold:$otherTime');
|
| + outSink.writeln("total-cold:$totalTime");
|
| + }
|
| +
|
| + _printErrorsAndPerf() {
|
| + // The following is a hack. We currently print out to stderr to ensure that
|
| + // when in batch mode we print to stderr, this is because the prints from
|
| + // batch are made to stderr. The reason that options.shouldBatch isn't used
|
| + // is because when the argument flags are constructed in BatchRunner and
|
| + // passed in from batch mode which removes the batch flag to prevent the
|
| + // "cannot have the batch flag and source file" error message.
|
| + StringSink sink = options.machineFormat ? errorSink : outSink;
|
| +
|
| + // Print errors.
|
| + ErrorFormatter formatter =
|
| + new ErrorFormatter(sink, options, _isDesiredError);
|
| + formatter.formatErrors(errorInfos);
|
| + }
|
| +
|
| + /// Compute the severity of the error; however:
|
| + /// * if [options.enableTypeChecks] is false, then de-escalate checked-mode
|
| + /// compile time errors to a severity of [ErrorSeverity.INFO].
|
| + /// * if [options.hintsAreFatal] is true, escalate hints to errors.
|
| + static ErrorSeverity computeSeverity(
|
| + AnalysisError error, CommandLineOptions options) {
|
| + if (!options.enableTypeChecks &&
|
| + error.errorCode.type == ErrorType.CHECKED_MODE_COMPILE_TIME_ERROR) {
|
| + return ErrorSeverity.INFO;
|
| + }
|
| + if (options.hintsAreFatal && error.errorCode is HintCode) {
|
| + return ErrorSeverity.ERROR;
|
| + }
|
| + return error.errorCode.errorSeverity;
|
| + }
|
| +
|
| + /// Return the corresponding package directory or `null` if none is found.
|
| + static JavaFile getPackageDirectoryFor(JavaFile sourceFile) {
|
| + // We are going to ask parent file, so get absolute path.
|
| + sourceFile = sourceFile.getAbsoluteFile();
|
| + // Look in the containing directories.
|
| + JavaFile dir = sourceFile.getParentFile();
|
| + while (dir != null) {
|
| + JavaFile packagesDir = new JavaFile.relative(dir, "packages");
|
| + if (packagesDir.exists()) {
|
| + return packagesDir;
|
| + }
|
| + dir = dir.getParentFile();
|
| + }
|
| + // Not found.
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +/// This [Logger] prints out information comments to [outSink] and error messages
|
| +/// to [errorSink].
|
| +class StdLogger extends Logger {
|
| + StdLogger();
|
| +
|
| + @override
|
| + void logError(String message, [CaughtException exception]) {
|
| + errorSink.writeln(message);
|
| + if (exception != null) {
|
| + errorSink.writeln(exception);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void logError2(String message, Object exception) {
|
| + errorSink.writeln(message);
|
| + if (exception != null) {
|
| + errorSink.writeln(exception.toString());
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void logInformation(String message, [CaughtException exception]) {
|
| + outSink.writeln(message);
|
| + if (exception != null) {
|
| + outSink.writeln(exception);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void logInformation2(String message, Object exception) {
|
| + outSink.writeln(message);
|
| + if (exception != null) {
|
| + outSink.writeln(exception.toString());
|
| + }
|
| + }
|
| +}
|
|
|