Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017, 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 import 'package:analyzer/dart/ast/ast.dart'; | |
| 6 import 'package:analyzer/dart/ast/token.dart'; | |
| 7 import 'package:analyzer/dart/element/element.dart'; | |
| 8 import 'package:analyzer/error/error.dart'; | |
| 9 import 'package:analyzer/error/listener.dart'; | |
| 10 import 'package:analyzer/src/context/context.dart'; | |
| 11 import 'package:analyzer/src/dart/analysis/file_state.dart'; | |
| 12 import 'package:analyzer/src/dart/ast/ast.dart'; | |
| 13 import 'package:analyzer/src/dart/element/element.dart'; | |
| 14 import 'package:analyzer/src/dart/scanner/scanner.dart'; | |
| 15 import 'package:analyzer/src/error/codes.dart'; | |
| 16 import 'package:analyzer/src/error/pending_error.dart'; | |
| 17 import 'package:analyzer/src/generated/declaration_resolver.dart'; | |
| 18 import 'package:analyzer/src/generated/engine.dart'; | |
| 19 import 'package:analyzer/src/generated/error_verifier.dart'; | |
| 20 import 'package:analyzer/src/generated/parser.dart'; | |
| 21 import 'package:analyzer/src/generated/resolver.dart'; | |
| 22 import 'package:analyzer/src/generated/source.dart'; | |
| 23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | |
| 24 import 'package:analyzer/src/task/dart.dart'; | |
| 25 import 'package:analyzer/src/task/strong/checker.dart'; | |
| 26 import 'package:front_end/src/scanner/reader.dart'; | |
| 27 | |
| 28 /** | |
| 29 * Analyzer of Dart files. | |
| 30 * | |
| 31 * Work in progress, not ready to be used. | |
| 32 */ | |
| 33 class AnalyzerImpl { | |
| 34 final AnalysisOptions analysisOptions; | |
| 35 final SourceFactory sourceFactory; | |
| 36 final FileSystemState fsState; | |
| 37 final SummaryDataStore store; | |
| 38 | |
| 39 AnalysisContextImpl analysisContext; | |
| 40 TypeProvider typeProvider; | |
| 41 StoreBasedSummaryResynthesizer resynthesizer; | |
| 42 final Map<FileState, RecordingErrorListener> _errorListeners = {}; | |
| 43 final Map<FileState, ErrorReporter> _errorReporters = {}; | |
| 44 final List<UsedImportedElements> usedImportedElementsList = []; | |
| 45 | |
| 46 AnalyzerImpl( | |
| 47 this.analysisOptions, this.sourceFactory, this.fsState, this.store); | |
| 48 | |
| 49 /** | |
| 50 * Compute analysis results for all units of the [library]. | |
| 51 */ | |
| 52 Map<FileState, UnitAnalysisResult> analyze(FileState library) { | |
| 53 Map<FileState, CompilationUnit> units = {}; | |
| 54 | |
| 55 // Parse all files. | |
| 56 units[library] = _parse(library); | |
| 57 for (FileState part in library.partedFiles) { | |
| 58 units[part] = _parse(part); | |
| 59 } | |
| 60 | |
| 61 // Resolve directives. | |
| 62 units.forEach((file, unit) { | |
| 63 _resolveUriBasedDirectives(file, unit); | |
| 64 }); | |
| 65 | |
| 66 _createAnalysisContext(); | |
| 67 | |
| 68 try { | |
| 69 resynthesizer = new StoreBasedSummaryResynthesizer( | |
| 70 analysisContext, sourceFactory, analysisOptions.strongMode, store); | |
| 71 typeProvider = resynthesizer.typeProvider; | |
| 72 analysisContext.typeProvider = typeProvider; | |
| 73 | |
| 74 units.forEach((file, unit) { | |
| 75 _resolveFile(analysisContext, library, file, unit); | |
| 76 }); | |
| 77 | |
| 78 List<UsedLocalElements> usedLocalElementsList = []; | |
| 79 units.forEach((file, unit) { | |
| 80 GatherUsedLocalElementsVisitor visitor = | |
| 81 new GatherUsedLocalElementsVisitor(unit.element.library); | |
| 82 unit.accept(visitor); | |
| 83 usedLocalElementsList.add(visitor.usedElements); | |
| 84 }); | |
| 85 | |
| 86 units.forEach((file, unit) { | |
|
Paul Berry
2017/02/06 22:44:36
Can we merge this loop with the loop above? It se
| |
| 87 LibraryElement libraryElement = unit.element.library; | |
| 88 var visitor = new GatherUsedImportedElementsVisitor(libraryElement); | |
| 89 unit.accept(visitor); | |
| 90 usedImportedElementsList.add(visitor.usedElements); | |
| 91 }); | |
| 92 | |
| 93 units.forEach((file, unit) { | |
| 94 _computeVerifyErrorsAndHints( | |
| 95 analysisContext, library, usedLocalElementsList, file, unit); | |
| 96 }); | |
| 97 } finally { | |
| 98 analysisContext.dispose(); | |
| 99 } | |
| 100 | |
| 101 // Return full results. | |
| 102 Map<FileState, UnitAnalysisResult> results = {}; | |
| 103 units.forEach((file, unit) { | |
| 104 List<AnalysisError> errors = _getErrorListener(file).errors; | |
| 105 results[file] = new UnitAnalysisResult(file, unit, errors); | |
| 106 }); | |
| 107 return results; | |
| 108 } | |
| 109 | |
| 110 void _computeVerifyErrorsAndHints( | |
| 111 AnalysisContext analysisContext, | |
| 112 FileState libraryFile, | |
| 113 List<UsedLocalElements> usedLocalElementsList, | |
| 114 FileState file, | |
| 115 CompilationUnit unit) { | |
| 116 RecordingErrorListener errorListener = _getErrorListener(file); | |
| 117 CompilationUnitElement unitElement = unit.element; | |
| 118 LibraryElement libraryElement = unitElement.library; | |
| 119 | |
| 120 // Verify imports. | |
| 121 { | |
| 122 ImportsVerifier verifier = new ImportsVerifier(); | |
| 123 verifier.addImports(unit); | |
| 124 usedImportedElementsList.forEach(verifier.removeUsedElements); | |
| 125 ErrorReporter errorReporter = _getErrorReporter(file); | |
| 126 verifier.generateDuplicateImportHints(errorReporter); | |
| 127 verifier.generateUnusedImportHints(errorReporter); | |
| 128 verifier.generateUnusedShownNameHints(errorReporter); | |
| 129 } | |
| 130 | |
| 131 { | |
| 132 GatherUsedLocalElementsVisitor visitor = | |
| 133 new GatherUsedLocalElementsVisitor(libraryElement); | |
| 134 unit.accept(visitor); | |
| 135 } | |
| 136 | |
| 137 // Unused local elements. | |
| 138 { | |
| 139 UsedLocalElements usedElements = | |
| 140 new UsedLocalElements.merge(usedLocalElementsList); | |
| 141 UnusedLocalElementsVerifier visitor = | |
| 142 new UnusedLocalElementsVerifier(errorListener, usedElements); | |
| 143 unitElement.accept(visitor); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 void _createAnalysisContext() { | |
| 148 AnalysisContextImpl analysisContext = | |
| 149 AnalysisEngine.instance.createAnalysisContext(); | |
| 150 analysisContext.analysisOptions = analysisOptions; | |
| 151 analysisContext.sourceFactory = sourceFactory.clone(); | |
| 152 analysisContext.contentCache = new _ContentCacheWrapper(fsState); | |
| 153 this.analysisContext = analysisContext; | |
| 154 } | |
| 155 | |
| 156 RecordingErrorListener _getErrorListener(FileState file) => | |
| 157 _errorListeners.putIfAbsent(file, () => new RecordingErrorListener()); | |
| 158 | |
| 159 ErrorReporter _getErrorReporter(FileState file) { | |
| 160 return _errorReporters.putIfAbsent(file, () { | |
| 161 RecordingErrorListener listener = _getErrorListener(file); | |
| 162 return new ErrorReporter(listener, file.source); | |
| 163 }); | |
| 164 } | |
| 165 | |
| 166 /** | |
| 167 * Return a new parsed unresolved [CompilationUnit]. | |
| 168 */ | |
| 169 CompilationUnit _parse(FileState file) { | |
| 170 RecordingErrorListener errorListener = _getErrorListener(file); | |
| 171 | |
| 172 CharSequenceReader reader = new CharSequenceReader(file.content); | |
| 173 Scanner scanner = new Scanner(file.source, reader, errorListener); | |
| 174 scanner.scanGenericMethodComments = analysisOptions.strongMode; | |
| 175 Token token = scanner.tokenize(); | |
| 176 LineInfo lineInfo = new LineInfo(scanner.lineStarts); | |
| 177 | |
| 178 Parser parser = new Parser(file.source, errorListener); | |
| 179 parser.parseGenericMethodComments = analysisOptions.strongMode; | |
| 180 CompilationUnit unit = parser.parseCompilationUnit(token); | |
| 181 unit.lineInfo = lineInfo; | |
| 182 return unit; | |
| 183 } | |
| 184 | |
| 185 void _resolveFile(AnalysisContext analysisContext, FileState library, | |
| 186 FileState file, CompilationUnit unit) { | |
| 187 if (!file.exists) { | |
| 188 var unitElement = new CompilationUnitElementImpl(file.source.shortName); | |
| 189 var libraryElement = new LibraryElementImpl(analysisContext, null, -1, 0); | |
| 190 libraryElement.definingCompilationUnit = unitElement; | |
| 191 unit.element = unitElement; | |
| 192 return; | |
| 193 } | |
| 194 | |
| 195 RecordingErrorListener errorListener = _getErrorListener(file); | |
| 196 | |
| 197 String libraryUri = library.uri.toString(); | |
| 198 String unitUri = file.uri.toString(); | |
| 199 CompilationUnitElement unitElement = resynthesizer.getElement( | |
| 200 new ElementLocationImpl.con3(<String>[libraryUri, unitUri])); | |
| 201 LibraryElement libraryElement = unitElement.library; | |
| 202 | |
| 203 // TODO(scheglov) Hack: set types for top-level variables | |
| 204 // Otherwise TypeResolverVisitor will set declared types, and because we | |
| 205 // don't run InferStaticVariableTypeTask, we will stuck with these declared | |
| 206 // types. And we don't need to run this task - resynthesized elements have | |
| 207 // inferred types. | |
| 208 for (var e in unitElement.topLevelVariables) { | |
| 209 if (!e.isSynthetic) { | |
| 210 e.type; | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 new DeclarationResolver().resolve(unit, unitElement); | |
| 215 | |
| 216 if (file == library) { | |
| 217 // TODO(scheglov) fill these maps? | |
| 218 DirectiveResolver resolver = new DirectiveResolver({}, {}, {}); | |
| 219 unit.accept(resolver); | |
| 220 } | |
| 221 | |
| 222 unit.accept(new EnumMemberBuilder(typeProvider)); | |
| 223 | |
| 224 new TypeParameterBoundsResolver( | |
| 225 typeProvider, libraryElement, unitElement.source, errorListener) | |
| 226 .resolveTypeBounds(unit); | |
| 227 | |
| 228 unit.accept(new TypeResolverVisitor( | |
| 229 libraryElement, unitElement.source, typeProvider, errorListener)); | |
| 230 | |
| 231 LibraryScope libraryScope = new LibraryScope(libraryElement); | |
| 232 unit.accept(new VariableResolverVisitor( | |
| 233 libraryElement, unitElement.source, typeProvider, errorListener, | |
| 234 nameScope: libraryScope)); | |
| 235 | |
| 236 unit.accept(new PartialResolverVisitor(libraryElement, unitElement.source, | |
| 237 typeProvider, AnalysisErrorListener.NULL_LISTENER)); | |
| 238 | |
| 239 // Nothing for RESOLVED_UNIT8? | |
| 240 // Nothing for RESOLVED_UNIT9? | |
| 241 // Nothing for RESOLVED_UNIT10? | |
| 242 | |
| 243 unit.accept(new ResolverVisitor( | |
| 244 libraryElement, unitElement.source, typeProvider, errorListener)); | |
| 245 | |
| 246 // TODO(scheglov) RESOLVED_UNIT12: compute constants | |
| 247 | |
| 248 // | |
| 249 // Use the ErrorVerifier to compute errors. | |
| 250 // | |
| 251 List<PendingError> pendingErrors; | |
| 252 { | |
| 253 RequiredConstantsComputer computer = | |
| 254 new RequiredConstantsComputer(file.source); | |
| 255 unit.accept(computer); | |
| 256 pendingErrors = computer.pendingErrors; | |
| 257 List<ConstantEvaluationTarget> requiredConstants = | |
| 258 computer.requiredConstants; | |
| 259 } | |
| 260 | |
| 261 if (analysisOptions.strongMode) { | |
| 262 AnalysisOptionsImpl options = analysisOptions as AnalysisOptionsImpl; | |
| 263 CodeChecker checker = new CodeChecker( | |
| 264 typeProvider, | |
| 265 new StrongTypeSystemImpl(typeProvider, | |
| 266 implicitCasts: options.implicitCasts, | |
| 267 nonnullableTypes: options.nonnullableTypes), | |
| 268 errorListener, | |
| 269 options); | |
| 270 checker.visitCompilationUnit(unit); | |
| 271 } | |
| 272 | |
| 273 var errorReporter = _getErrorReporter(file); | |
| 274 | |
| 275 // | |
| 276 // Validate the directives. | |
| 277 // | |
| 278 _validateUriBasedDirectives(file, unit); | |
| 279 | |
| 280 // | |
| 281 // Use the ConstantVerifier to compute errors. | |
| 282 // | |
| 283 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, | |
| 284 libraryElement, typeProvider, analysisContext.declaredVariables); | |
| 285 unit.accept(constantVerifier); | |
| 286 | |
| 287 // | |
| 288 // Use the ErrorVerifier to compute errors. | |
| 289 // | |
| 290 ErrorVerifier errorVerifier = new ErrorVerifier( | |
| 291 errorReporter, | |
| 292 libraryElement, | |
| 293 typeProvider, | |
| 294 new InheritanceManager(libraryElement), | |
| 295 analysisOptions.enableSuperMixins); | |
| 296 unit.accept(errorVerifier); | |
| 297 | |
| 298 // | |
| 299 // Convert the pending errors into actual errors. | |
| 300 // | |
| 301 for (PendingError pendingError in pendingErrors) { | |
| 302 errorListener.onError(pendingError.toAnalysisError()); | |
| 303 } | |
| 304 | |
| 305 // | |
| 306 // Find dead code. | |
| 307 // | |
| 308 unit.accept(new DeadCodeVerifier(errorReporter, | |
| 309 typeSystem: analysisContext.typeSystem)); | |
| 310 | |
| 311 // Dart2js analysis. | |
| 312 if (analysisOptions.dart2jsHint) { | |
| 313 unit.accept(new Dart2JSVerifier(errorReporter)); | |
| 314 } | |
| 315 | |
| 316 InheritanceManager inheritanceManager = new InheritanceManager( | |
| 317 libraryElement, | |
| 318 includeAbstractFromSuperclasses: true); | |
| 319 | |
| 320 unit.accept(new BestPracticesVerifier( | |
| 321 errorReporter, typeProvider, libraryElement, inheritanceManager, | |
| 322 typeSystem: analysisContext.typeSystem)); | |
| 323 | |
| 324 unit.accept(new OverrideVerifier(errorReporter, inheritanceManager)); | |
| 325 | |
| 326 new ToDoFinder(errorReporter).findIn(unit); | |
| 327 } | |
| 328 | |
| 329 /** | |
| 330 * Return the result of resolve the given [uriContent], reporting errors | |
| 331 * against the [uriLiteral]. | |
| 332 */ | |
| 333 Source _resolveUri(FileState file, bool isImport, StringLiteral uriLiteral, | |
| 334 String uriContent) { | |
| 335 UriValidationCode code = | |
| 336 UriBasedDirectiveImpl.validateUri(isImport, uriLiteral, uriContent); | |
| 337 if (code == null) { | |
| 338 try { | |
| 339 Uri.parse(uriContent); | |
| 340 } on FormatException { | |
| 341 return null; | |
| 342 } | |
| 343 return sourceFactory.resolveUri(file.source, uriContent); | |
| 344 } else if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) { | |
| 345 return null; | |
| 346 } else if (code == UriValidationCode.URI_WITH_INTERPOLATION) { | |
| 347 _getErrorReporter(file).reportErrorForNode( | |
| 348 CompileTimeErrorCode.URI_WITH_INTERPOLATION, uriLiteral); | |
| 349 return null; | |
| 350 } else if (code == UriValidationCode.INVALID_URI) { | |
| 351 _getErrorReporter(file).reportErrorForNode( | |
| 352 CompileTimeErrorCode.INVALID_URI, uriLiteral, [uriContent]); | |
| 353 return null; | |
| 354 } | |
| 355 return null; | |
| 356 } | |
| 357 | |
| 358 void _resolveUriBasedDirectives(FileState file, CompilationUnit unit) { | |
| 359 for (Directive directive in unit.directives) { | |
| 360 if (directive is UriBasedDirective) { | |
| 361 StringLiteral uriLiteral = directive.uri; | |
| 362 String uriContent = uriLiteral.stringValue?.trim(); | |
| 363 directive.uriContent = uriContent; | |
| 364 Source defaultSource = _resolveUri( | |
| 365 file, directive is ImportDirective, uriLiteral, uriContent); | |
| 366 directive.uriSource = defaultSource; | |
| 367 } | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 /** | |
| 372 * Check the given [directive] to see if the referenced source exists and | |
| 373 * report an error if it does not. | |
| 374 */ | |
| 375 void _validateUriBasedDirective( | |
| 376 FileState file, UriBasedDirectiveImpl directive) { | |
| 377 Source source = directive.uriSource; | |
| 378 if (source != null) { | |
| 379 if (analysisContext.exists(source)) { | |
| 380 return; | |
| 381 } | |
| 382 } else { | |
| 383 // Don't report errors already reported by ParseDartTask.resolveDirective | |
| 384 if (directive.validate() != null) { | |
| 385 return; | |
| 386 } | |
| 387 } | |
| 388 StringLiteral uriLiteral = directive.uri; | |
| 389 CompileTimeErrorCode errorCode = CompileTimeErrorCode.URI_DOES_NOT_EXIST; | |
| 390 if (_isGenerated(source)) { | |
| 391 errorCode = CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED; | |
| 392 } | |
| 393 _getErrorReporter(file) | |
| 394 .reportErrorForNode(errorCode, uriLiteral, [directive.uriContent]); | |
| 395 } | |
| 396 | |
| 397 /** | |
| 398 * Check each directive in the given [unit] to see if the referenced source | |
| 399 * exists and report an error if it does not. | |
| 400 */ | |
| 401 void _validateUriBasedDirectives(FileState file, CompilationUnit unit) { | |
| 402 for (Directive directive in unit.directives) { | |
| 403 if (directive is UriBasedDirective) { | |
| 404 _validateUriBasedDirective(file, directive); | |
| 405 } | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 /** | |
| 410 * Return `true` if the given [source] refers to a file that is assumed to be | |
| 411 * generated. | |
| 412 */ | |
| 413 static bool _isGenerated(Source source) { | |
| 414 if (source == null) { | |
| 415 return false; | |
| 416 } | |
| 417 // TODO(brianwilkerson) Generalize this mechanism. | |
| 418 const List<String> suffixes = const <String>[ | |
| 419 '.g.dart', | |
| 420 '.pb.dart', | |
| 421 '.pbenum.dart', | |
| 422 '.pbserver.dart', | |
| 423 '.pbjson.dart', | |
| 424 '.template.dart' | |
| 425 ]; | |
| 426 String fullName = source.fullName; | |
| 427 for (String suffix in suffixes) { | |
| 428 if (fullName.endsWith(suffix)) { | |
| 429 return true; | |
| 430 } | |
| 431 } | |
| 432 return false; | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 /** | |
| 437 * Analysis result for single file. | |
| 438 */ | |
| 439 class UnitAnalysisResult { | |
| 440 final FileState file; | |
| 441 final CompilationUnit unit; | |
| 442 final List<AnalysisError> errors; | |
| 443 | |
| 444 UnitAnalysisResult(this.file, this.unit, this.errors); | |
| 445 } | |
| 446 | |
| 447 /** | |
| 448 * [ContentCache] wrapper around [FileContentOverlay]. | |
| 449 */ | |
| 450 class _ContentCacheWrapper implements ContentCache { | |
| 451 final FileSystemState fsState; | |
| 452 | |
| 453 _ContentCacheWrapper(this.fsState); | |
| 454 | |
| 455 @override | |
| 456 void accept(ContentCacheVisitor visitor) { | |
| 457 throw new UnimplementedError(); | |
| 458 } | |
| 459 | |
| 460 @override | |
| 461 String getContents(Source source) { | |
| 462 return _getFileForSource(source).content; | |
| 463 } | |
| 464 | |
| 465 @override | |
| 466 bool getExists(Source source) { | |
| 467 return _getFileForSource(source).exists; | |
| 468 } | |
| 469 | |
| 470 @override | |
| 471 int getModificationStamp(Source source) { | |
| 472 return _getFileForSource(source).exists ? 0 : -1; | |
| 473 } | |
| 474 | |
| 475 @override | |
| 476 String setContents(Source source, String contents) { | |
| 477 throw new UnimplementedError(); | |
| 478 } | |
| 479 | |
| 480 FileState _getFileForSource(Source source) { | |
| 481 String path = source.fullName; | |
| 482 return fsState.getFileForPath(path); | |
| 483 } | |
| 484 } | |
| OLD | NEW |