OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library analyzer_cli.src.analyzer_impl; |
| 6 |
| 7 import 'dart:collection'; |
| 8 import 'dart:io'; |
| 9 |
| 10 import 'package:analyzer/src/generated/element.dart'; |
| 11 import 'package:analyzer/src/generated/engine.dart'; |
| 12 import 'package:analyzer/src/generated/error.dart'; |
| 13 import 'package:analyzer/src/generated/java_engine.dart'; |
| 14 import 'package:analyzer/src/generated/java_io.dart'; |
| 15 import 'package:analyzer/src/generated/sdk_io.dart'; |
| 16 import 'package:analyzer/src/generated/source.dart'; |
| 17 import 'package:analyzer/src/generated/source_io.dart'; |
| 18 import 'package:analyzer/src/generated/utilities_general.dart'; |
| 19 import 'package:analyzer_cli/src/driver.dart'; |
| 20 import 'package:analyzer_cli/src/error_formatter.dart'; |
| 21 import 'package:analyzer_cli/src/options.dart'; |
| 22 |
| 23 DirectoryBasedDartSdk sdk; |
| 24 |
| 25 /// The maximum number of sources for which AST structures should be kept in the
cache. |
| 26 const int _maxCacheSize = 512; |
| 27 |
| 28 int currentTimeMillis() => new DateTime.now().millisecondsSinceEpoch; |
| 29 |
| 30 /// Analyzes single library [File]. |
| 31 class AnalyzerImpl { |
| 32 final CommandLineOptions options; |
| 33 final int startTime; |
| 34 |
| 35 final AnalysisContext context; |
| 36 final Source librarySource; |
| 37 |
| 38 /// All [Source]s references by the analyzed library. |
| 39 final Set<Source> sources = new Set<Source>(); |
| 40 |
| 41 /// All [AnalysisErrorInfo]s in the analyzed library. |
| 42 final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>(); |
| 43 |
| 44 /// [HashMap] between sources and analysis error infos. |
| 45 final HashMap<Source, AnalysisErrorInfo> sourceErrorsMap = |
| 46 new HashMap<Source, AnalysisErrorInfo>(); |
| 47 |
| 48 /// If the file specified on the command line is part of a package, the name |
| 49 /// of that package. Otherwise `null`. This allows us to analyze the file |
| 50 /// specified on the command line as though it is reached via a "package:" |
| 51 /// URI, but avoid suppressing its output in the event that the user has not |
| 52 /// specified the "--package-warnings" option. |
| 53 String _selfPackageName; |
| 54 |
| 55 AnalyzerImpl(this.context, this.librarySource, this.options, this.startTime); |
| 56 |
| 57 /// Returns the maximal [ErrorSeverity] of the recorded errors. |
| 58 ErrorSeverity get maxErrorSeverity { |
| 59 var status = ErrorSeverity.NONE; |
| 60 for (AnalysisErrorInfo errorInfo in errorInfos) { |
| 61 for (AnalysisError error in errorInfo.errors) { |
| 62 if (!_isDesiredError(error)) { |
| 63 continue; |
| 64 } |
| 65 var severity = computeSeverity(error, options); |
| 66 status = status.max(severity); |
| 67 } |
| 68 } |
| 69 return status; |
| 70 } |
| 71 |
| 72 void addCompilationUnitSource(CompilationUnitElement unit, |
| 73 Set<LibraryElement> libraries, Set<CompilationUnitElement> units) { |
| 74 if (unit == null || units.contains(unit)) { |
| 75 return; |
| 76 } |
| 77 units.add(unit); |
| 78 sources.add(unit.source); |
| 79 } |
| 80 |
| 81 void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries, |
| 82 Set<CompilationUnitElement> units) { |
| 83 if (library == null || !libraries.add(library)) { |
| 84 return; |
| 85 } |
| 86 // Maybe skip library. |
| 87 { |
| 88 UriKind uriKind = library.source.uriKind; |
| 89 // Optionally skip package: libraries. |
| 90 if (!options.showPackageWarnings && _isOtherPackage(library.source.uri)) { |
| 91 return; |
| 92 } |
| 93 // Optionally skip SDK libraries. |
| 94 if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) { |
| 95 return; |
| 96 } |
| 97 } |
| 98 // Add compilation units. |
| 99 addCompilationUnitSource(library.definingCompilationUnit, libraries, units); |
| 100 for (CompilationUnitElement child in library.parts) { |
| 101 addCompilationUnitSource(child, libraries, units); |
| 102 } |
| 103 // Add referenced libraries. |
| 104 for (LibraryElement child in library.importedLibraries) { |
| 105 addLibrarySources(child, libraries, units); |
| 106 } |
| 107 for (LibraryElement child in library.exportedLibraries) { |
| 108 addLibrarySources(child, libraries, units); |
| 109 } |
| 110 } |
| 111 |
| 112 /// Treats the [sourcePath] as the top level library and analyzes it using a |
| 113 /// synchronous algorithm over the analysis engine. If [printMode] is `0`, |
| 114 /// then no error or performance information is printed. If [printMode] is `1`
, |
| 115 /// then both will be printed. If [printMode] is `2`, then only performance |
| 116 /// information will be printed, and it will be marked as being for a cold VM. |
| 117 ErrorSeverity analyzeSync({int printMode: 1}) { |
| 118 setupForAnalysis(); |
| 119 return _analyzeSync(printMode); |
| 120 } |
| 121 |
| 122 /// Fills [errorInfos] using [sources]. |
| 123 void prepareErrors() { |
| 124 for (Source source in sources) { |
| 125 context.computeErrors(source); |
| 126 |
| 127 errorInfos.add(context.getErrors(source)); |
| 128 } |
| 129 } |
| 130 |
| 131 /// Fills [sources]. |
| 132 void prepareSources(LibraryElement library) { |
| 133 var units = new Set<CompilationUnitElement>(); |
| 134 var libraries = new Set<LibraryElement>(); |
| 135 addLibrarySources(library, libraries, units); |
| 136 } |
| 137 |
| 138 /// Setup local fields such as the analysis context for analysis. |
| 139 void setupForAnalysis() { |
| 140 sources.clear(); |
| 141 errorInfos.clear(); |
| 142 Uri libraryUri = librarySource.uri; |
| 143 if (libraryUri.scheme == 'package' && libraryUri.pathSegments.length > 0) { |
| 144 _selfPackageName = libraryUri.pathSegments[0]; |
| 145 } |
| 146 } |
| 147 |
| 148 /// The sync version of analysis. |
| 149 ErrorSeverity _analyzeSync(int printMode) { |
| 150 // Don't try to analyze parts. |
| 151 if (context.computeKindOf(librarySource) == SourceKind.PART) { |
| 152 stderr.writeln("Only libraries can be analyzed."); |
| 153 stderr.writeln( |
| 154 "${librarySource.fullName} is a part and can not be analyzed."); |
| 155 return ErrorSeverity.ERROR; |
| 156 } |
| 157 // Resolve library. |
| 158 var libraryElement = context.computeLibraryElement(librarySource); |
| 159 // Prepare source and errors. |
| 160 prepareSources(libraryElement); |
| 161 prepareErrors(); |
| 162 |
| 163 // Print errors and performance numbers. |
| 164 if (printMode == 1) { |
| 165 _printErrorsAndPerf(); |
| 166 } else if (printMode == 2) { |
| 167 _printColdPerf(); |
| 168 } |
| 169 |
| 170 // Compute max severity and set exitCode. |
| 171 ErrorSeverity status = maxErrorSeverity; |
| 172 if (status == ErrorSeverity.WARNING && options.warningsAreFatal) { |
| 173 status = ErrorSeverity.ERROR; |
| 174 } |
| 175 return status; |
| 176 } |
| 177 |
| 178 bool _isDesiredError(AnalysisError error) { |
| 179 if (error.errorCode.type == ErrorType.TODO) { |
| 180 return false; |
| 181 } |
| 182 if (computeSeverity(error, options) == ErrorSeverity.INFO && |
| 183 options.disableHints) { |
| 184 return false; |
| 185 } |
| 186 return true; |
| 187 } |
| 188 |
| 189 /// Determine whether the given URI refers to a package other than the package |
| 190 /// being analyzed. |
| 191 bool _isOtherPackage(Uri uri) { |
| 192 if (uri.scheme != 'package') { |
| 193 return false; |
| 194 } |
| 195 if (_selfPackageName != null && |
| 196 uri.pathSegments.length > 0 && |
| 197 uri.pathSegments[0] == _selfPackageName) { |
| 198 return false; |
| 199 } |
| 200 return true; |
| 201 } |
| 202 |
| 203 _printColdPerf() { |
| 204 // Print cold VM performance numbers. |
| 205 int totalTime = currentTimeMillis() - startTime; |
| 206 int otherTime = totalTime; |
| 207 for (PerformanceTag tag in PerformanceTag.all) { |
| 208 if (tag != PerformanceTag.UNKNOWN) { |
| 209 int tagTime = tag.elapsedMs; |
| 210 outSink.writeln('${tag.label}-cold:$tagTime'); |
| 211 otherTime -= tagTime; |
| 212 } |
| 213 } |
| 214 outSink.writeln('other-cold:$otherTime'); |
| 215 outSink.writeln("total-cold:$totalTime"); |
| 216 } |
| 217 |
| 218 _printErrorsAndPerf() { |
| 219 // The following is a hack. We currently print out to stderr to ensure that |
| 220 // when in batch mode we print to stderr, this is because the prints from |
| 221 // batch are made to stderr. The reason that options.shouldBatch isn't used |
| 222 // is because when the argument flags are constructed in BatchRunner and |
| 223 // passed in from batch mode which removes the batch flag to prevent the |
| 224 // "cannot have the batch flag and source file" error message. |
| 225 StringSink sink = options.machineFormat ? errorSink : outSink; |
| 226 |
| 227 // Print errors. |
| 228 ErrorFormatter formatter = |
| 229 new ErrorFormatter(sink, options, _isDesiredError); |
| 230 formatter.formatErrors(errorInfos); |
| 231 } |
| 232 |
| 233 /// Compute the severity of the error; however: |
| 234 /// * if [options.enableTypeChecks] is false, then de-escalate checked-mode |
| 235 /// compile time errors to a severity of [ErrorSeverity.INFO]. |
| 236 /// * if [options.hintsAreFatal] is true, escalate hints to errors. |
| 237 static ErrorSeverity computeSeverity( |
| 238 AnalysisError error, CommandLineOptions options) { |
| 239 if (!options.enableTypeChecks && |
| 240 error.errorCode.type == ErrorType.CHECKED_MODE_COMPILE_TIME_ERROR) { |
| 241 return ErrorSeverity.INFO; |
| 242 } |
| 243 if (options.hintsAreFatal && error.errorCode is HintCode) { |
| 244 return ErrorSeverity.ERROR; |
| 245 } |
| 246 return error.errorCode.errorSeverity; |
| 247 } |
| 248 |
| 249 /// Return the corresponding package directory or `null` if none is found. |
| 250 static JavaFile getPackageDirectoryFor(JavaFile sourceFile) { |
| 251 // We are going to ask parent file, so get absolute path. |
| 252 sourceFile = sourceFile.getAbsoluteFile(); |
| 253 // Look in the containing directories. |
| 254 JavaFile dir = sourceFile.getParentFile(); |
| 255 while (dir != null) { |
| 256 JavaFile packagesDir = new JavaFile.relative(dir, "packages"); |
| 257 if (packagesDir.exists()) { |
| 258 return packagesDir; |
| 259 } |
| 260 dir = dir.getParentFile(); |
| 261 } |
| 262 // Not found. |
| 263 return null; |
| 264 } |
| 265 } |
| 266 |
| 267 /// This [Logger] prints out information comments to [outSink] and error message
s |
| 268 /// to [errorSink]. |
| 269 class StdLogger extends Logger { |
| 270 StdLogger(); |
| 271 |
| 272 @override |
| 273 void logError(String message, [CaughtException exception]) { |
| 274 errorSink.writeln(message); |
| 275 if (exception != null) { |
| 276 errorSink.writeln(exception); |
| 277 } |
| 278 } |
| 279 |
| 280 @override |
| 281 void logError2(String message, Object exception) { |
| 282 errorSink.writeln(message); |
| 283 if (exception != null) { |
| 284 errorSink.writeln(exception.toString()); |
| 285 } |
| 286 } |
| 287 |
| 288 @override |
| 289 void logInformation(String message, [CaughtException exception]) { |
| 290 outSink.writeln(message); |
| 291 if (exception != null) { |
| 292 outSink.writeln(exception); |
| 293 } |
| 294 } |
| 295 |
| 296 @override |
| 297 void logInformation2(String message, Object exception) { |
| 298 outSink.writeln(message); |
| 299 if (exception != null) { |
| 300 outSink.writeln(exception.toString()); |
| 301 } |
| 302 } |
| 303 } |
OLD | NEW |