Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: pkg/analyzer/lib/src/generated/incremental_resolver.dart

Issue 770333003: First step to resolving simple API changes. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 engine.incremental_resolver; 5 library engine.incremental_resolver;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import 'dart:math' as math; 8 import 'dart:math' as math;
9 9
10 import 'ast.dart'; 10 import 'ast.dart';
11 import 'element.dart'; 11 import 'element.dart';
12 import 'engine.dart'; 12 import 'engine.dart';
13 import 'error.dart'; 13 import 'error.dart';
14 import 'error_verifier.dart'; 14 import 'error_verifier.dart';
15 import 'incremental_logger.dart' show logger, LoggingTimer; 15 import 'incremental_logger.dart' show logger, LoggingTimer;
16 import 'java_engine.dart'; 16 import 'java_engine.dart';
17 import 'parser.dart'; 17 import 'parser.dart';
18 import 'resolver.dart'; 18 import 'resolver.dart';
19 import 'scanner.dart'; 19 import 'scanner.dart';
20 import 'source.dart'; 20 import 'source.dart';
21 import 'utilities_collection.dart';
21 import 'utilities_dart.dart'; 22 import 'utilities_dart.dart';
22 23
23 24
24 /** 25 /**
25 * If `true`, an attempt to resolve API-changing modifications is made. 26 * If `true`, an attempt to resolve API-changing modifications is made.
26 */ 27 */
27 bool _resolveApiChanges = false; 28 bool _resolveApiChanges = false;
28 29
29 30
30 /** 31 /**
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 } 112 }
112 // no API changes 113 // no API changes
113 if (_removedElements.isEmpty && _addedElements.isEmpty) { 114 if (_removedElements.isEmpty && _addedElements.isEmpty) {
114 return DeclarationMatchKind.MATCH; 115 return DeclarationMatchKind.MATCH;
115 } 116 }
116 // simple API change 117 // simple API change
117 if (_removedElements.length <= 1 && _addedElements.length == 1) { 118 if (_removedElements.length <= 1 && _addedElements.length == 1) {
118 return DeclarationMatchKind.MISMATCH_OK; 119 return DeclarationMatchKind.MISMATCH_OK;
119 } 120 }
120 // something more complex 121 // something more complex
122 logger.log('_removedElements: $_removedElements');
123 logger.log('_addedElements: $_addedElements');
121 return DeclarationMatchKind.MISMATCH; 124 return DeclarationMatchKind.MISMATCH;
122 } 125 }
123 126
124 @override 127 @override
125 visitBlockFunctionBody(BlockFunctionBody node) { 128 visitBlockFunctionBody(BlockFunctionBody node) {
126 // ignore bodies 129 // ignore bodies
127 } 130 }
128 131
129 @override 132 @override
130 visitClassDeclaration(ClassDeclaration node) { 133 visitClassDeclaration(ClassDeclaration node) {
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 if (type == null) { 517 if (type == null) {
515 return _assertTrue(false); 518 return _assertTrue(false);
516 } 519 }
517 // prepare name 520 // prepare name
518 Identifier nameIdentifier = node.name; 521 Identifier nameIdentifier = node.name;
519 if (nameIdentifier is PrefixedIdentifier) { 522 if (nameIdentifier is PrefixedIdentifier) {
520 nameIdentifier = (nameIdentifier as PrefixedIdentifier).identifier; 523 nameIdentifier = (nameIdentifier as PrefixedIdentifier).identifier;
521 } 524 }
522 String nodeName = nameIdentifier.name; 525 String nodeName = nameIdentifier.name;
523 // check specific type kinds 526 // check specific type kinds
524 if (type is InterfaceType) { 527 if (type is ParameterizedType) {
525 _assertEquals(nodeName, type.name); 528 _assertEquals(nodeName, type.name);
526 // check arguments 529 // check arguments
527 TypeArgumentList nodeArgumentList = node.typeArguments; 530 TypeArgumentList nodeArgumentList = node.typeArguments;
528 List<DartType> typeArguments = type.typeArguments; 531 List<DartType> typeArguments = type.typeArguments;
529 if (nodeArgumentList == null) { 532 if (nodeArgumentList == null) {
530 // Node doesn't have type arguments, so all type arguments of the 533 // Node doesn't have type arguments, so all type arguments of the
531 // element must be "dynamic". 534 // element must be "dynamic".
532 for (DartType typeArgument in typeArguments) { 535 for (DartType typeArgument in typeArguments) {
533 _assertTrue(typeArgument.isDynamic); 536 _assertTrue(typeArgument.isDynamic);
534 } 537 }
535 } else { 538 } else {
536 List<TypeName> nodeArguments = nodeArgumentList.arguments; 539 List<TypeName> nodeArguments = nodeArgumentList.arguments;
537 _assertSameTypes(nodeArguments, typeArguments); 540 _assertSameTypes(nodeArguments, typeArguments);
538 } 541 }
539 } else if (type is TypeParameterType) { 542 } else if (type is TypeParameterType) {
540 _assertEquals(nodeName, type.name); 543 _assertEquals(nodeName, type.name);
541 // TODO(scheglov) it should be possible to rename type parameters 544 // TODO(scheglov) it should be possible to rename type parameters
542 } else if (type.isVoid) { 545 } else if (type.isVoid) {
543 _assertEquals(nodeName, 'void'); 546 _assertEquals(nodeName, 'void');
544 } else if (type.isDynamic) { 547 } else if (type.isDynamic) {
545 _assertEquals(nodeName, 'dynamic'); 548 _assertEquals(nodeName, 'dynamic');
546 } else { 549 } else {
547 // TODO(scheglov) support other types 550 // TODO(scheglov) support other types
548 // print('node: $node type: $type type.type: ${type.runtimeType}'); 551 logger.log('node: $node type: $type type.type: ${type.runtimeType}');
549 _assertTrue(false); 552 _assertTrue(false);
550 } 553 }
551 } 554 }
552 555
553 void _assertSameTypeParameter(TypeParameter node, 556 void _assertSameTypeParameter(TypeParameter node,
554 TypeParameterElement element) { 557 TypeParameterElement element) {
555 _assertSameType(node.bound, element.bound); 558 _assertSameType(node.bound, element.bound);
556 } 559 }
557 560
558 void _assertSameTypeParameters(TypeParameterList nodesList, 561 void _assertSameTypeParameters(TypeParameterList nodesList,
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 final int _updateNewLength; 761 final int _updateNewLength;
759 762
760 RecordingErrorListener errorListener = new RecordingErrorListener(); 763 RecordingErrorListener errorListener = new RecordingErrorListener();
761 ResolutionContext _resolutionContext; 764 ResolutionContext _resolutionContext;
762 765
763 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; 766 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
764 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; 767 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
765 List<AnalysisError> _hints = AnalysisError.NO_ERRORS; 768 List<AnalysisError> _hints = AnalysisError.NO_ERRORS;
766 769
767 /** 770 /**
771 * The elements that should be resolved because of API changes.
772 */
773 HashSet<Element> _resolutionQueue = new HashSet<Element>();
Brian Wilkerson 2014/12/05 23:05:57 I have some concerns about how long the incrementa
scheglov 2014/12/05 23:49:05 Done.
774
775 /**
768 * Initialize a newly created incremental resolver to resolve a node in the 776 * Initialize a newly created incremental resolver to resolve a node in the
769 * given source in the given library. 777 * given source in the given library.
770 */ 778 */
771 IncrementalResolver(this._typeProvider, this._definingUnit, 779 IncrementalResolver(this._typeProvider, this._definingUnit,
772 this._updateOffset, this._updateOldLength, this._updateNewLength) { 780 this._updateOffset, this._updateOldLength, this._updateNewLength) {
773 _definingLibrary = _definingUnit.library; 781 _definingLibrary = _definingUnit.library;
774 _source = _definingUnit.source; 782 _source = _definingUnit.source;
775 } 783 }
776 784
777 /** 785 /**
778 * Resolve [node], reporting any errors or warnings to the given listener. 786 * Resolve [node], reporting any errors or warnings to the given listener.
779 * 787 *
780 * [node] - the root of the AST structure to be resolved. 788 * [node] - the root of the AST structure to be resolved.
781 * 789 *
782 * Returns `true` if resolution was successful. 790 * Returns `true` if resolution was successful.
783 */ 791 */
784 bool resolve(AstNode node) { 792 bool resolve(AstNode node) {
785 logger.enter('resolve: $_definingUnit'); 793 logger.enter('resolve: $_definingUnit');
786 try { 794 try {
787 logger.log(() => 'node: $node'); 795 logger.log(() => 'node: $node');
788 AstNode rootNode = _findResolutionRoot(node); 796 AstNode rootNode = _findResolutionRoot(node);
789 logger.log(() => 'rootNode: $rootNode'); 797 logger.log(() => 'rootNode: $rootNode');
790 _prepareResolutionContext(rootNode); 798 _prepareResolutionContext(rootNode);
791 // update elements 799 // update elements
792 _updateElementNameOffsets( 800 _updateElementNameOffsets(
793 _definingUnit, 801 _definingUnit,
794 _updateOffset, 802 _updateOffset,
795 _updateNewLength - _updateOldLength); 803 _updateNewLength - _updateOldLength);
796 _buildElements(rootNode); 804 _buildElements(rootNode);
797 if (_elementModelChanged(rootNode)) { 805 if (!_canBeIncrementallyResolved(rootNode)) {
798 return false; 806 return false;
799 } 807 }
800 // resolve 808 // resolve
801 _resolveReferences(rootNode); 809 _resolveReferences(rootNode);
802 // verify 810 // verify
803 _verify(rootNode); 811 _verify(rootNode);
804 _generateHints(rootNode); 812 _generateHints(rootNode);
813 // resolve queue in response of API changes
814 _resolveQueue();
805 // OK 815 // OK
806 return true; 816 return true;
807 } finally { 817 } finally {
808 logger.exit(); 818 logger.exit();
809 } 819 }
810 } 820 }
811 821
812 void _buildElements(AstNode node) { 822 void _buildElements(AstNode node) {
813 LoggingTimer timer = logger.startTimer(); 823 LoggingTimer timer = logger.startTimer();
814 try { 824 try {
815 ElementHolder holder = new ElementHolder(); 825 ElementHolder holder = new ElementHolder();
816 ElementBuilder builder = new ElementBuilder(holder); 826 ElementBuilder builder = new ElementBuilder(holder);
817 node.accept(builder); 827 node.accept(builder);
818 } finally { 828 } finally {
819 timer.stop('build elements'); 829 timer.stop('build elements');
820 } 830 }
821 } 831 }
822 832
823 /** 833 /**
834 * Return `true` if [node] does not have element model changes, or these
835 * changes can be incrementally propagated.
836 */
837 bool _canBeIncrementallyResolved(AstNode node) {
838 // If we are replacing the whole declaration, this means that its signature
839 // is changed. It might be an API change, or not.
840 //
841 // If, for example, a required parameter is changed, it is not an API
842 // change, but we want to find the existing corresponding Element in the
843 // enclosing one, set it for the node and update as needed.
844 //
845 // If, for example, the name of a method is changed, it is an API change,
846 // we need to know the old Element and the new Element. Again, we need to
847 // check the whole enclosing Element.
848 if (node is Declaration) {
849 node = node.parent;
850 }
851 Element element = _getElement(node);
852 DeclarationMatcher matcher = new DeclarationMatcher();
853 DeclarationMatchKind matchKind = matcher.matches(node, element);
854 if (matchKind == DeclarationMatchKind.MATCH) {
855 return true;
856 }
857 // try to resolve a simple API change
858 if (_resolveApiChanges && matchKind == DeclarationMatchKind.MISMATCH_OK) {
859 _fillResolutionQueue(matcher);
860 return true;
861 }
862 // mismatch that cannot be incrementally fixed
863 return false;
864 }
865
866 /**
824 * Return `true` if the given node can be resolved independently of any other 867 * Return `true` if the given node can be resolved independently of any other
825 * nodes. 868 * nodes.
826 * 869 *
827 * *Note*: This method needs to be kept in sync with 870 * *Note*: This method needs to be kept in sync with
828 * [ScopeBuilder.ContextBuilder]. 871 * [ScopeBuilder.ContextBuilder].
829 * 872 *
830 * [node] - the node being tested. 873 * [node] - the node being tested.
831 */ 874 */
832 bool _canBeResolved(AstNode node) => 875 bool _canBeResolved(AstNode node) =>
833 node is ClassDeclaration || 876 node is ClassDeclaration ||
834 node is ClassTypeAlias || 877 node is ClassTypeAlias ||
835 node is CompilationUnit || 878 node is CompilationUnit ||
836 node is ConstructorDeclaration || 879 node is ConstructorDeclaration ||
837 node is FunctionDeclaration || 880 node is FunctionDeclaration ||
838 node is FunctionTypeAlias || 881 node is FunctionTypeAlias ||
839 node is MethodDeclaration; 882 node is MethodDeclaration;
840 883
841 /** 884 void _fillResolutionQueue(DeclarationMatcher matcher) {
842 * Return `true` if the portion of the element model defined by the given node 885 for (Element removedElement in matcher._removedElements) {
843 * has changed. 886 AnalysisContextImpl context = removedElement.context;
844 * 887 IntSet users = removedElement.users;
845 * [node] - the node defining the portion of the element model being tested. 888 while (!users.isEmpty) {
846 * 889 int id = users.remove();
847 * Throws [AnalysisException] if the correctness of the element model cannot 890 Element removedElementUser = context.findElementById(id);
848 * be determined. 891 _resolutionQueue.add(removedElementUser);
849 */ 892 }
850 bool _elementModelChanged(AstNode node) {
851 // If we are replacing the whole declaration, this means that its signature
852 // is changed. It might be an API change, or not.
853 //
854 // If, for example, a required parameter is changed, it is not an API
855 // change, but we want to find the existing corresponding Element in the
856 // enclosing one, set it for the node and update as needed.
857 //
858 // If, for example, the name of a method is changed, it is an API change,
859 // we need to know the old Element and the new Element. Again, we need to
860 // check the whole enclosing Element.
861 if (node is Declaration) {
862 node = node.parent;
863 } 893 }
864 Element element = _getElement(node); 894 // TODO(scheglov) a method change might also require its class, and
865 if (element == null) { 895 // subclasses resolution
866 throw new AnalysisException(
867 "Cannot resolve node: a ${node.runtimeType} does not define an element ");
868 }
869 DeclarationMatcher matcher = new DeclarationMatcher();
870 return matcher.matches(node, element) != DeclarationMatchKind.MATCH;
871 } 896 }
872 897
873 /** 898 /**
874 * Starting at [node], find the smallest AST node that can be resolved 899 * Starting at [node], find the smallest AST node that can be resolved
875 * independently of any other nodes. Return the node that was found. 900 * independently of any other nodes. Return the node that was found.
876 * 901 *
877 * [node] - the node at which the search is to begin 902 * [node] - the node at which the search is to begin
878 * 903 *
879 * Throws [AnalysisException] if there is no such node. 904 * Throws [AnalysisException] if there is no such node.
880 */ 905 */
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
916 return null; 941 return null;
917 } 942 }
918 943
919 void _prepareResolutionContext(AstNode node) { 944 void _prepareResolutionContext(AstNode node) {
920 if (_resolutionContext == null) { 945 if (_resolutionContext == null) {
921 _resolutionContext = 946 _resolutionContext =
922 ResolutionContextBuilder.contextFor(node, errorListener); 947 ResolutionContextBuilder.contextFor(node, errorListener);
923 } 948 }
924 } 949 }
925 950
951 /**
952 * Resolves elements [_resolutionQueue].
953 *
954 * TODO(scheglov) work in progress
955 */
956 void _resolveQueue() {
957 for (Element element in _resolutionQueue) {
958 // TODO(scheglov) in general, we should not call Element.node, it
959 // might perform complete unit resolution.
960 AstNode node = element.node;
961 CompilationUnitElement unit =
962 element.getAncestor((e) => e is CompilationUnitElement);
963 IncrementalResolver resolver =
964 new IncrementalResolver(_typeProvider, unit, 0, 0, 0);
965 resolver._resolveReferences(node);
966 }
967 }
968
926 _resolveReferences(AstNode node) { 969 _resolveReferences(AstNode node) {
927 LoggingTimer timer = logger.startTimer(); 970 LoggingTimer timer = logger.startTimer();
928 try { 971 try {
929 _prepareResolutionContext(node); 972 _prepareResolutionContext(node);
930 Scope scope = _resolutionContext.scope; 973 Scope scope = _resolutionContext.scope;
931 // resolve types 974 // resolve types
932 { 975 {
933 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( 976 TypeResolverVisitor visitor = new TypeResolverVisitor.con3(
934 _definingLibrary, 977 _definingLibrary,
935 _source, 978 _source,
(...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after
1682 String toString() => name; 1725 String toString() => name;
1683 } 1726 }
1684 1727
1685 1728
1686 class _TokenPair { 1729 class _TokenPair {
1687 final _TokenDifferenceKind kind; 1730 final _TokenDifferenceKind kind;
1688 final Token oldToken; 1731 final Token oldToken;
1689 final Token newToken; 1732 final Token newToken;
1690 _TokenPair(this.kind, this.oldToken, this.newToken); 1733 _TokenPair(this.kind, this.oldToken, this.newToken);
1691 } 1734 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698