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 |