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

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

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library engine.incremental_resolver;
6
7 import 'dart:collection';
8 import 'dart:math' as math;
9
10 import 'package:analyzer/src/context/cache.dart'
11 show CacheEntry, TargetedResult;
12 import 'package:analyzer/src/generated/constant.dart';
13 import 'package:analyzer/src/task/dart.dart'
14 show
15 HINTS,
16 PARSE_ERRORS,
17 RESOLVE_REFERENCES_ERRORS,
18 RESOLVE_TYPE_NAMES_ERRORS,
19 SCAN_ERRORS,
20 USED_IMPORTED_ELEMENTS,
21 USED_LOCAL_ELEMENTS,
22 VARIABLE_REFERENCE_ERRORS,
23 VERIFY_ERRORS;
24 import 'package:analyzer/task/dart.dart'
25 show DART_ERRORS, LibrarySpecificUnit, PARSED_UNIT, TOKEN_STREAM;
26 import 'package:analyzer/task/general.dart' show CONTENT;
27 import 'package:analyzer/task/model.dart' show ResultDescriptor;
28
29 import 'ast.dart';
30 import 'element.dart';
31 import 'engine.dart';
32 import 'error.dart';
33 import 'error_verifier.dart';
34 import 'incremental_logger.dart' show logger, LoggingTimer;
35 import 'java_engine.dart';
36 import 'parser.dart';
37 import 'resolver.dart';
38 import 'scanner.dart';
39 import 'source.dart';
40 import 'utilities_dart.dart';
41
42 /**
43 * If `true`, an attempt to resolve API-changing modifications is made.
44 */
45 bool _resolveApiChanges = false;
46
47 /**
48 * This method is used to enable/disable API-changing modifications resolution.
49 */
50 void set test_resolveApiChanges(bool value) {
51 _resolveApiChanges = value;
52 }
53
54 /**
55 * Instances of the class [DeclarationMatcher] determine whether the element
56 * model defined by a given AST structure matches an existing element model.
57 */
58 class DeclarationMatcher extends RecursiveAstVisitor {
59 /**
60 * The libary containing the AST nodes being visited.
61 */
62 LibraryElement _enclosingLibrary;
63
64 /**
65 * The compilation unit containing the AST nodes being visited.
66 */
67 CompilationUnitElement _enclosingUnit;
68
69 /**
70 * The function type alias containing the AST nodes being visited, or `null` i f we are not
71 * in the scope of a function type alias.
72 */
73 FunctionTypeAliasElement _enclosingAlias;
74
75 /**
76 * The class containing the AST nodes being visited, or `null` if we are not
77 * in the scope of a class.
78 */
79 ClassElementImpl _enclosingClass;
80
81 /**
82 * The parameter containing the AST nodes being visited, or `null` if we are n ot in the
83 * scope of a parameter.
84 */
85 ParameterElement _enclosingParameter;
86
87 FieldDeclaration _enclosingFieldNode = null;
88 bool _inTopLevelVariableDeclaration = false;
89
90 /**
91 * Is `true` if the current class declaration has a constructor.
92 */
93 bool _hasConstructor = false;
94
95 /**
96 * A set containing all of the elements in the element model that were defined by the old AST node
97 * corresponding to the AST node being visited.
98 */
99 HashSet<Element> _allElements = new HashSet<Element>();
100
101 /**
102 * A set containing all of the elements were defined in the old element model,
103 * but are not defined in the new element model.
104 */
105 HashSet<Element> _removedElements = new HashSet<Element>();
106
107 /**
108 * A set containing all of the elements are defined in the new element model,
109 * but were not defined in the old element model.
110 */
111 HashSet<Element> _addedElements = new HashSet<Element>();
112
113 /**
114 * Determines how elements model corresponding to the given [node] differs
115 * from the [element].
116 */
117 DeclarationMatchKind matches(AstNode node, Element element) {
118 logger.enter('match $element @ ${element.nameOffset}');
119 try {
120 _captureEnclosingElements(element);
121 _gatherElements(element);
122 node.accept(this);
123 } on _DeclarationMismatchException {
124 return DeclarationMatchKind.MISMATCH;
125 } finally {
126 logger.exit();
127 }
128 // no API changes
129 if (_removedElements.isEmpty && _addedElements.isEmpty) {
130 return DeclarationMatchKind.MATCH;
131 }
132 // simple API change
133 logger.log('_removedElements: $_removedElements');
134 logger.log('_addedElements: $_addedElements');
135 _removedElements.forEach(_removeElement);
136 if (_removedElements.length <= 1 && _addedElements.length == 1) {
137 return DeclarationMatchKind.MISMATCH_OK;
138 }
139 // something more complex
140 return DeclarationMatchKind.MISMATCH;
141 }
142
143 @override
144 visitBlockFunctionBody(BlockFunctionBody node) {
145 // ignore bodies
146 }
147
148 @override
149 visitClassDeclaration(ClassDeclaration node) {
150 String name = node.name.name;
151 ClassElement element = _findElement(_enclosingUnit.types, name);
152 _enclosingClass = element;
153 _processElement(element);
154 _assertSameAnnotations(node, element);
155 _assertSameTypeParameters(node.typeParameters, element.typeParameters);
156 // check for missing clauses
157 if (node.extendsClause == null) {
158 _assertTrue(element.supertype.name == 'Object');
159 }
160 if (node.implementsClause == null) {
161 _assertTrue(element.interfaces.isEmpty);
162 }
163 if (node.withClause == null) {
164 _assertTrue(element.mixins.isEmpty);
165 }
166 // process clauses and members
167 _hasConstructor = false;
168 super.visitClassDeclaration(node);
169 // process default constructor
170 if (!_hasConstructor) {
171 ConstructorElement constructor = element.unnamedConstructor;
172 _processElement(constructor);
173 if (!constructor.isSynthetic) {
174 _assertEquals(constructor.parameters.length, 0);
175 }
176 }
177 }
178
179 @override
180 visitClassTypeAlias(ClassTypeAlias node) {
181 String name = node.name.name;
182 ClassElement element = _findElement(_enclosingUnit.types, name);
183 _enclosingClass = element;
184 _processElement(element);
185 _assertSameTypeParameters(node.typeParameters, element.typeParameters);
186 super.visitClassTypeAlias(node);
187 }
188
189 @override
190 visitCompilationUnit(CompilationUnit node) {
191 _processElement(_enclosingUnit);
192 super.visitCompilationUnit(node);
193 }
194
195 @override
196 visitConstructorDeclaration(ConstructorDeclaration node) {
197 _hasConstructor = true;
198 SimpleIdentifier constructorName = node.name;
199 ConstructorElementImpl element = constructorName == null
200 ? _enclosingClass.unnamedConstructor
201 : _enclosingClass.getNamedConstructor(constructorName.name);
202 _processElement(element);
203 _assertEquals(node.constKeyword != null, element.isConst);
204 _assertEquals(node.factoryKeyword != null, element.isFactory);
205 _assertCompatibleParameters(node.parameters, element.parameters);
206 // matches, update the existing element
207 ExecutableElement newElement = node.element;
208 node.element = element;
209 _setLocalElements(element, newElement);
210 }
211
212 @override
213 visitEnumConstantDeclaration(EnumConstantDeclaration node) {
214 String name = node.name.name;
215 FieldElement element = _findElement(_enclosingClass.fields, name);
216 _processElement(element);
217 }
218
219 @override
220 visitEnumDeclaration(EnumDeclaration node) {
221 String name = node.name.name;
222 ClassElement element = _findElement(_enclosingUnit.enums, name);
223 _enclosingClass = element;
224 _processElement(element);
225 _assertTrue(element.isEnum);
226 super.visitEnumDeclaration(node);
227 }
228
229 @override
230 visitExportDirective(ExportDirective node) {
231 String uri = _getStringValue(node.uri);
232 if (uri != null) {
233 ExportElement element =
234 _findUriReferencedElement(_enclosingLibrary.exports, uri);
235 _processElement(element);
236 _assertCombinators(node.combinators, element.combinators);
237 }
238 }
239
240 @override
241 visitExpressionFunctionBody(ExpressionFunctionBody node) {
242 // ignore bodies
243 }
244
245 @override
246 visitExtendsClause(ExtendsClause node) {
247 _assertSameType(node.superclass, _enclosingClass.supertype);
248 }
249
250 @override
251 visitFieldDeclaration(FieldDeclaration node) {
252 _enclosingFieldNode = node;
253 try {
254 super.visitFieldDeclaration(node);
255 } finally {
256 _enclosingFieldNode = null;
257 }
258 }
259
260 @override
261 visitFunctionDeclaration(FunctionDeclaration node) {
262 // prepare element name
263 String name = node.name.name;
264 if (node.isSetter) {
265 name += '=';
266 }
267 // prepare element
268 Token property = node.propertyKeyword;
269 ExecutableElementImpl element;
270 if (property == null) {
271 element = _findElement(_enclosingUnit.functions, name);
272 } else {
273 element = _findElement(_enclosingUnit.accessors, name);
274 }
275 // process element
276 _processElement(element);
277 _assertSameAnnotations(node, element);
278 _assertFalse(element.isSynthetic);
279 _assertSameType(node.returnType, element.returnType);
280 _assertCompatibleParameters(
281 node.functionExpression.parameters, element.parameters);
282 _assertBody(node.functionExpression.body, element);
283 // matches, update the existing element
284 ExecutableElement newElement = node.element;
285 node.name.staticElement = element;
286 node.functionExpression.element = element;
287 _setLocalElements(element, newElement);
288 }
289
290 @override
291 visitFunctionTypeAlias(FunctionTypeAlias node) {
292 String name = node.name.name;
293 FunctionTypeAliasElement element =
294 _findElement(_enclosingUnit.functionTypeAliases, name);
295 _processElement(element);
296 _assertSameTypeParameters(node.typeParameters, element.typeParameters);
297 _assertSameType(node.returnType, element.returnType);
298 _assertCompatibleParameters(node.parameters, element.parameters);
299 }
300
301 @override
302 visitImplementsClause(ImplementsClause node) {
303 List<TypeName> nodes = node.interfaces;
304 List<InterfaceType> types = _enclosingClass.interfaces;
305 _assertSameTypes(nodes, types);
306 }
307
308 @override
309 visitImportDirective(ImportDirective node) {
310 String uri = _getStringValue(node.uri);
311 if (uri != null) {
312 ImportElement element =
313 _findUriReferencedElement(_enclosingLibrary.imports, uri);
314 _processElement(element);
315 // match the prefix
316 SimpleIdentifier prefixNode = node.prefix;
317 PrefixElement prefixElement = element.prefix;
318 if (prefixNode == null) {
319 _assertNull(prefixElement);
320 } else {
321 _assertNotNull(prefixElement);
322 _assertEquals(prefixNode.name, prefixElement.name);
323 }
324 // match combinators
325 _assertCombinators(node.combinators, element.combinators);
326 }
327 }
328
329 @override
330 visitMethodDeclaration(MethodDeclaration node) {
331 // prepare element name
332 String name = node.name.name;
333 if (name == TokenType.MINUS.lexeme &&
334 node.parameters.parameters.length == 0) {
335 name = "unary-";
336 }
337 if (node.isSetter) {
338 name += '=';
339 }
340 // prepare element
341 Token property = node.propertyKeyword;
342 ExecutableElementImpl element;
343 if (property == null) {
344 element = _findElement(_enclosingClass.methods, name);
345 } else {
346 element = _findElement(_enclosingClass.accessors, name);
347 }
348 // process element
349 ExecutableElement newElement = node.element;
350 try {
351 _assertNotNull(element);
352 _assertSameAnnotations(node, element);
353 _assertEquals(node.isStatic, element.isStatic);
354 _assertSameType(node.returnType, element.returnType);
355 _assertCompatibleParameters(node.parameters, element.parameters);
356 _assertBody(node.body, element);
357 _removedElements.remove(element);
358 // matches, update the existing element
359 node.name.staticElement = element;
360 _setLocalElements(element, newElement);
361 } on _DeclarationMismatchException {
362 _removeElement(element);
363 // add new element
364 if (newElement != null) {
365 _addedElements.add(newElement);
366 if (newElement is MethodElement) {
367 List<MethodElement> methods = _enclosingClass.methods;
368 methods.add(newElement);
369 _enclosingClass.methods = methods;
370 } else {
371 List<PropertyAccessorElement> accessors = _enclosingClass.accessors;
372 accessors.add(newElement);
373 _enclosingClass.accessors = accessors;
374 }
375 }
376 }
377 }
378
379 @override
380 visitPartDirective(PartDirective node) {
381 String uri = _getStringValue(node.uri);
382 if (uri != null) {
383 CompilationUnitElement element =
384 _findUriReferencedElement(_enclosingLibrary.parts, uri);
385 _processElement(element);
386 }
387 super.visitPartDirective(node);
388 }
389
390 @override
391 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
392 _inTopLevelVariableDeclaration = true;
393 try {
394 super.visitTopLevelVariableDeclaration(node);
395 } finally {
396 _inTopLevelVariableDeclaration = false;
397 }
398 }
399
400 @override
401 visitVariableDeclaration(VariableDeclaration node) {
402 // prepare variable
403 String name = node.name.name;
404 PropertyInducingElement element;
405 if (_inTopLevelVariableDeclaration) {
406 element = _findElement(_enclosingUnit.topLevelVariables, name);
407 } else {
408 element = _findElement(_enclosingClass.fields, name);
409 }
410 // verify
411 PropertyInducingElement newElement = node.name.staticElement;
412 _processElement(element);
413 _assertSameAnnotations(node, element);
414 _assertEquals(node.isConst, element.isConst);
415 _assertEquals(node.isFinal, element.isFinal);
416 if (_enclosingFieldNode != null) {
417 _assertEquals(_enclosingFieldNode.isStatic, element.isStatic);
418 }
419 _assertSameType(
420 (node.parent as VariableDeclarationList).type, element.type);
421 // matches, restore the existing element
422 node.name.staticElement = element;
423 if (element is VariableElementImpl) {
424 (element as VariableElementImpl).initializer = newElement.initializer;
425 }
426 }
427
428 @override
429 visitWithClause(WithClause node) {
430 List<TypeName> nodes = node.mixinTypes;
431 List<InterfaceType> types = _enclosingClass.mixins;
432 _assertSameTypes(nodes, types);
433 }
434
435 /**
436 * Assert that the given [body] is compatible with the given [element].
437 * It should not be empty if the [element] is not an abstract class member.
438 * If it is present, it should have the same async / generator modifiers.
439 */
440 void _assertBody(FunctionBody body, ExecutableElementImpl element) {
441 if (body is EmptyFunctionBody) {
442 _assertTrue(element.isAbstract);
443 } else {
444 _assertFalse(element.isAbstract);
445 _assertEquals(body.isSynchronous, element.isSynchronous);
446 _assertEquals(body.isGenerator, element.isGenerator);
447 }
448 }
449
450 void _assertCombinators(List<Combinator> nodeCombinators,
451 List<NamespaceCombinator> elementCombinators) {
452 // prepare shown/hidden names in the element
453 Set<String> showNames = new Set<String>();
454 Set<String> hideNames = new Set<String>();
455 for (NamespaceCombinator combinator in elementCombinators) {
456 if (combinator is ShowElementCombinator) {
457 showNames.addAll(combinator.shownNames);
458 } else if (combinator is HideElementCombinator) {
459 hideNames.addAll(combinator.hiddenNames);
460 }
461 }
462 // match combinators with the node
463 for (Combinator combinator in nodeCombinators) {
464 if (combinator is ShowCombinator) {
465 for (SimpleIdentifier nameNode in combinator.shownNames) {
466 String name = nameNode.name;
467 _assertTrue(showNames.remove(name));
468 }
469 } else if (combinator is HideCombinator) {
470 for (SimpleIdentifier nameNode in combinator.hiddenNames) {
471 String name = nameNode.name;
472 _assertTrue(hideNames.remove(name));
473 }
474 }
475 }
476 _assertTrue(showNames.isEmpty);
477 _assertTrue(hideNames.isEmpty);
478 }
479
480 void _assertCompatibleParameter(
481 FormalParameter node, ParameterElement element) {
482 _assertEquals(node.kind, element.parameterKind);
483 if (node.kind == ParameterKind.NAMED) {
484 _assertEquals(node.identifier.name, element.name);
485 }
486 // check parameter type specific properties
487 if (node is DefaultFormalParameter) {
488 Expression nodeDefault = node.defaultValue;
489 if (nodeDefault == null) {
490 _assertNull(element.defaultValueCode);
491 } else {
492 _assertEquals(nodeDefault.toSource(), element.defaultValueCode);
493 }
494 _assertCompatibleParameter(node.parameter, element);
495 } else if (node is FieldFormalParameter) {
496 _assertTrue(element.isInitializingFormal);
497 _assertCompatibleParameters(node.parameters, element.parameters);
498 } else if (node is FunctionTypedFormalParameter) {
499 _assertFalse(element.isInitializingFormal);
500 _assertTrue(element.type is FunctionType);
501 FunctionType elementType = element.type;
502 _assertCompatibleParameters(node.parameters, element.parameters);
503 _assertSameType(node.returnType, elementType.returnType);
504 } else if (node is SimpleFormalParameter) {
505 _assertFalse(element.isInitializingFormal);
506 _assertSameType(node.type, element.type);
507 }
508 }
509
510 void _assertCompatibleParameters(
511 FormalParameterList nodes, List<ParameterElement> elements) {
512 if (nodes == null) {
513 return _assertEquals(elements.length, 0);
514 }
515 List<FormalParameter> parameters = nodes.parameters;
516 int length = parameters.length;
517 _assertEquals(length, elements.length);
518 for (int i = 0; i < length; i++) {
519 _assertCompatibleParameter(parameters[i], elements[i]);
520 }
521 }
522
523 /**
524 * Asserts that there is an import with the same prefix as the given
525 * [prefixNode], which exposes the given [element].
526 */
527 void _assertElementVisibleWithPrefix(
528 SimpleIdentifier prefixNode, Element element) {
529 if (prefixNode == null) {
530 return;
531 }
532 String prefixName = prefixNode.name;
533 for (ImportElement import in _enclosingLibrary.imports) {
534 if (import.prefix != null && import.prefix.name == prefixName) {
535 Namespace namespace =
536 new NamespaceBuilder().createImportNamespaceForDirective(import);
537 Iterable<Element> visibleElements = namespace.definedNames.values;
538 if (visibleElements.contains(element)) {
539 return;
540 }
541 }
542 }
543 _assertTrue(false);
544 }
545
546 void _assertEquals(Object a, Object b) {
547 if (a != b) {
548 throw new _DeclarationMismatchException();
549 }
550 }
551
552 void _assertFalse(bool condition) {
553 if (condition) {
554 throw new _DeclarationMismatchException();
555 }
556 }
557
558 void _assertNotNull(Object object) {
559 if (object == null) {
560 throw new _DeclarationMismatchException();
561 }
562 }
563
564 void _assertNull(Object object) {
565 if (object != null) {
566 throw new _DeclarationMismatchException();
567 }
568 }
569
570 void _assertSameAnnotation(Annotation node, ElementAnnotation annotation) {
571 Element element = annotation.element;
572 if (element is ConstructorElement) {
573 _assertTrue(node.name is SimpleIdentifier);
574 _assertNull(node.constructorName);
575 TypeName nodeType = new TypeName(node.name, null);
576 _assertSameType(nodeType, element.returnType);
577 // TODO(scheglov) validate arguments
578 }
579 if (element is PropertyAccessorElement) {
580 _assertTrue(node.name is SimpleIdentifier);
581 String nodeName = node.name.name;
582 String elementName = element.displayName;
583 _assertEquals(nodeName, elementName);
584 }
585 }
586
587 void _assertSameAnnotations(AnnotatedNode node, Element element) {
588 List<Annotation> nodeAnnotaitons = node.metadata;
589 List<ElementAnnotation> elementAnnotations = element.metadata;
590 int length = nodeAnnotaitons.length;
591 _assertEquals(elementAnnotations.length, length);
592 for (int i = 0; i < length; i++) {
593 _assertSameAnnotation(nodeAnnotaitons[i], elementAnnotations[i]);
594 }
595 }
596
597 void _assertSameType(TypeName node, DartType type) {
598 // no type == dynamic
599 if (node == null) {
600 return _assertTrue(type == null || type.isDynamic);
601 }
602 if (type == null) {
603 return _assertTrue(false);
604 }
605 // prepare name
606 SimpleIdentifier prefixIdentifier = null;
607 Identifier nameIdentifier = node.name;
608 if (nameIdentifier is PrefixedIdentifier) {
609 PrefixedIdentifier prefixedIdentifier = nameIdentifier;
610 prefixIdentifier = prefixedIdentifier.prefix;
611 nameIdentifier = prefixedIdentifier.identifier;
612 }
613 String nodeName = nameIdentifier.name;
614 // check specific type kinds
615 if (type is ParameterizedType) {
616 _assertEquals(nodeName, type.name);
617 _assertElementVisibleWithPrefix(prefixIdentifier, type.element);
618 // check arguments
619 TypeArgumentList nodeArgumentList = node.typeArguments;
620 List<DartType> typeArguments = type.typeArguments;
621 if (nodeArgumentList == null) {
622 // Node doesn't have type arguments, so all type arguments of the
623 // element must be "dynamic".
624 for (DartType typeArgument in typeArguments) {
625 _assertTrue(typeArgument.isDynamic);
626 }
627 } else {
628 List<TypeName> nodeArguments = nodeArgumentList.arguments;
629 _assertSameTypes(nodeArguments, typeArguments);
630 }
631 } else if (type is TypeParameterType) {
632 _assertEquals(nodeName, type.name);
633 // TODO(scheglov) it should be possible to rename type parameters
634 } else if (type.isVoid) {
635 _assertEquals(nodeName, 'void');
636 } else if (type.isDynamic) {
637 _assertEquals(nodeName, 'dynamic');
638 } else {
639 // TODO(scheglov) support other types
640 logger.log('node: $node type: $type type.type: ${type.runtimeType}');
641 _assertTrue(false);
642 }
643 }
644
645 void _assertSameTypeParameter(
646 TypeParameter node, TypeParameterElement element) {
647 _assertSameType(node.bound, element.bound);
648 }
649
650 void _assertSameTypeParameters(
651 TypeParameterList nodesList, List<TypeParameterElement> elements) {
652 if (nodesList == null) {
653 return _assertEquals(elements.length, 0);
654 }
655 List<TypeParameter> nodes = nodesList.typeParameters;
656 int length = nodes.length;
657 _assertEquals(length, elements.length);
658 for (int i = 0; i < length; i++) {
659 _assertSameTypeParameter(nodes[i], elements[i]);
660 }
661 }
662
663 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) {
664 int length = nodes.length;
665 _assertEquals(length, types.length);
666 for (int i = 0; i < length; i++) {
667 _assertSameType(nodes[i], types[i]);
668 }
669 }
670
671 void _assertTrue(bool condition) {
672 if (!condition) {
673 throw new _DeclarationMismatchException();
674 }
675 }
676
677 /**
678 * Given that the comparison is to begin with the given [element], capture
679 * the enclosing elements that might be used while performing the comparison.
680 */
681 void _captureEnclosingElements(Element element) {
682 Element parent =
683 element is CompilationUnitElement ? element : element.enclosingElement;
684 while (parent != null) {
685 if (parent is CompilationUnitElement) {
686 _enclosingUnit = parent;
687 _enclosingLibrary = element.library;
688 } else if (parent is ClassElement) {
689 if (_enclosingClass == null) {
690 _enclosingClass = parent;
691 }
692 } else if (parent is FunctionTypeAliasElement) {
693 if (_enclosingAlias == null) {
694 _enclosingAlias = parent;
695 }
696 } else if (parent is ParameterElement) {
697 if (_enclosingParameter == null) {
698 _enclosingParameter = parent;
699 }
700 }
701 parent = parent.enclosingElement;
702 }
703 }
704
705 void _gatherElements(Element element) {
706 _ElementsGatherer gatherer = new _ElementsGatherer(this);
707 element.accept(gatherer);
708 // TODO(scheglov) what if a change in a directive?
709 if (identical(element, _enclosingLibrary.definingCompilationUnit)) {
710 gatherer.addElements(_enclosingLibrary.imports);
711 gatherer.addElements(_enclosingLibrary.exports);
712 gatherer.addElements(_enclosingLibrary.parts);
713 }
714 }
715
716 void _processElement(Element element) {
717 _assertNotNull(element);
718 if (!_allElements.contains(element)) {
719 throw new _DeclarationMismatchException();
720 }
721 _removedElements.remove(element);
722 }
723
724 void _removeElement(Element element) {
725 if (element != null) {
726 Element enclosingElement = element.enclosingElement;
727 if (element is MethodElement) {
728 ClassElement classElement = enclosingElement;
729 _removeIdenticalElement(classElement.methods, element);
730 } else if (element is PropertyAccessorElement) {
731 if (enclosingElement is ClassElement) {
732 _removeIdenticalElement(enclosingElement.accessors, element);
733 }
734 if (enclosingElement is CompilationUnitElement) {
735 _removeIdenticalElement(enclosingElement.accessors, element);
736 }
737 }
738 }
739 }
740
741 /**
742 * Return the [Element] in [elements] with the given [name].
743 */
744 static Element _findElement(List<Element> elements, String name) {
745 for (Element element in elements) {
746 if (element.name == name) {
747 return element;
748 }
749 }
750 return null;
751 }
752
753 /**
754 * Return the [UriReferencedElement] from [elements] with the given [uri], or
755 * `null` if there is no such element.
756 */
757 static UriReferencedElement _findUriReferencedElement(
758 List<UriReferencedElement> elements, String uri) {
759 for (UriReferencedElement element in elements) {
760 if (element.uri == uri) {
761 return element;
762 }
763 }
764 return null;
765 }
766
767 /**
768 * Return the value of [literal], or `null` if the string is not a constant
769 * string without any string interpolation.
770 */
771 static String _getStringValue(StringLiteral literal) {
772 if (literal is StringInterpolation) {
773 return null;
774 }
775 return literal.stringValue;
776 }
777
778 /**
779 * Removes the first element identical to the given [element] from [elements].
780 */
781 static void _removeIdenticalElement(List elements, Object element) {
782 int length = elements.length;
783 for (int i = 0; i < length; i++) {
784 if (identical(elements[i], element)) {
785 elements.removeAt(i);
786 return;
787 }
788 }
789 }
790
791 static void _setLocalElements(
792 ExecutableElementImpl to, ExecutableElement from) {
793 if (from != null) {
794 to.functions = from.functions;
795 to.labels = from.labels;
796 to.localVariables = from.localVariables;
797 to.parameters = from.parameters;
798 }
799 }
800 }
801
802 /**
803 * Describes how declarations match an existing elements model.
804 */
805 class DeclarationMatchKind {
806 /**
807 * Complete match, no API changes.
808 */
809 static const MATCH = const DeclarationMatchKind('MATCH');
810
811 /**
812 * Has API changes that we might be able to resolve incrementally.
813 */
814 static const MISMATCH_OK = const DeclarationMatchKind('MISMATCH_OK');
815
816 /**
817 * Has API changes that we cannot resolve incrementally.
818 */
819 static const MISMATCH = const DeclarationMatchKind('MISMATCH');
820
821 final String name;
822
823 const DeclarationMatchKind(this.name);
824
825 @override
826 String toString() => name;
827 }
828
829 /**
830 * Instances of the class [IncrementalResolver] resolve the smallest portion of
831 * an AST structure that we currently know how to resolve.
832 */
833 class IncrementalResolver {
834 /**
835 * The element of the compilation unit being resolved.
836 */
837 final CompilationUnitElementImpl _definingUnit;
838
839 /**
840 * The context the compilation unit being resolved in.
841 */
842 AnalysisContext _context;
843
844 /**
845 * The object used to access the types from the core library.
846 */
847 TypeProvider _typeProvider;
848
849 /**
850 * The element for the library containing the compilation unit being resolved.
851 */
852 LibraryElementImpl _definingLibrary;
853
854 /**
855 * The [DartEntry] corresponding to the source being resolved.
856 */
857 DartEntry oldEntry;
858
859 /**
860 * The [CacheEntry] corresponding to the source being resolved.
861 */
862 CacheEntry newSourceEntry;
863
864 /**
865 * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
866 */
867 CacheEntry newUnitEntry;
868
869 /**
870 * The source representing the compilation unit being visited.
871 */
872 Source _source;
873
874 /**
875 * The source representing the library of the compilation unit being visited.
876 */
877 Source _librarySource;
878
879 /**
880 * The offset of the changed contents.
881 */
882 final int _updateOffset;
883
884 /**
885 * The end of the changed contents in the old unit.
886 */
887 final int _updateEndOld;
888
889 /**
890 * The end of the changed contents in the new unit.
891 */
892 final int _updateEndNew;
893
894 int _updateDelta;
895
896 RecordingErrorListener errorListener = new RecordingErrorListener();
897 ResolutionContext _resolutionContext;
898
899 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
900 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
901
902 /**
903 * Initialize a newly created incremental resolver to resolve a node in the
904 * given source in the given library.
905 */
906 IncrementalResolver(this.oldEntry, this.newSourceEntry, this.newUnitEntry,
907 this._definingUnit, this._updateOffset, this._updateEndOld,
908 this._updateEndNew) {
909 _updateDelta = _updateEndNew - _updateEndOld;
910 _definingLibrary = _definingUnit.library;
911 _librarySource = _definingLibrary.source;
912 _source = _definingUnit.source;
913 _context = _definingUnit.context;
914 _typeProvider = _context.typeProvider;
915 }
916
917 /**
918 * Resolve [node], reporting any errors or warnings to the given listener.
919 *
920 * [node] - the root of the AST structure to be resolved.
921 *
922 * Returns `true` if resolution was successful.
923 */
924 bool resolve(AstNode node) {
925 logger.enter('resolve: $_definingUnit');
926 try {
927 AstNode rootNode = _findResolutionRoot(node);
928 _prepareResolutionContext(rootNode);
929 // update elements
930 _updateElementNameOffsets();
931 _buildElements(rootNode);
932 if (!_canBeIncrementallyResolved(rootNode)) {
933 return false;
934 }
935 // resolve
936 _resolveReferences(rootNode);
937 _computeConstants(rootNode);
938 _resolveErrors = errorListener.getErrorsForSource(_source);
939 // verify
940 _verify(rootNode);
941 _context.invalidateLibraryHints(_librarySource);
942 // update entry errors
943 _updateEntry();
944 // notify unit
945 _definingUnit.afterIncrementalResolution();
946 // OK
947 return true;
948 } finally {
949 logger.exit();
950 }
951 }
952
953 void _buildElements(AstNode node) {
954 LoggingTimer timer = logger.startTimer();
955 try {
956 ElementHolder holder = new ElementHolder();
957 ElementBuilder builder = new ElementBuilder(holder);
958 if (_resolutionContext.enclosingClassDeclaration != null) {
959 builder.visitClassDeclarationIncrementally(
960 _resolutionContext.enclosingClassDeclaration);
961 }
962 node.accept(builder);
963 } finally {
964 timer.stop('build elements');
965 }
966 }
967
968 /**
969 * Return `true` if [node] does not have element model changes, or these
970 * changes can be incrementally propagated.
971 */
972 bool _canBeIncrementallyResolved(AstNode node) {
973 // If we are replacing the whole declaration, this means that its signature
974 // is changed. It might be an API change, or not.
975 //
976 // If, for example, a required parameter is changed, it is not an API
977 // change, but we want to find the existing corresponding Element in the
978 // enclosing one, set it for the node and update as needed.
979 //
980 // If, for example, the name of a method is changed, it is an API change,
981 // we need to know the old Element and the new Element. Again, we need to
982 // check the whole enclosing Element.
983 if (node is Declaration) {
984 node = node.parent;
985 }
986 Element element = _getElement(node);
987 DeclarationMatcher matcher = new DeclarationMatcher();
988 DeclarationMatchKind matchKind = matcher.matches(node, element);
989 if (matchKind == DeclarationMatchKind.MATCH) {
990 return true;
991 }
992 // mismatch that cannot be incrementally fixed
993 return false;
994 }
995
996 /**
997 * Return `true` if the given node can be resolved independently of any other
998 * nodes.
999 *
1000 * *Note*: This method needs to be kept in sync with
1001 * [ScopeBuilder.ContextBuilder].
1002 *
1003 * [node] - the node being tested.
1004 */
1005 bool _canBeResolved(AstNode node) => node is ClassDeclaration ||
1006 node is ClassTypeAlias ||
1007 node is CompilationUnit ||
1008 node is ConstructorDeclaration ||
1009 node is FunctionDeclaration ||
1010 node is FunctionTypeAlias ||
1011 node is MethodDeclaration ||
1012 node is TopLevelVariableDeclaration;
1013
1014 /**
1015 * Compute a value for all of the constants in the given [node].
1016 */
1017 void _computeConstants(AstNode node) {
1018 // compute values
1019 {
1020 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
1021 ConstantValueComputer computer = new ConstantValueComputer(
1022 _context, _typeProvider, _context.declaredVariables);
1023 computer.add(unit, _source, _librarySource);
1024 computer.computeValues();
1025 }
1026 // validate
1027 {
1028 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
1029 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
1030 _definingLibrary, _typeProvider, _context.declaredVariables);
1031 node.accept(constantVerifier);
1032 }
1033 }
1034
1035 /**
1036 * Starting at [node], find the smallest AST node that can be resolved
1037 * independently of any other nodes. Return the node that was found.
1038 *
1039 * [node] - the node at which the search is to begin
1040 *
1041 * Throws [AnalysisException] if there is no such node.
1042 */
1043 AstNode _findResolutionRoot(AstNode node) {
1044 while (node != null) {
1045 if (_canBeResolved(node)) {
1046 return node;
1047 }
1048 node = node.parent;
1049 }
1050 throw new AnalysisException("Cannot resolve node: no resolvable node");
1051 }
1052
1053 /**
1054 * Return the element defined by [node], or `null` if the node does not
1055 * define an element.
1056 */
1057 Element _getElement(AstNode node) {
1058 if (node is Declaration) {
1059 return node.element;
1060 } else if (node is CompilationUnit) {
1061 return node.element;
1062 }
1063 return null;
1064 }
1065
1066 void _prepareResolutionContext(AstNode node) {
1067 if (_resolutionContext == null) {
1068 _resolutionContext =
1069 ResolutionContextBuilder.contextFor(node, errorListener);
1070 }
1071 }
1072
1073 _resolveReferences(AstNode node) {
1074 LoggingTimer timer = logger.startTimer();
1075 try {
1076 _prepareResolutionContext(node);
1077 Scope scope = _resolutionContext.scope;
1078 // resolve types
1079 {
1080 TypeResolverVisitor visitor = new TypeResolverVisitor(
1081 _definingLibrary, _source, _typeProvider, errorListener,
1082 nameScope: scope);
1083 node.accept(visitor);
1084 }
1085 // resolve variables
1086 {
1087 VariableResolverVisitor visitor = new VariableResolverVisitor(
1088 _definingLibrary, _source, _typeProvider, errorListener,
1089 nameScope: scope);
1090 node.accept(visitor);
1091 }
1092 // resolve references
1093 {
1094 ResolverVisitor visitor = new ResolverVisitor(
1095 _definingLibrary, _source, _typeProvider, errorListener,
1096 nameScope: scope);
1097 if (_resolutionContext.enclosingClassDeclaration != null) {
1098 visitor.visitClassDeclarationIncrementally(
1099 _resolutionContext.enclosingClassDeclaration);
1100 }
1101 if (node is Comment) {
1102 visitor.resolveOnlyCommentInFunctionBody = true;
1103 node = node.parent;
1104 }
1105 visitor.initForIncrementalResolution();
1106 node.accept(visitor);
1107 }
1108 } finally {
1109 timer.stop('resolve references');
1110 }
1111 }
1112
1113 void _shiftEntryErrors() {
1114 if (oldEntry != null) {
1115 _shiftEntryErrors_OLD();
1116 } else {
1117 _shiftEntryErrors_NEW();
1118 }
1119 }
1120
1121 void _shiftEntryErrors_NEW() {
1122 _shiftErrors_NEW(HINTS);
1123 _shiftErrors_NEW(RESOLVE_REFERENCES_ERRORS);
1124 _shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
1125 _shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
1126 _shiftErrors_NEW(VERIFY_ERRORS);
1127 }
1128
1129 void _shiftEntryErrors_OLD() {
1130 _shiftErrors_OLD(DartEntry.RESOLUTION_ERRORS);
1131 _shiftErrors_OLD(DartEntry.VERIFICATION_ERRORS);
1132 _shiftErrors_OLD(DartEntry.HINTS);
1133 _shiftErrors_OLD(DartEntry.LINTS);
1134 }
1135
1136 void _shiftErrors(List<AnalysisError> errors) {
1137 for (AnalysisError error in errors) {
1138 int errorOffset = error.offset;
1139 if (errorOffset > _updateOffset) {
1140 error.offset += _updateDelta;
1141 }
1142 }
1143 }
1144
1145 void _shiftErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor) {
1146 List<AnalysisError> errors = newUnitEntry.getValue(descriptor);
1147 _shiftErrors(errors);
1148 }
1149
1150 void _shiftErrors_OLD(DataDescriptor<List<AnalysisError>> descriptor) {
1151 List<AnalysisError> errors =
1152 oldEntry.getValueInLibrary(descriptor, _librarySource);
1153 _shiftErrors(errors);
1154 }
1155
1156 void _updateElementNameOffsets() {
1157 LoggingTimer timer = logger.startTimer();
1158 try {
1159 _definingUnit
1160 .accept(new _ElementNameOffsetUpdater(_updateOffset, _updateDelta));
1161 } finally {
1162 timer.stop('update element offsets');
1163 }
1164 }
1165
1166 void _updateEntry() {
1167 if (oldEntry != null) {
1168 _updateEntry_OLD();
1169 } else {
1170 _updateEntry_NEW();
1171 }
1172 }
1173
1174 void _updateEntry_NEW() {
1175 _updateErrors_NEW(RESOLVE_REFERENCES_ERRORS, _resolveErrors);
1176 _updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
1177 _updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
1178 _updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
1179 // invalidate results we don't update incrementally
1180 newUnitEntry.setState(USED_IMPORTED_ELEMENTS, CacheState.INVALID);
1181 newUnitEntry.setState(USED_LOCAL_ELEMENTS, CacheState.INVALID);
1182 newUnitEntry.setState(HINTS, CacheState.INVALID);
1183 }
1184
1185 void _updateEntry_OLD() {
1186 _updateErrors_OLD(DartEntry.RESOLUTION_ERRORS, _resolveErrors);
1187 _updateErrors_OLD(DartEntry.VERIFICATION_ERRORS, _verifyErrors);
1188 }
1189
1190 List<AnalysisError> _updateErrors(
1191 List<AnalysisError> oldErrors, List<AnalysisError> newErrors) {
1192 List<AnalysisError> errors = new List<AnalysisError>();
1193 // add updated old errors
1194 for (AnalysisError error in oldErrors) {
1195 int errorOffset = error.offset;
1196 if (errorOffset < _updateOffset) {
1197 errors.add(error);
1198 } else if (errorOffset > _updateEndOld) {
1199 error.offset += _updateDelta;
1200 errors.add(error);
1201 }
1202 }
1203 // add new errors
1204 for (AnalysisError error in newErrors) {
1205 int errorOffset = error.offset;
1206 if (errorOffset > _updateOffset && errorOffset < _updateEndNew) {
1207 errors.add(error);
1208 }
1209 }
1210 // done
1211 return errors;
1212 }
1213
1214 void _updateErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor,
1215 List<AnalysisError> newErrors) {
1216 List<AnalysisError> oldErrors = newUnitEntry.getValue(descriptor);
1217 List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
1218 newUnitEntry.setValueIncremental(descriptor, errors);
1219 }
1220
1221 void _updateErrors_OLD(DataDescriptor<List<AnalysisError>> descriptor,
1222 List<AnalysisError> newErrors) {
1223 List<AnalysisError> oldErrors =
1224 oldEntry.getValueInLibrary(descriptor, _librarySource);
1225 List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
1226 oldEntry.setValueInLibrary(descriptor, _librarySource, errors);
1227 }
1228
1229 void _verify(AstNode node) {
1230 LoggingTimer timer = logger.startTimer();
1231 try {
1232 RecordingErrorListener errorListener = new RecordingErrorListener();
1233 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
1234 ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter,
1235 _definingLibrary, _typeProvider,
1236 new InheritanceManager(_definingLibrary));
1237 if (_resolutionContext.enclosingClassDeclaration != null) {
1238 errorVerifier.visitClassDeclarationIncrementally(
1239 _resolutionContext.enclosingClassDeclaration);
1240 }
1241 node.accept(errorVerifier);
1242 _verifyErrors = errorListener.getErrorsForSource(_source);
1243 } finally {
1244 timer.stop('verify');
1245 }
1246 }
1247 }
1248
1249 class PoorMansIncrementalResolver {
1250 final TypeProvider _typeProvider;
1251 final Source _unitSource;
1252
1253 /**
1254 * The [DartEntry] corresponding to the source being resolved.
1255 */
1256 DartEntry _oldEntry;
1257
1258 /**
1259 * The [CacheEntry] corresponding to the source being resolved.
1260 */
1261 CacheEntry _newSourceEntry;
1262
1263 /**
1264 * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
1265 */
1266 CacheEntry _newUnitEntry;
1267
1268 final CompilationUnit _oldUnit;
1269 final AnalysisOptions _options;
1270 CompilationUnitElement _unitElement;
1271
1272 int _updateOffset;
1273 int _updateDelta;
1274 int _updateEndOld;
1275 int _updateEndNew;
1276
1277 List<AnalysisError> _newScanErrors = <AnalysisError>[];
1278 List<AnalysisError> _newParseErrors = <AnalysisError>[];
1279
1280 PoorMansIncrementalResolver(this._typeProvider, this._unitSource,
1281 this._oldEntry, this._newSourceEntry, this._newUnitEntry, this._oldUnit,
1282 bool resolveApiChanges, this._options) {
1283 _resolveApiChanges = resolveApiChanges;
1284 }
1285
1286 /**
1287 * Attempts to update [_oldUnit] to the state corresponding to [newCode].
1288 * Returns `true` if success, or `false` otherwise.
1289 * The [_oldUnit] might be damaged.
1290 */
1291 bool resolve(String newCode) {
1292 logger.enter('diff/resolve $_unitSource');
1293 try {
1294 // prepare old unit
1295 if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) {
1296 logger.log('Unbalanced number of curly brackets in the old unit.');
1297 return false;
1298 }
1299 _unitElement = _oldUnit.element;
1300 // prepare new unit
1301 CompilationUnit newUnit = _parseUnit(newCode);
1302 if (!_areCurlyBracketsBalanced(newUnit.beginToken)) {
1303 logger.log('Unbalanced number of curly brackets in the new unit.');
1304 return false;
1305 }
1306 // find difference
1307 _TokenPair firstPair =
1308 _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken);
1309 _TokenPair lastPair =
1310 _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken);
1311 if (firstPair != null && lastPair != null) {
1312 int firstOffsetOld = firstPair.oldToken.offset;
1313 int firstOffsetNew = firstPair.newToken.offset;
1314 int lastOffsetOld = lastPair.oldToken.end;
1315 int lastOffsetNew = lastPair.newToken.end;
1316 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld);
1317 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld);
1318 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew);
1319 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew);
1320 // check for a whitespace only change
1321 if (identical(lastPair.oldToken, firstPair.oldToken) &&
1322 identical(lastPair.newToken, firstPair.newToken)) {
1323 _updateOffset = beginOffsetOld - 1;
1324 _updateEndOld = endOffsetOld;
1325 _updateEndNew = endOffsetNew;
1326 _updateDelta = newUnit.length - _oldUnit.length;
1327 // A Dart documentation comment change.
1328 if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) {
1329 bool success = _resolveCommentDoc(newUnit, firstPair);
1330 logger.log('Documentation comment resolved: $success');
1331 return success;
1332 }
1333 // A pure whitespace change.
1334 if (firstPair.kind == _TokenDifferenceKind.OFFSET) {
1335 logger.log('Whitespace change.');
1336 _shiftTokens(firstPair.oldToken);
1337 {
1338 IncrementalResolver incrementalResolver = new IncrementalResolver(
1339 _oldEntry, _newSourceEntry, _newUnitEntry, _unitElement,
1340 _updateOffset, _updateEndOld, _updateEndNew);
1341 incrementalResolver._updateElementNameOffsets();
1342 incrementalResolver._shiftEntryErrors();
1343 }
1344 _updateEntry();
1345 logger.log('Success.');
1346 return true;
1347 }
1348 // fall-through, end-of-line comment
1349 }
1350 // Find nodes covering the "old" and "new" token ranges.
1351 AstNode oldNode =
1352 _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld - 1);
1353 AstNode newNode =
1354 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew - 1);
1355 logger.log(() => 'oldNode: $oldNode');
1356 logger.log(() => 'newNode: $newNode');
1357 // Try to find the smallest common node, a FunctionBody currently.
1358 {
1359 List<AstNode> oldParents = _getParents(oldNode);
1360 List<AstNode> newParents = _getParents(newNode);
1361 int length = math.min(oldParents.length, newParents.length);
1362 bool found = false;
1363 for (int i = 0; i < length; i++) {
1364 AstNode oldParent = oldParents[i];
1365 AstNode newParent = newParents[i];
1366 if (oldParent is ConstructorInitializer ||
1367 newParent is ConstructorInitializer) {
1368 logger.log('Failure: changes in constant constructor initializers'
1369 ' may cause external changes in constant objects.');
1370 return false;
1371 }
1372 if (oldParent is FunctionDeclaration &&
1373 newParent is FunctionDeclaration ||
1374 oldParent is MethodDeclaration &&
1375 newParent is MethodDeclaration ||
1376 oldParent is ConstructorDeclaration &&
1377 newParent is ConstructorDeclaration) {
1378 Element oldElement = (oldParent as Declaration).element;
1379 if (new DeclarationMatcher().matches(newParent, oldElement) ==
1380 DeclarationMatchKind.MATCH) {
1381 oldNode = oldParent;
1382 newNode = newParent;
1383 found = true;
1384 }
1385 }
1386 if (oldParent is FunctionBody && newParent is FunctionBody) {
1387 oldNode = oldParent;
1388 newNode = newParent;
1389 found = true;
1390 break;
1391 }
1392 }
1393 if (!found) {
1394 logger.log('Failure: no enclosing function body or executable.');
1395 return false;
1396 }
1397 // fail if a comment change outside the bodies
1398 if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
1399 if (beginOffsetOld <= oldNode.offset ||
1400 beginOffsetNew <= newNode.offset) {
1401 logger.log('Failure: comment outside a function body.');
1402 return false;
1403 }
1404 }
1405 }
1406 logger.log(() => 'oldNode: $oldNode');
1407 logger.log(() => 'newNode: $newNode');
1408 // prepare update range
1409 _updateOffset = oldNode.offset;
1410 _updateEndOld = oldNode.end;
1411 _updateEndNew = newNode.end;
1412 _updateDelta = _updateEndNew - _updateEndOld;
1413 // replace node
1414 NodeReplacer.replace(oldNode, newNode);
1415 // update token references
1416 {
1417 Token oldBeginToken = _getBeginTokenNotComment(oldNode);
1418 Token newBeginToken = _getBeginTokenNotComment(newNode);
1419 if (oldBeginToken.previous.type == TokenType.EOF) {
1420 _oldUnit.beginToken = newBeginToken;
1421 } else {
1422 oldBeginToken.previous.setNext(newBeginToken);
1423 }
1424 newNode.endToken.setNext(oldNode.endToken.next);
1425 _shiftTokens(oldNode.endToken.next);
1426 }
1427 // perform incremental resolution
1428 IncrementalResolver incrementalResolver = new IncrementalResolver(
1429 _oldEntry, _newSourceEntry, _newUnitEntry, _unitElement,
1430 _updateOffset, _updateEndOld, _updateEndNew);
1431 bool success = incrementalResolver.resolve(newNode);
1432 // check if success
1433 if (!success) {
1434 logger.log('Failure: element model changed.');
1435 return false;
1436 }
1437 // update DartEntry
1438 _updateEntry();
1439 logger.log('Success.');
1440 return true;
1441 }
1442 } catch (e, st) {
1443 logger.log(e);
1444 logger.log(st);
1445 logger.log('Failure: exception.');
1446 } finally {
1447 logger.exit();
1448 }
1449 return false;
1450 }
1451
1452 CompilationUnit _parseUnit(String code) {
1453 LoggingTimer timer = logger.startTimer();
1454 try {
1455 Token token = _scan(code);
1456 RecordingErrorListener errorListener = new RecordingErrorListener();
1457 Parser parser = new Parser(_unitSource, errorListener);
1458 AnalysisOptions options = _unitElement.context.analysisOptions;
1459 parser.parseGenericMethods = options.enableGenericMethods;
1460 CompilationUnit unit = parser.parseCompilationUnit(token);
1461 _newParseErrors = errorListener.errors;
1462 return unit;
1463 } finally {
1464 timer.stop('parse');
1465 }
1466 }
1467
1468 /**
1469 * Attempts to resolve a documentation comment change.
1470 * Returns `true` if success.
1471 */
1472 bool _resolveCommentDoc(CompilationUnit newUnit, _TokenPair firstPair) {
1473 Token oldToken = firstPair.oldToken;
1474 Token newToken = firstPair.newToken;
1475 CommentToken oldComments = oldToken.precedingComments;
1476 CommentToken newComments = newToken.precedingComments;
1477 if (oldComments == null || newComments == null) {
1478 return false;
1479 }
1480 // find nodes
1481 int offset = oldComments.offset;
1482 logger.log('offset: $offset');
1483 Comment oldComment = _findNodeCovering(_oldUnit, offset, offset);
1484 Comment newComment = _findNodeCovering(newUnit, offset, offset);
1485 logger.log('oldComment.beginToken: ${oldComment.beginToken}');
1486 logger.log('newComment.beginToken: ${newComment.beginToken}');
1487 _updateOffset = oldToken.offset - 1;
1488 // update token references
1489 _shiftTokens(firstPair.oldToken);
1490 _setPrecedingComments(oldToken, newComment.tokens.first);
1491 // replace node
1492 NodeReplacer.replace(oldComment, newComment);
1493 // update elements
1494 IncrementalResolver incrementalResolver = new IncrementalResolver(_oldEntry,
1495 _newSourceEntry, _newUnitEntry, _unitElement, _updateOffset,
1496 _updateEndOld, _updateEndNew);
1497 incrementalResolver._updateElementNameOffsets();
1498 incrementalResolver._shiftEntryErrors();
1499 _updateEntry();
1500 // resolve references in the comment
1501 incrementalResolver._resolveReferences(newComment);
1502 // OK
1503 return true;
1504 }
1505
1506 Token _scan(String code) {
1507 RecordingErrorListener errorListener = new RecordingErrorListener();
1508 CharSequenceReader reader = new CharSequenceReader(code);
1509 Scanner scanner = new Scanner(_unitSource, reader, errorListener);
1510 Token token = scanner.tokenize();
1511 _newScanErrors = errorListener.errors;
1512 return token;
1513 }
1514
1515 /**
1516 * Set the given [comment] as a "precedingComments" for [token].
1517 */
1518 void _setPrecedingComments(Token token, CommentToken comment) {
1519 if (token is BeginTokenWithComment) {
1520 token.precedingComments = comment;
1521 } else if (token is KeywordTokenWithComment) {
1522 token.precedingComments = comment;
1523 } else if (token is StringTokenWithComment) {
1524 token.precedingComments = comment;
1525 } else if (token is TokenWithComment) {
1526 token.precedingComments = comment;
1527 } else {
1528 Type parentType = token != null ? token.runtimeType : null;
1529 throw new AnalysisException('Uknown parent token type: $parentType');
1530 }
1531 }
1532
1533 void _shiftTokens(Token token) {
1534 while (token != null) {
1535 if (token.offset > _updateOffset) {
1536 token.offset += _updateDelta;
1537 }
1538 // comments
1539 _shiftTokens(token.precedingComments);
1540 if (token is DocumentationCommentToken) {
1541 for (Token reference in token.references) {
1542 _shiftTokens(reference);
1543 }
1544 }
1545 // next
1546 if (token.type == TokenType.EOF) {
1547 break;
1548 }
1549 token = token.next;
1550 }
1551 }
1552
1553 void _updateEntry() {
1554 if (_oldEntry != null) {
1555 _updateEntry_OLD();
1556 } else {
1557 _updateEntry_NEW();
1558 }
1559 }
1560
1561 void _updateEntry_NEW() {
1562 _newSourceEntry.setState(DART_ERRORS, CacheState.INVALID);
1563 // scan results
1564 _newSourceEntry.setState(SCAN_ERRORS, CacheState.INVALID);
1565 List<TargetedResult> scanDeps =
1566 <TargetedResult>[new TargetedResult(_unitSource, CONTENT)];
1567 _newSourceEntry.setValue(SCAN_ERRORS, _newScanErrors, scanDeps);
1568 // parse results
1569 List<TargetedResult> parseDeps =
1570 <TargetedResult>[new TargetedResult(_unitSource, TOKEN_STREAM)];
1571 _newSourceEntry.setState(PARSE_ERRORS, CacheState.INVALID);
1572 _newSourceEntry.setValue(PARSE_ERRORS, _newParseErrors, parseDeps);
1573 _newSourceEntry.setValue(PARSED_UNIT, _oldUnit, parseDeps);
1574 }
1575
1576 void _updateEntry_OLD() {
1577 _oldEntry.setValue(DartEntry.SCAN_ERRORS, _newScanErrors);
1578 _oldEntry.setValue(DartEntry.PARSE_ERRORS, _newParseErrors);
1579 }
1580
1581 /**
1582 * Checks if [token] has a balanced number of open and closed curly brackets.
1583 */
1584 static bool _areCurlyBracketsBalanced(Token token) {
1585 int numOpen = _getTokenCount(token, TokenType.OPEN_CURLY_BRACKET);
1586 int numOpen2 =
1587 _getTokenCount(token, TokenType.STRING_INTERPOLATION_EXPRESSION);
1588 int numClosed = _getTokenCount(token, TokenType.CLOSE_CURLY_BRACKET);
1589 return numOpen + numOpen2 == numClosed;
1590 }
1591
1592 static _TokenDifferenceKind _compareToken(
1593 Token oldToken, Token newToken, int delta, bool forComment) {
1594 while (true) {
1595 if (oldToken == null && newToken == null) {
1596 return null;
1597 }
1598 if (oldToken == null || newToken == null) {
1599 return _TokenDifferenceKind.CONTENT;
1600 }
1601 if (oldToken.type != newToken.type) {
1602 return _TokenDifferenceKind.CONTENT;
1603 }
1604 if (oldToken.lexeme != newToken.lexeme) {
1605 return _TokenDifferenceKind.CONTENT;
1606 }
1607 if (newToken.offset - oldToken.offset != delta) {
1608 return _TokenDifferenceKind.OFFSET;
1609 }
1610 // continue if comment tokens are being checked
1611 if (!forComment) {
1612 break;
1613 }
1614 oldToken = oldToken.next;
1615 newToken = newToken.next;
1616 }
1617 return null;
1618 }
1619
1620 static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
1621 while (true) {
1622 if (oldToken.type == TokenType.EOF && newToken.type == TokenType.EOF) {
1623 return null;
1624 }
1625 if (oldToken.type == TokenType.EOF || newToken.type == TokenType.EOF) {
1626 return new _TokenPair(_TokenDifferenceKind.CONTENT, oldToken, newToken);
1627 }
1628 // compare comments
1629 {
1630 Token oldComment = oldToken.precedingComments;
1631 Token newComment = newToken.precedingComments;
1632 if (_compareToken(oldComment, newComment, 0, true) != null) {
1633 _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
1634 if (oldComment is DocumentationCommentToken ||
1635 newComment is DocumentationCommentToken) {
1636 diffKind = _TokenDifferenceKind.COMMENT_DOC;
1637 }
1638 return new _TokenPair(diffKind, oldToken, newToken);
1639 }
1640 }
1641 // compare tokens
1642 _TokenDifferenceKind diffKind =
1643 _compareToken(oldToken, newToken, 0, false);
1644 if (diffKind != null) {
1645 return new _TokenPair(diffKind, oldToken, newToken);
1646 }
1647 // next tokens
1648 oldToken = oldToken.next;
1649 newToken = newToken.next;
1650 }
1651 // no difference
1652 return null;
1653 }
1654
1655 static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
1656 int delta = newToken.offset - oldToken.offset;
1657 while (oldToken.previous != oldToken && newToken.previous != newToken) {
1658 // compare tokens
1659 _TokenDifferenceKind diffKind =
1660 _compareToken(oldToken, newToken, delta, false);
1661 if (diffKind != null) {
1662 return new _TokenPair(diffKind, oldToken.next, newToken.next);
1663 }
1664 // compare comments
1665 {
1666 Token oldComment = oldToken.precedingComments;
1667 Token newComment = newToken.precedingComments;
1668 if (_compareToken(oldComment, newComment, delta, true) != null) {
1669 _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
1670 if (oldComment is DocumentationCommentToken ||
1671 newComment is DocumentationCommentToken) {
1672 diffKind = _TokenDifferenceKind.COMMENT_DOC;
1673 }
1674 return new _TokenPair(diffKind, oldToken, newToken);
1675 }
1676 }
1677 // next tokens
1678 oldToken = oldToken.previous;
1679 newToken = newToken.previous;
1680 }
1681 return null;
1682 }
1683
1684 static AstNode _findNodeCovering(AstNode root, int offset, int end) {
1685 NodeLocator nodeLocator = new NodeLocator(offset, end);
1686 return nodeLocator.searchWithin(root);
1687 }
1688
1689 static Token _getBeginTokenNotComment(AstNode node) {
1690 Token oldBeginToken = node.beginToken;
1691 if (oldBeginToken is CommentToken) {
1692 oldBeginToken = (oldBeginToken as CommentToken).parent;
1693 }
1694 return oldBeginToken;
1695 }
1696
1697 static List<AstNode> _getParents(AstNode node) {
1698 List<AstNode> parents = <AstNode>[];
1699 while (node != null) {
1700 parents.insert(0, node);
1701 node = node.parent;
1702 }
1703 return parents;
1704 }
1705
1706 /**
1707 * Returns number of tokens with the given [type].
1708 */
1709 static int _getTokenCount(Token token, TokenType type) {
1710 int count = 0;
1711 while (token.type != TokenType.EOF) {
1712 if (token.type == type) {
1713 count++;
1714 }
1715 token = token.next;
1716 }
1717 return count;
1718 }
1719 }
1720
1721 /**
1722 * The context to resolve an [AstNode] in.
1723 */
1724 class ResolutionContext {
1725 CompilationUnitElement enclosingUnit;
1726 ClassDeclaration enclosingClassDeclaration;
1727 ClassElement enclosingClass;
1728 Scope scope;
1729 }
1730
1731 /**
1732 * Instances of the class [ResolutionContextBuilder] build the context for a
1733 * given node in an AST structure. At the moment, this class only handles
1734 * top-level and class-level declarations.
1735 */
1736 class ResolutionContextBuilder {
1737 /**
1738 * The listener to which analysis errors will be reported.
1739 */
1740 final AnalysisErrorListener _errorListener;
1741
1742 /**
1743 * The class containing the enclosing [CompilationUnitElement].
1744 */
1745 CompilationUnitElement _enclosingUnit;
1746
1747 /**
1748 * The class containing the enclosing [ClassDeclaration], or `null` if we are
1749 * not in the scope of a class.
1750 */
1751 ClassDeclaration _enclosingClassDeclaration;
1752
1753 /**
1754 * The class containing the enclosing [ClassElement], or `null` if we are not
1755 * in the scope of a class.
1756 */
1757 ClassElement _enclosingClass;
1758
1759 /**
1760 * Initialize a newly created scope builder to generate a scope that will
1761 * report errors to the given listener.
1762 */
1763 ResolutionContextBuilder(this._errorListener);
1764
1765 Scope _scopeFor(AstNode node) {
1766 if (node is CompilationUnit) {
1767 return _scopeForAstNode(node);
1768 }
1769 AstNode parent = node.parent;
1770 if (parent == null) {
1771 throw new AnalysisException(
1772 "Cannot create scope: node is not part of a CompilationUnit");
1773 }
1774 return _scopeForAstNode(parent);
1775 }
1776
1777 /**
1778 * Return the scope in which the given AST structure should be resolved.
1779 *
1780 * *Note:* This method needs to be kept in sync with
1781 * [IncrementalResolver.canBeResolved].
1782 *
1783 * [node] - the root of the AST structure to be resolved.
1784 *
1785 * Throws [AnalysisException] if the AST structure has not been resolved or
1786 * is not part of a [CompilationUnit]
1787 */
1788 Scope _scopeForAstNode(AstNode node) {
1789 if (node is CompilationUnit) {
1790 return _scopeForCompilationUnit(node);
1791 }
1792 AstNode parent = node.parent;
1793 if (parent == null) {
1794 throw new AnalysisException(
1795 "Cannot create scope: node is not part of a CompilationUnit");
1796 }
1797 Scope scope = _scopeForAstNode(parent);
1798 if (node is ClassDeclaration) {
1799 _enclosingClassDeclaration = node;
1800 _enclosingClass = node.element;
1801 if (_enclosingClass == null) {
1802 throw new AnalysisException(
1803 "Cannot build a scope for an unresolved class");
1804 }
1805 scope = new ClassScope(
1806 new TypeParameterScope(scope, _enclosingClass), _enclosingClass);
1807 } else if (node is ClassTypeAlias) {
1808 ClassElement element = node.element;
1809 if (element == null) {
1810 throw new AnalysisException(
1811 "Cannot build a scope for an unresolved class type alias");
1812 }
1813 scope = new ClassScope(new TypeParameterScope(scope, element), element);
1814 } else if (node is ConstructorDeclaration) {
1815 ConstructorElement element = node.element;
1816 if (element == null) {
1817 throw new AnalysisException(
1818 "Cannot build a scope for an unresolved constructor");
1819 }
1820 FunctionScope functionScope = new FunctionScope(scope, element);
1821 functionScope.defineParameters();
1822 scope = functionScope;
1823 } else if (node is FunctionDeclaration) {
1824 ExecutableElement element = node.element;
1825 if (element == null) {
1826 throw new AnalysisException(
1827 "Cannot build a scope for an unresolved function");
1828 }
1829 FunctionScope functionScope = new FunctionScope(scope, element);
1830 functionScope.defineParameters();
1831 scope = functionScope;
1832 } else if (node is FunctionTypeAlias) {
1833 scope = new FunctionTypeScope(scope, node.element);
1834 } else if (node is MethodDeclaration) {
1835 ExecutableElement element = node.element;
1836 if (element == null) {
1837 throw new AnalysisException(
1838 "Cannot build a scope for an unresolved method");
1839 }
1840 FunctionScope functionScope = new FunctionScope(scope, element);
1841 functionScope.defineParameters();
1842 scope = functionScope;
1843 }
1844 return scope;
1845 }
1846
1847 Scope _scopeForCompilationUnit(CompilationUnit node) {
1848 _enclosingUnit = node.element;
1849 if (_enclosingUnit == null) {
1850 throw new AnalysisException(
1851 "Cannot create scope: compilation unit is not resolved");
1852 }
1853 LibraryElement libraryElement = _enclosingUnit.library;
1854 if (libraryElement == null) {
1855 throw new AnalysisException(
1856 "Cannot create scope: compilation unit is not part of a library");
1857 }
1858 return new LibraryScope(libraryElement, _errorListener);
1859 }
1860
1861 /**
1862 * Return the context in which the given AST structure should be resolved.
1863 *
1864 * [node] - the root of the AST structure to be resolved.
1865 * [errorListener] - the listener to which analysis errors will be reported.
1866 *
1867 * Throws [AnalysisException] if the AST structure has not been resolved or
1868 * is not part of a [CompilationUnit]
1869 */
1870 static ResolutionContext contextFor(
1871 AstNode node, AnalysisErrorListener errorListener) {
1872 if (node == null) {
1873 throw new AnalysisException("Cannot create context: node is null");
1874 }
1875 // build scope
1876 ResolutionContextBuilder builder =
1877 new ResolutionContextBuilder(errorListener);
1878 Scope scope = builder._scopeFor(node);
1879 // prepare context
1880 ResolutionContext context = new ResolutionContext();
1881 context.scope = scope;
1882 context.enclosingUnit = builder._enclosingUnit;
1883 context.enclosingClassDeclaration = builder._enclosingClassDeclaration;
1884 context.enclosingClass = builder._enclosingClass;
1885 return context;
1886 }
1887 }
1888
1889 /**
1890 * Instances of the class [_DeclarationMismatchException] represent an exception
1891 * that is thrown when the element model defined by a given AST structure does
1892 * not match an existing element model.
1893 */
1894 class _DeclarationMismatchException {}
1895
1896 class _ElementNameOffsetUpdater extends GeneralizingElementVisitor {
1897 final int updateOffset;
1898 final int updateDelta;
1899
1900 _ElementNameOffsetUpdater(this.updateOffset, this.updateDelta);
1901
1902 @override
1903 visitElement(Element element) {
1904 int nameOffset = element.nameOffset;
1905 if (nameOffset > updateOffset) {
1906 (element as ElementImpl).nameOffset = nameOffset + updateDelta;
1907 }
1908 super.visitElement(element);
1909 }
1910 }
1911
1912 class _ElementsGatherer extends GeneralizingElementVisitor {
1913 final DeclarationMatcher matcher;
1914
1915 _ElementsGatherer(this.matcher);
1916
1917 void addElements(List<Element> elements) {
1918 for (Element element in elements) {
1919 if (!element.isSynthetic) {
1920 _addElement(element);
1921 }
1922 }
1923 }
1924
1925 @override
1926 visitElement(Element element) {
1927 _addElement(element);
1928 super.visitElement(element);
1929 }
1930
1931 @override
1932 visitExecutableElement(ExecutableElement element) {
1933 _addElement(element);
1934 }
1935
1936 @override
1937 visitParameterElement(ParameterElement element) {}
1938
1939 @override
1940 visitPropertyAccessorElement(PropertyAccessorElement element) {
1941 if (!element.isSynthetic) {
1942 _addElement(element);
1943 }
1944 // Don't visit children (such as synthetic setter parameters).
1945 }
1946
1947 @override
1948 visitPropertyInducingElement(PropertyInducingElement element) {
1949 if (!element.isSynthetic) {
1950 _addElement(element);
1951 }
1952 // Don't visit children (such as property accessors).
1953 }
1954
1955 @override
1956 visitTypeParameterElement(TypeParameterElement element) {}
1957
1958 void _addElement(Element element) {
1959 if (element != null) {
1960 matcher._allElements.add(element);
1961 matcher._removedElements.add(element);
1962 }
1963 }
1964 }
1965
1966 /**
1967 * Describes how two [Token]s are different.
1968 */
1969 class _TokenDifferenceKind {
1970 static const COMMENT = const _TokenDifferenceKind('COMMENT');
1971 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC');
1972 static const CONTENT = const _TokenDifferenceKind('CONTENT');
1973 static const OFFSET = const _TokenDifferenceKind('OFFSET');
1974
1975 final String name;
1976
1977 const _TokenDifferenceKind(this.name);
1978
1979 @override
1980 String toString() => name;
1981 }
1982
1983 class _TokenPair {
1984 final _TokenDifferenceKind kind;
1985 final Token oldToken;
1986 final Token newToken;
1987 _TokenPair(this.kind, this.oldToken, this.newToken);
1988 }
OLDNEW
« no previous file with comments | « analyzer/lib/src/generated/incremental_resolution_validator.dart ('k') | analyzer/lib/src/generated/incremental_scanner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698