Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.task.dart; | 5 library analyzer.src.task.dart; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| (...skipping 2502 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2513 // We don't know what to do with the given target, invalidate it. | 2513 // We don't know what to do with the given target, invalidate it. |
| 2514 return DeltaResult.INVALIDATE; | 2514 return DeltaResult.INVALIDATE; |
| 2515 } | 2515 } |
| 2516 } | 2516 } |
| 2517 | 2517 |
| 2518 /** | 2518 /** |
| 2519 * A task that merges all of the errors for a single source into a single list | 2519 * A task that merges all of the errors for a single source into a single list |
| 2520 * of errors. | 2520 * of errors. |
| 2521 */ | 2521 */ |
| 2522 class DartErrorsTask extends SourceBasedAnalysisTask { | 2522 class DartErrorsTask extends SourceBasedAnalysisTask { |
| 2523 static final RegExp spacesRegExp = new RegExp(r'\s+'); | |
| 2524 | |
| 2525 /** | 2523 /** |
| 2526 * The task descriptor describing this kind of task. | 2524 * The task descriptor describing this kind of task. |
| 2527 */ | 2525 */ |
| 2528 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('DartErrorsTask', | 2526 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('DartErrorsTask', |
| 2529 createTask, buildInputs, <ResultDescriptor>[DART_ERRORS]); | 2527 createTask, buildInputs, <ResultDescriptor>[DART_ERRORS]); |
| 2530 | 2528 |
| 2531 /** | 2529 /** |
| 2530 * The name of the [IGNORE_INFO_INPUT] input. | |
| 2531 */ | |
| 2532 static const String IGNORE_INFO_INPUT = 'IGNORE_INFO_INPUT'; | |
| 2533 | |
| 2534 /** | |
| 2532 * The name of the [LINE_INFO_INPUT] input. | 2535 * The name of the [LINE_INFO_INPUT] input. |
| 2533 */ | 2536 */ |
| 2534 static const String LINE_INFO_INPUT = 'LINE_INFO_INPUT'; | 2537 static const String LINE_INFO_INPUT = 'LINE_INFO_INPUT'; |
| 2535 | 2538 |
| 2536 /** | |
| 2537 * The name of the [PARSED_UNIT_INPUT] input. | |
| 2538 */ | |
| 2539 static const String PARSED_UNIT_INPUT = 'PARSED_UNIT_INPUT'; | |
| 2540 | |
| 2541 // Prefix for comments ignoring error codes. | |
| 2542 static const String _normalizedIgnorePrefix = '//ignore:'; | |
| 2543 | |
| 2544 DartErrorsTask(InternalAnalysisContext context, AnalysisTarget target) | 2539 DartErrorsTask(InternalAnalysisContext context, AnalysisTarget target) |
| 2545 : super(context, target); | 2540 : super(context, target); |
| 2546 | 2541 |
| 2547 @override | 2542 @override |
| 2548 TaskDescriptor get descriptor => DESCRIPTOR; | 2543 TaskDescriptor get descriptor => DESCRIPTOR; |
| 2549 | 2544 |
| 2550 @override | 2545 @override |
| 2551 void internalPerform() { | 2546 void internalPerform() { |
| 2552 List<List<AnalysisError>> errorLists = <List<AnalysisError>>[]; | 2547 List<List<AnalysisError>> errorLists = <List<AnalysisError>>[]; |
| 2553 // | 2548 // |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 2577 // | 2572 // |
| 2578 List<AnalysisError> errors = | 2573 List<AnalysisError> errors = |
| 2579 _filterIgnores(AnalysisError.mergeLists(errorLists)); | 2574 _filterIgnores(AnalysisError.mergeLists(errorLists)); |
| 2580 | 2575 |
| 2581 // | 2576 // |
| 2582 // Record outputs. | 2577 // Record outputs. |
| 2583 // | 2578 // |
| 2584 outputs[DART_ERRORS] = errors; | 2579 outputs[DART_ERRORS] = errors; |
| 2585 } | 2580 } |
| 2586 | 2581 |
| 2587 Token _advanceToLine(Token token, LineInfo lineInfo, int line) { | |
| 2588 int offset = lineInfo.getOffsetOfLine(line - 1); // 0-based | |
| 2589 while (token.offset < offset) { | |
| 2590 token = token.next; | |
| 2591 } | |
| 2592 return token; | |
| 2593 } | |
| 2594 | |
| 2595 List<AnalysisError> _filterIgnores(List<AnalysisError> errors) { | 2582 List<AnalysisError> _filterIgnores(List<AnalysisError> errors) { |
| 2596 if (errors.isEmpty) { | 2583 if (errors.isEmpty) { |
| 2597 return errors; | 2584 return errors; |
| 2598 } | 2585 } |
| 2599 | 2586 |
| 2600 // Sort errors. | 2587 IgnoreInfo ignoreInfo = getRequiredInput(IGNORE_INFO_INPUT); |
| 2601 errors.sort((AnalysisError e1, AnalysisError e2) => e1.offset - e2.offset); | 2588 if (!ignoreInfo.hasIgnores) { |
| 2589 return errors; | |
| 2590 } | |
| 2602 | 2591 |
| 2603 CompilationUnit cu = getRequiredInput(PARSED_UNIT_INPUT); | |
| 2604 Token token = cu.beginToken; | |
| 2605 LineInfo lineInfo = getRequiredInput(LINE_INFO_INPUT); | 2592 LineInfo lineInfo = getRequiredInput(LINE_INFO_INPUT); |
| 2606 | 2593 |
| 2607 bool isIgnored(AnalysisError error) { | 2594 bool isIgnored(AnalysisError error) { |
| 2608 int errorLine = lineInfo.getLocation(error.offset).lineNumber; | 2595 int errorLine = lineInfo.getLocation(error.offset).lineNumber; |
| 2609 token = _advanceToLine(token, lineInfo, errorLine); | 2596 String errorCode = error.errorCode.name.toLowerCase(); |
| 2610 | 2597 // Ignores can be on the line or just preceding the error. |
| 2611 //Check for leading comment. | 2598 return ignoreInfo.ignoredAt(errorCode, errorLine) || |
| 2612 Token comments = token.precedingComments; | 2599 ignoreInfo.ignoredAt(errorCode, errorLine - 1); |
| 2613 while (comments?.next != null) { | |
| 2614 comments = comments.next; | |
| 2615 } | |
| 2616 if (_isIgnoredBy(error, comments)) { | |
| 2617 return true; | |
| 2618 } | |
| 2619 | |
| 2620 //Check for trailing comment. | |
| 2621 int lineNumber = errorLine + 1; | |
| 2622 if (lineNumber <= lineInfo.lineCount) { | |
| 2623 Token nextLine = _advanceToLine(token, lineInfo, lineNumber); | |
| 2624 comments = nextLine.precedingComments; | |
| 2625 if (comments != null && nextLine.previous.type != TokenType.EOF) { | |
| 2626 int commentLine = lineInfo.getLocation(comments.offset).lineNumber; | |
| 2627 if (commentLine == errorLine) { | |
| 2628 return _isIgnoredBy(error, comments); | |
| 2629 } | |
| 2630 } | |
| 2631 } | |
| 2632 | |
| 2633 return false; | |
| 2634 } | 2600 } |
| 2635 | 2601 |
| 2636 return errors.where((AnalysisError e) => !isIgnored(e)).toList(); | 2602 return errors.where((AnalysisError e) => !isIgnored(e)).toList(); |
| 2637 } | 2603 } |
| 2638 | 2604 |
| 2639 bool _isIgnoredBy(AnalysisError error, Token comment) { | |
| 2640 //Normalize first. | |
| 2641 String contents = | |
| 2642 comment?.lexeme?.toLowerCase()?.replaceAll(spacesRegExp, ''); | |
| 2643 if (contents == null || !contents.startsWith(_normalizedIgnorePrefix)) { | |
| 2644 return false; | |
| 2645 } | |
| 2646 return contents | |
| 2647 .substring(_normalizedIgnorePrefix.length) | |
| 2648 .split(',') | |
| 2649 .contains(error.errorCode.name.toLowerCase()); | |
| 2650 } | |
| 2651 | |
| 2652 /** | 2605 /** |
| 2653 * Return a map from the names of the inputs of this kind of task to the task | 2606 * Return a map from the names of the inputs of this kind of task to the task |
| 2654 * input descriptors describing those inputs for a task with the | 2607 * input descriptors describing those inputs for a task with the |
| 2655 * given [target]. | 2608 * given [target]. |
| 2656 */ | 2609 */ |
| 2657 static Map<String, TaskInput> buildInputs(AnalysisTarget target) { | 2610 static Map<String, TaskInput> buildInputs(AnalysisTarget target) { |
| 2658 Source source = target; | 2611 Source source = target; |
| 2659 Map<String, TaskInput> inputs = <String, TaskInput>{}; | 2612 Map<String, TaskInput> inputs = <String, TaskInput>{}; |
| 2660 inputs[LINE_INFO_INPUT] = LINE_INFO.of(source); | 2613 inputs[LINE_INFO_INPUT] = LINE_INFO.of(source); |
| 2661 inputs[PARSED_UNIT_INPUT] = PARSED_UNIT.of(source); | 2614 inputs[IGNORE_INFO_INPUT] = IGNORE_INFO.of(source); |
| 2662 EnginePlugin enginePlugin = AnalysisEngine.instance.enginePlugin; | 2615 EnginePlugin enginePlugin = AnalysisEngine.instance.enginePlugin; |
| 2663 // for Source | 2616 // for Source |
| 2664 List<ResultDescriptor> errorsForSource = enginePlugin.dartErrorsForSource; | 2617 List<ResultDescriptor> errorsForSource = enginePlugin.dartErrorsForSource; |
| 2665 int sourceLength = errorsForSource.length; | 2618 int sourceLength = errorsForSource.length; |
| 2666 for (int i = 0; i < sourceLength; i++) { | 2619 for (int i = 0; i < sourceLength; i++) { |
| 2667 ResultDescriptor result = errorsForSource[i]; | 2620 ResultDescriptor result = errorsForSource[i]; |
| 2668 String inputName = result.name + '_input'; | 2621 String inputName = result.name + '_input'; |
| 2669 inputs[inputName] = result.of(source); | 2622 inputs[inputName] = result.of(source); |
| 2670 } | 2623 } |
| 2671 // for LibrarySpecificUnit | 2624 // for LibrarySpecificUnit |
| (...skipping 2750 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5422 */ | 5375 */ |
| 5423 static const String MODIFICATION_TIME_INPUT = 'MODIFICATION_TIME_INPUT'; | 5376 static const String MODIFICATION_TIME_INPUT = 'MODIFICATION_TIME_INPUT'; |
| 5424 | 5377 |
| 5425 /** | 5378 /** |
| 5426 * The task descriptor describing this kind of task. | 5379 * The task descriptor describing this kind of task. |
| 5427 */ | 5380 */ |
| 5428 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( | 5381 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| 5429 'ScanDartTask', | 5382 'ScanDartTask', |
| 5430 createTask, | 5383 createTask, |
| 5431 buildInputs, | 5384 buildInputs, |
| 5432 <ResultDescriptor>[LINE_INFO, SCAN_ERRORS, TOKEN_STREAM], | 5385 <ResultDescriptor>[LINE_INFO, IGNORE_INFO, SCAN_ERRORS, TOKEN_STREAM], |
|
scheglov
2016/05/27 18:05:47
We try to keep these sorted.
IGNORE_INFO would be
pquitslund
2016/05/27 21:50:53
Done.
| |
| 5433 suitabilityFor: suitabilityFor); | 5386 suitabilityFor: suitabilityFor); |
| 5434 | 5387 |
| 5435 /** | 5388 /** |
| 5389 * A regular expression for matching 'ignore' comments. Produces matches | |
| 5390 * containing 3 groups. For example: | |
| 5391 * | |
| 5392 * * ['//ignore: error_code', '//ignore:', 'error_code'] | |
| 5393 * | |
| 5394 * Resulting codes may be in a list ('error_code_1,error_code2'). | |
| 5395 */ | |
| 5396 static final RegExp _IGNORE_MATCHER = | |
| 5397 new RegExp(r'(//[ ]*ignore:)(.*)$', multiLine: true); | |
|
scheglov
2016/05/27 18:05:47
Do we use the (//ignore:) group?
I don't know whet
pquitslund
2016/05/27 21:50:53
Ah! Yea. Great catch. No need for that first gr
| |
| 5398 | |
| 5399 /** | |
| 5436 * Initialize a newly created task to access the content of the source | 5400 * Initialize a newly created task to access the content of the source |
| 5437 * associated with the given [target] in the given [context]. | 5401 * associated with the given [target] in the given [context]. |
| 5438 */ | 5402 */ |
| 5439 ScanDartTask(InternalAnalysisContext context, AnalysisTarget target) | 5403 ScanDartTask(InternalAnalysisContext context, AnalysisTarget target) |
| 5440 : super(context, target); | 5404 : super(context, target); |
| 5441 | 5405 |
| 5442 @override | 5406 @override |
| 5443 TaskDescriptor get descriptor => DESCRIPTOR; | 5407 TaskDescriptor get descriptor => DESCRIPTOR; |
| 5444 | 5408 |
| 5445 @override | 5409 @override |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 5475 ScriptFragment fragment = fragments[0]; | 5439 ScriptFragment fragment = fragments[0]; |
| 5476 | 5440 |
| 5477 Scanner scanner = new Scanner( | 5441 Scanner scanner = new Scanner( |
| 5478 source, | 5442 source, |
| 5479 new SubSequenceReader(fragment.content, fragment.offset), | 5443 new SubSequenceReader(fragment.content, fragment.offset), |
| 5480 errorListener); | 5444 errorListener); |
| 5481 scanner.setSourceStart(fragment.line, fragment.column); | 5445 scanner.setSourceStart(fragment.line, fragment.column); |
| 5482 scanner.preserveComments = context.analysisOptions.preserveComments; | 5446 scanner.preserveComments = context.analysisOptions.preserveComments; |
| 5483 scanner.scanGenericMethodComments = context.analysisOptions.strongMode; | 5447 scanner.scanGenericMethodComments = context.analysisOptions.strongMode; |
| 5484 | 5448 |
| 5449 LineInfo lineInfo = new LineInfo(scanner.lineStarts); | |
| 5450 | |
| 5485 outputs[TOKEN_STREAM] = scanner.tokenize(); | 5451 outputs[TOKEN_STREAM] = scanner.tokenize(); |
| 5486 outputs[LINE_INFO] = new LineInfo(scanner.lineStarts); | 5452 outputs[LINE_INFO] = lineInfo; |
| 5453 outputs[IGNORE_INFO] = calculateIgnores(fragment.content, lineInfo); | |
| 5487 outputs[SCAN_ERRORS] = getUniqueErrors(errorListener.errors); | 5454 outputs[SCAN_ERRORS] = getUniqueErrors(errorListener.errors); |
| 5488 } else if (target is Source) { | 5455 } else if (target is Source) { |
| 5489 String content = getRequiredInput(CONTENT_INPUT_NAME); | 5456 String content = getRequiredInput(CONTENT_INPUT_NAME); |
| 5490 | 5457 |
| 5491 Scanner scanner = | 5458 Scanner scanner = |
| 5492 new Scanner(source, new CharSequenceReader(content), errorListener); | 5459 new Scanner(source, new CharSequenceReader(content), errorListener); |
| 5493 scanner.preserveComments = context.analysisOptions.preserveComments; | 5460 scanner.preserveComments = context.analysisOptions.preserveComments; |
| 5494 scanner.scanGenericMethodComments = context.analysisOptions.strongMode; | 5461 scanner.scanGenericMethodComments = context.analysisOptions.strongMode; |
| 5495 | 5462 |
| 5463 LineInfo lineInfo = new LineInfo(scanner.lineStarts); | |
| 5464 | |
| 5496 outputs[TOKEN_STREAM] = scanner.tokenize(); | 5465 outputs[TOKEN_STREAM] = scanner.tokenize(); |
| 5497 outputs[LINE_INFO] = new LineInfo(scanner.lineStarts); | 5466 outputs[LINE_INFO] = lineInfo; |
| 5467 outputs[IGNORE_INFO] = calculateIgnores(content, lineInfo); | |
| 5498 outputs[SCAN_ERRORS] = getUniqueErrors(errorListener.errors); | 5468 outputs[SCAN_ERRORS] = getUniqueErrors(errorListener.errors); |
| 5499 } else { | 5469 } else { |
| 5500 throw new AnalysisException( | 5470 throw new AnalysisException( |
| 5501 'Cannot scan Dart code from a ${target.runtimeType}'); | 5471 'Cannot scan Dart code from a ${target.runtimeType}'); |
| 5502 } | 5472 } |
| 5503 } | 5473 } |
| 5504 | 5474 |
| 5505 /** | 5475 /** |
| 5506 * Return a map from the names of the inputs of this kind of task to the task | 5476 * Return a map from the names of the inputs of this kind of task to the task |
| 5507 * input descriptors describing those inputs for a task with the given | 5477 * input descriptors describing those inputs for a task with the given |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 5521 return <String, TaskInput>{ | 5491 return <String, TaskInput>{ |
| 5522 '-': DART_SCRIPTS.of(source), | 5492 '-': DART_SCRIPTS.of(source), |
| 5523 MODIFICATION_TIME_INPUT: MODIFICATION_TIME.of(source) | 5493 MODIFICATION_TIME_INPUT: MODIFICATION_TIME.of(source) |
| 5524 }; | 5494 }; |
| 5525 } | 5495 } |
| 5526 throw new AnalysisException( | 5496 throw new AnalysisException( |
| 5527 'Cannot build inputs for a ${target.runtimeType}'); | 5497 'Cannot build inputs for a ${target.runtimeType}'); |
| 5528 } | 5498 } |
| 5529 | 5499 |
| 5530 /** | 5500 /** |
| 5501 * Calculate ignores for the given [content] with line [info]. | |
| 5502 */ | |
| 5503 static IgnoreInfo calculateIgnores(String content, LineInfo info) { | |
|
Brian Wilkerson
2016/05/27 18:21:18
This could be a static method on IgnoreInfo.
pquitslund
2016/05/27 21:50:53
Done.
| |
| 5504 IgnoreInfo ignoreInfo = new IgnoreInfo(); | |
| 5505 for (Match match in _IGNORE_MATCHER.allMatches(content)) { | |
| 5506 // Matches contain 3 groups: | |
| 5507 // ['//ignore: error_code', '//ignore:', 'error_code'] | |
| 5508 // And resulting codes may be in a list. | |
| 5509 List<String> codes = match.group(2).split(','); | |
| 5510 for (String code in codes) { | |
| 5511 ignoreInfo.add(info.getLocation(match.start).lineNumber, code.trim()); | |
|
Brian Wilkerson
2016/05/27 18:21:18
It would be more efficient to define LineInfo.addA
pquitslund
2016/05/27 21:50:53
Done.
| |
| 5512 } | |
| 5513 } | |
| 5514 return ignoreInfo; | |
|
Brian Wilkerson
2016/05/27 18:21:18
For memory efficiency, it would be good to use a s
pquitslund
2016/05/27 21:50:53
Good idea. Done
| |
| 5515 } | |
| 5516 | |
| 5517 /** | |
| 5531 * Create a [ScanDartTask] based on the given [target] in the given [context]. | 5518 * Create a [ScanDartTask] based on the given [target] in the given [context]. |
| 5532 */ | 5519 */ |
| 5533 static ScanDartTask createTask( | 5520 static ScanDartTask createTask( |
| 5534 AnalysisContext context, AnalysisTarget target) { | 5521 AnalysisContext context, AnalysisTarget target) { |
| 5535 return new ScanDartTask(context, target); | 5522 return new ScanDartTask(context, target); |
| 5536 } | 5523 } |
| 5537 | 5524 |
| 5538 /** | 5525 /** |
| 5539 * Return an indication of how suitable this task is for the given [target]. | 5526 * Return an indication of how suitable this task is for the given [target]. |
| 5540 */ | 5527 */ |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5899 | 5886 |
| 5900 @override | 5887 @override |
| 5901 bool moveNext() { | 5888 bool moveNext() { |
| 5902 if (_newSources.isEmpty) { | 5889 if (_newSources.isEmpty) { |
| 5903 return false; | 5890 return false; |
| 5904 } | 5891 } |
| 5905 currentTarget = _newSources.removeLast(); | 5892 currentTarget = _newSources.removeLast(); |
| 5906 return true; | 5893 return true; |
| 5907 } | 5894 } |
| 5908 } | 5895 } |
| OLD | NEW |