| 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/standard_resolution_map.dart'; | 10 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 import 'package:analyzer/src/generated/parser.dart'; | 36 import 'package:analyzer/src/generated/parser.dart'; |
| 37 import 'package:analyzer/src/generated/resolver.dart'; | 37 import 'package:analyzer/src/generated/resolver.dart'; |
| 38 import 'package:analyzer/src/generated/sdk.dart'; | 38 import 'package:analyzer/src/generated/sdk.dart'; |
| 39 import 'package:analyzer/src/generated/source.dart'; | 39 import 'package:analyzer/src/generated/source.dart'; |
| 40 import 'package:analyzer/src/generated/utilities_dart.dart'; | 40 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 41 import 'package:analyzer/src/plugin/engine_plugin.dart'; | 41 import 'package:analyzer/src/plugin/engine_plugin.dart'; |
| 42 import 'package:analyzer/src/services/lint.dart'; | 42 import 'package:analyzer/src/services/lint.dart'; |
| 43 import 'package:analyzer/src/task/driver.dart'; | 43 import 'package:analyzer/src/task/driver.dart'; |
| 44 import 'package:analyzer/src/task/general.dart'; | 44 import 'package:analyzer/src/task/general.dart'; |
| 45 import 'package:analyzer/src/task/html.dart'; | 45 import 'package:analyzer/src/task/html.dart'; |
| 46 import 'package:analyzer/src/task/incremental_element_builder.dart'; | |
| 47 import 'package:analyzer/src/task/inputs.dart'; | 46 import 'package:analyzer/src/task/inputs.dart'; |
| 48 import 'package:analyzer/src/task/model.dart'; | 47 import 'package:analyzer/src/task/model.dart'; |
| 49 import 'package:analyzer/src/task/strong/checker.dart'; | 48 import 'package:analyzer/src/task/strong/checker.dart'; |
| 50 import 'package:analyzer/src/task/strong_mode.dart'; | 49 import 'package:analyzer/src/task/strong_mode.dart'; |
| 51 import 'package:analyzer/task/dart.dart'; | 50 import 'package:analyzer/task/dart.dart'; |
| 52 import 'package:analyzer/task/general.dart'; | 51 import 'package:analyzer/task/general.dart'; |
| 53 import 'package:analyzer/task/model.dart'; | 52 import 'package:analyzer/task/model.dart'; |
| 54 | 53 |
| 55 /** | 54 /** |
| 56 * The [ResultCachingPolicy] for ASTs. | 55 * The [ResultCachingPolicy] for ASTs. |
| (...skipping 2402 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2459 * Create a [ContainingLibrariesTask] based on the given [target] in the given | 2458 * Create a [ContainingLibrariesTask] based on the given [target] in the given |
| 2460 * [context]. | 2459 * [context]. |
| 2461 */ | 2460 */ |
| 2462 static ContainingLibrariesTask createTask( | 2461 static ContainingLibrariesTask createTask( |
| 2463 AnalysisContext context, AnalysisTarget target) { | 2462 AnalysisContext context, AnalysisTarget target) { |
| 2464 return new ContainingLibrariesTask(context, target); | 2463 return new ContainingLibrariesTask(context, target); |
| 2465 } | 2464 } |
| 2466 } | 2465 } |
| 2467 | 2466 |
| 2468 /** | 2467 /** |
| 2469 * The description for a change in a Dart source. | |
| 2470 */ | |
| 2471 class DartDelta extends Delta { | |
| 2472 final Set<String> changedNames = new Set<String>(); | |
| 2473 final Map<Source, Set<String>> changedPrivateNames = <Source, Set<String>>{}; | |
| 2474 | |
| 2475 final Map<String, ClassElementDelta> changedClasses = | |
| 2476 <String, ClassElementDelta>{}; | |
| 2477 | |
| 2478 /** | |
| 2479 * The cache of libraries in which all results are invalid. | |
| 2480 */ | |
| 2481 final Set<Source> librariesWithAllInvalidResults = new Set<Source>(); | |
| 2482 | |
| 2483 /** | |
| 2484 * The cache of libraries in which all results are valid. | |
| 2485 */ | |
| 2486 final Set<Source> librariesWithAllValidResults = new Set<Source>(); | |
| 2487 | |
| 2488 /** | |
| 2489 * The cache of libraries with all, but [HINTS] and [VERIFY_ERRORS] results | |
| 2490 * are valid. | |
| 2491 */ | |
| 2492 final Set<Source> libraryWithInvalidErrors = new Set<Source>(); | |
| 2493 | |
| 2494 /** | |
| 2495 * This set is cleared in every [gatherEnd], and [gatherChanges] uses it | |
| 2496 * to find changes in every source only once per visit process. | |
| 2497 */ | |
| 2498 final Set<Source> currentVisitUnits = new Set<Source>(); | |
| 2499 | |
| 2500 DartDelta(Source source) : super(source); | |
| 2501 | |
| 2502 @override | |
| 2503 bool get shouldGatherChanges => true; | |
| 2504 | |
| 2505 /** | |
| 2506 * Add names that are changed in the given [references]. | |
| 2507 * Return `true` if any change was added. | |
| 2508 */ | |
| 2509 bool addChangedElements(ReferencedNames references, Source refLibrary) { | |
| 2510 int numberOfChanges = 0; | |
| 2511 int lastNumberOfChange = -1; | |
| 2512 while (numberOfChanges != lastNumberOfChange) { | |
| 2513 lastNumberOfChange = numberOfChanges; | |
| 2514 // Classes that extend changed classes are also changed. | |
| 2515 // If there is a delta for a superclass, use it for the subclass. | |
| 2516 // Otherwise mark the subclass as "general name change". | |
| 2517 references.superToSubs.forEach((String superName, Set<String> subNames) { | |
| 2518 ClassElementDelta superDelta = changedClasses[superName]; | |
| 2519 for (String subName in subNames) { | |
| 2520 if (superDelta != null) { | |
| 2521 ClassElementDelta subDelta = changedClasses.putIfAbsent(subName, | |
| 2522 () => new ClassElementDelta(null, refLibrary, subName)); | |
| 2523 _log(() => '$subName in $refLibrary has delta because of its ' | |
| 2524 'superclass $superName has delta'); | |
| 2525 if (subDelta.superDeltas.add(superDelta)) { | |
| 2526 numberOfChanges++; | |
| 2527 } | |
| 2528 } else if (isChanged(refLibrary, superName)) { | |
| 2529 if (nameChanged(refLibrary, subName)) { | |
| 2530 _log(() => '$subName in $refLibrary is changed because its ' | |
| 2531 'superclass $superName is changed'); | |
| 2532 numberOfChanges++; | |
| 2533 } | |
| 2534 } | |
| 2535 } | |
| 2536 }); | |
| 2537 // If a user element uses a changed top-level element, then the user is | |
| 2538 // also changed. Note that if a changed class with delta is used, this | |
| 2539 // does not make the user changed - classes with delta keep their | |
| 2540 // original elements, so resolution of their names does not change. | |
| 2541 references.userToDependsOn.forEach((user, dependencies) { | |
| 2542 for (String dependency in dependencies) { | |
| 2543 if (isChangedOrClassMember(refLibrary, dependency)) { | |
| 2544 if (nameChanged(refLibrary, user)) { | |
| 2545 _log(() => '$user in $refLibrary is changed because ' | |
| 2546 'of $dependency in $dependencies'); | |
| 2547 numberOfChanges++; | |
| 2548 } | |
| 2549 } | |
| 2550 } | |
| 2551 }); | |
| 2552 } | |
| 2553 return numberOfChanges != 0; | |
| 2554 } | |
| 2555 | |
| 2556 void classChanged(ClassElementDelta classDelta) { | |
| 2557 changedClasses[classDelta.name] = classDelta; | |
| 2558 } | |
| 2559 | |
| 2560 void elementChanged(Element element) { | |
| 2561 Source librarySource = element.library.source; | |
| 2562 nameChanged(librarySource, element.name); | |
| 2563 } | |
| 2564 | |
| 2565 @override | |
| 2566 bool gatherChanges(InternalAnalysisContext context, AnalysisTarget target, | |
| 2567 ResultDescriptor descriptor, Object value) { | |
| 2568 // Prepare target source. | |
| 2569 Source targetUnit = target.source; | |
| 2570 Source targetLibrary = target.librarySource; | |
| 2571 if (target is Source) { | |
| 2572 if (context.getKindOf(target) == SourceKind.LIBRARY) { | |
| 2573 targetLibrary = target; | |
| 2574 } | |
| 2575 } | |
| 2576 // We don't know what to do with the given target. | |
| 2577 if (targetUnit == null || targetUnit != targetLibrary) { | |
| 2578 return false; | |
| 2579 } | |
| 2580 // Attempt to find new changed names for the unit only once. | |
| 2581 if (!currentVisitUnits.add(targetUnit)) { | |
| 2582 return false; | |
| 2583 } | |
| 2584 // Add changes. | |
| 2585 ReferencedNames referencedNames = | |
| 2586 context.getResult(targetUnit, REFERENCED_NAMES); | |
| 2587 if (referencedNames == null) { | |
| 2588 return false; | |
| 2589 } | |
| 2590 return addChangedElements(referencedNames, targetLibrary); | |
| 2591 } | |
| 2592 | |
| 2593 @override | |
| 2594 void gatherEnd() { | |
| 2595 currentVisitUnits.clear(); | |
| 2596 } | |
| 2597 | |
| 2598 bool hasAffectedHintsVerifyErrors( | |
| 2599 ReferencedNames references, Source refLibrary) { | |
| 2600 for (String superName in references.superToSubs.keys) { | |
| 2601 if (isChangedOrClass(refLibrary, superName)) { | |
| 2602 _log(() => '$refLibrary hints/verify errors are affected because ' | |
| 2603 '${references.superToSubs[superName]} subclasses $superName'); | |
| 2604 return true; | |
| 2605 } | |
| 2606 } | |
| 2607 for (String name in references.names) { | |
| 2608 ClassElementDelta classDelta = changedClasses[name]; | |
| 2609 if (classDelta != null && classDelta.hasAnnotationChanges) { | |
| 2610 _log(() => '$refLibrary hints/verify errors are affected because ' | |
| 2611 '$name has a class delta with annotation changes'); | |
| 2612 return true; | |
| 2613 } | |
| 2614 } | |
| 2615 return false; | |
| 2616 } | |
| 2617 | |
| 2618 bool hasAffectedReferences(ReferencedNames references, Source refLibrary) { | |
| 2619 // Resolution must be performed when a referenced element changes. | |
| 2620 for (String name in references.names) { | |
| 2621 if (isChangedOrClassMember(refLibrary, name)) { | |
| 2622 _log(() => '$refLibrary is affected by $name'); | |
| 2623 return true; | |
| 2624 } | |
| 2625 } | |
| 2626 // Resolution must be performed when the unnamed constructor of | |
| 2627 // an instantiated class is added/changed/removed. | |
| 2628 // TODO(scheglov) Use only instantiations with default constructor. | |
| 2629 for (String name in references.instantiatedNames) { | |
| 2630 for (ClassElementDelta classDelta in changedClasses.values) { | |
| 2631 if (classDelta.name == name && classDelta.hasUnnamedConstructorChange) { | |
| 2632 _log(() => | |
| 2633 '$refLibrary is affected by the default constructor of $name'); | |
| 2634 return true; | |
| 2635 } | |
| 2636 } | |
| 2637 } | |
| 2638 for (String name in references.extendedUsedUnnamedConstructorNames) { | |
| 2639 for (ClassElementDelta classDelta in changedClasses.values) { | |
| 2640 if (classDelta.name == name && classDelta.hasUnnamedConstructorChange) { | |
| 2641 _log(() => | |
| 2642 '$refLibrary is affected by the default constructor of $name'); | |
| 2643 return true; | |
| 2644 } | |
| 2645 } | |
| 2646 } | |
| 2647 return false; | |
| 2648 } | |
| 2649 | |
| 2650 /** | |
| 2651 * Return `true` if the given [name], used in a unit of the [librarySource], | |
| 2652 * is affected by a changed top-level element, excluding classes. | |
| 2653 */ | |
| 2654 bool isChanged(Source librarySource, String name) { | |
| 2655 if (_isPrivateName(name)) { | |
| 2656 if (changedPrivateNames[librarySource]?.contains(name) ?? false) { | |
| 2657 return true; | |
| 2658 } | |
| 2659 } | |
| 2660 return changedNames.contains(name); | |
| 2661 } | |
| 2662 | |
| 2663 /** | |
| 2664 * Return `true` if the given [name], used in a unit of the [librarySource], | |
| 2665 * is affected by a changed top-level element or a class. | |
| 2666 */ | |
| 2667 bool isChangedOrClass(Source librarySource, String name) { | |
| 2668 if (isChanged(librarySource, name)) { | |
| 2669 return true; | |
| 2670 } | |
| 2671 return changedClasses[name] != null; | |
| 2672 } | |
| 2673 | |
| 2674 /** | |
| 2675 * Return `true` if the given [name], used in a unit of the [librarySource], | |
| 2676 * is affected by a changed top-level element or a class member. | |
| 2677 */ | |
| 2678 bool isChangedOrClassMember(Source librarySource, String name) { | |
| 2679 if (isChanged(librarySource, name)) { | |
| 2680 return true; | |
| 2681 } | |
| 2682 // TODO(scheglov) Optimize this. | |
| 2683 for (ClassElementDelta classDelta in changedClasses.values) { | |
| 2684 if (classDelta.hasChanges(librarySource, name)) { | |
| 2685 return true; | |
| 2686 } | |
| 2687 } | |
| 2688 return false; | |
| 2689 } | |
| 2690 | |
| 2691 /** | |
| 2692 * Register the fact that the given [name], defined in the [librarySource] | |
| 2693 * is changed. Return `true` if the [name] is a new name, not yet registered. | |
| 2694 */ | |
| 2695 bool nameChanged(Source librarySource, String name) { | |
| 2696 if (_isPrivateName(name)) { | |
| 2697 return changedPrivateNames | |
| 2698 .putIfAbsent(librarySource, () => new Set<String>()) | |
| 2699 .add(name); | |
| 2700 } else { | |
| 2701 return changedNames.add(name); | |
| 2702 } | |
| 2703 } | |
| 2704 | |
| 2705 @override | |
| 2706 DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target, | |
| 2707 ResultDescriptor descriptor, Object value) { | |
| 2708 // Always invalidate compounding results. | |
| 2709 if (descriptor == LIBRARY_ELEMENT4 || | |
| 2710 descriptor == READY_LIBRARY_ELEMENT6 || | |
| 2711 descriptor == READY_LIBRARY_ELEMENT7) { | |
| 2712 return DeltaResult.INVALIDATE_KEEP_DEPENDENCIES; | |
| 2713 } | |
| 2714 // Prepare target source. | |
| 2715 Source targetUnit = target.source; | |
| 2716 Source targetLibrary = target.librarySource; | |
| 2717 if (target is Source) { | |
| 2718 if (context.getKindOf(target) == SourceKind.LIBRARY) { | |
| 2719 targetLibrary = target; | |
| 2720 } | |
| 2721 } | |
| 2722 // We don't know what to do with the given target, invalidate it. | |
| 2723 if (targetUnit == null || targetUnit != targetLibrary) { | |
| 2724 return DeltaResult.INVALIDATE; | |
| 2725 } | |
| 2726 // Keep results that don't change: any library. | |
| 2727 if (_isTaskResult(ScanDartTask.DESCRIPTOR, descriptor) || | |
| 2728 _isTaskResult(ParseDartTask.DESCRIPTOR, descriptor) || | |
| 2729 _isTaskResult(BuildCompilationUnitElementTask.DESCRIPTOR, descriptor) || | |
| 2730 _isTaskResult(BuildLibraryElementTask.DESCRIPTOR, descriptor) || | |
| 2731 _isTaskResult(BuildDirectiveElementsTask.DESCRIPTOR, descriptor) || | |
| 2732 _isTaskResult(ResolveDirectiveElementsTask.DESCRIPTOR, descriptor) || | |
| 2733 _isTaskResult(BuildEnumMemberElementsTask.DESCRIPTOR, descriptor) || | |
| 2734 _isTaskResult(BuildSourceExportClosureTask.DESCRIPTOR, descriptor) || | |
| 2735 _isTaskResult(ReadyLibraryElement2Task.DESCRIPTOR, descriptor) || | |
| 2736 _isTaskResult(ComputeLibraryCycleTask.DESCRIPTOR, descriptor)) { | |
| 2737 return DeltaResult.KEEP_CONTINUE; | |
| 2738 } | |
| 2739 // Keep results that don't change: changed library. | |
| 2740 if (targetUnit == source) { | |
| 2741 return DeltaResult.INVALIDATE; | |
| 2742 } | |
| 2743 // Keep results that don't change: dependent library. | |
| 2744 if (targetUnit != source) { | |
| 2745 if (_isTaskResult(BuildPublicNamespaceTask.DESCRIPTOR, descriptor)) { | |
| 2746 return DeltaResult.KEEP_CONTINUE; | |
| 2747 } | |
| 2748 } | |
| 2749 // Handle in-library results only for now. | |
| 2750 if (targetLibrary != null) { | |
| 2751 // Use cached library results. | |
| 2752 if (librariesWithAllInvalidResults.contains(targetLibrary)) { | |
| 2753 return DeltaResult.INVALIDATE; | |
| 2754 } | |
| 2755 if (librariesWithAllValidResults.contains(targetLibrary)) { | |
| 2756 return DeltaResult.KEEP_CONTINUE; | |
| 2757 } | |
| 2758 // The library is almost, but not completely valid. | |
| 2759 // Some error results are invalid. | |
| 2760 if (libraryWithInvalidErrors.contains(targetLibrary)) { | |
| 2761 if (descriptor == HINTS || descriptor == VERIFY_ERRORS) { | |
| 2762 return DeltaResult.INVALIDATE_NO_DELTA; | |
| 2763 } | |
| 2764 return DeltaResult.KEEP_CONTINUE; | |
| 2765 } | |
| 2766 // Compute the library result. | |
| 2767 ReferencedNames referencedNames = | |
| 2768 context.getResult(targetUnit, REFERENCED_NAMES); | |
| 2769 if (referencedNames == null) { | |
| 2770 return DeltaResult.INVALIDATE_NO_DELTA; | |
| 2771 } | |
| 2772 if (hasAffectedReferences(referencedNames, targetLibrary)) { | |
| 2773 librariesWithAllInvalidResults.add(targetLibrary); | |
| 2774 return DeltaResult.INVALIDATE; | |
| 2775 } | |
| 2776 if (hasAffectedHintsVerifyErrors(referencedNames, targetLibrary)) { | |
| 2777 libraryWithInvalidErrors.add(targetLibrary); | |
| 2778 return DeltaResult.KEEP_CONTINUE; | |
| 2779 } | |
| 2780 librariesWithAllValidResults.add(targetLibrary); | |
| 2781 return DeltaResult.KEEP_CONTINUE; | |
| 2782 } | |
| 2783 // We don't know what to do with the given target, invalidate it. | |
| 2784 return DeltaResult.INVALIDATE; | |
| 2785 } | |
| 2786 | |
| 2787 void _log(String getMessage()) { | |
| 2788 // String message = getMessage(); | |
| 2789 // print(message); | |
| 2790 } | |
| 2791 | |
| 2792 static bool _isPrivateName(String name) => name.startsWith('_'); | |
| 2793 | |
| 2794 static bool _isTaskResult( | |
| 2795 TaskDescriptor taskDescriptor, ResultDescriptor result) { | |
| 2796 return taskDescriptor.results.contains(result); | |
| 2797 } | |
| 2798 } | |
| 2799 | |
| 2800 /** | |
| 2801 * A task that merges all of the errors for a single source into a single list | 2468 * A task that merges all of the errors for a single source into a single list |
| 2802 * of errors. | 2469 * of errors. |
| 2803 */ | 2470 */ |
| 2804 class DartErrorsTask extends SourceBasedAnalysisTask { | 2471 class DartErrorsTask extends SourceBasedAnalysisTask { |
| 2805 /** | 2472 /** |
| 2806 * The task descriptor describing this kind of task. | 2473 * The task descriptor describing this kind of task. |
| 2807 */ | 2474 */ |
| 2808 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('DartErrorsTask', | 2475 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('DartErrorsTask', |
| 2809 createTask, buildInputs, <ResultDescriptor>[DART_ERRORS]); | 2476 createTask, buildInputs, <ResultDescriptor>[DART_ERRORS]); |
| 2810 | 2477 |
| (...skipping 3775 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6586 | 6253 |
| 6587 @override | 6254 @override |
| 6588 bool moveNext() { | 6255 bool moveNext() { |
| 6589 if (_newSources.isEmpty) { | 6256 if (_newSources.isEmpty) { |
| 6590 return false; | 6257 return false; |
| 6591 } | 6258 } |
| 6592 currentTarget = _newSources.removeLast(); | 6259 currentTarget = _newSources.removeLast(); |
| 6593 return true; | 6260 return true; |
| 6594 } | 6261 } |
| 6595 } | 6262 } |
| OLD | NEW |