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

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

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 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, LINE_INFO;
27 import 'package:analyzer/task/model.dart' show ResultDescriptor, TargetedResult;
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(
907 this.oldEntry,
908 this.newSourceEntry,
909 this.newUnitEntry,
910 this._definingUnit,
911 this._updateOffset,
912 this._updateEndOld,
913 this._updateEndNew) {
914 _updateDelta = _updateEndNew - _updateEndOld;
915 _definingLibrary = _definingUnit.library;
916 _librarySource = _definingLibrary.source;
917 _source = _definingUnit.source;
918 _context = _definingUnit.context;
919 _typeProvider = _context.typeProvider;
920 }
921
922 /**
923 * Resolve [node], reporting any errors or warnings to the given listener.
924 *
925 * [node] - the root of the AST structure to be resolved.
926 *
927 * Returns `true` if resolution was successful.
928 */
929 bool resolve(AstNode node) {
930 logger.enter('resolve: $_definingUnit');
931 try {
932 AstNode rootNode = _findResolutionRoot(node);
933 _prepareResolutionContext(rootNode);
934 // update elements
935 _updateElementNameOffsets();
936 _buildElements(rootNode);
937 if (!_canBeIncrementallyResolved(rootNode)) {
938 return false;
939 }
940 // resolve
941 _resolveReferences(rootNode);
942 _computeConstants(rootNode);
943 _resolveErrors = errorListener.getErrorsForSource(_source);
944 // verify
945 _verify(rootNode);
946 _context.invalidateLibraryHints(_librarySource);
947 // update entry errors
948 _updateEntry();
949 // notify unit
950 _definingUnit.afterIncrementalResolution();
951 // OK
952 return true;
953 } finally {
954 logger.exit();
955 }
956 }
957
958 void _buildElements(AstNode node) {
959 LoggingTimer timer = logger.startTimer();
960 try {
961 ElementHolder holder = new ElementHolder();
962 ElementBuilder builder = new ElementBuilder(holder);
963 if (_resolutionContext.enclosingClassDeclaration != null) {
964 builder.visitClassDeclarationIncrementally(
965 _resolutionContext.enclosingClassDeclaration);
966 }
967 node.accept(builder);
968 } finally {
969 timer.stop('build elements');
970 }
971 }
972
973 /**
974 * Return `true` if [node] does not have element model changes, or these
975 * changes can be incrementally propagated.
976 */
977 bool _canBeIncrementallyResolved(AstNode node) {
978 // If we are replacing the whole declaration, this means that its signature
979 // is changed. It might be an API change, or not.
980 //
981 // If, for example, a required parameter is changed, it is not an API
982 // change, but we want to find the existing corresponding Element in the
983 // enclosing one, set it for the node and update as needed.
984 //
985 // If, for example, the name of a method is changed, it is an API change,
986 // we need to know the old Element and the new Element. Again, we need to
987 // check the whole enclosing Element.
988 if (node is Declaration) {
989 node = node.parent;
990 }
991 Element element = _getElement(node);
992 DeclarationMatcher matcher = new DeclarationMatcher();
993 DeclarationMatchKind matchKind = matcher.matches(node, element);
994 if (matchKind == DeclarationMatchKind.MATCH) {
995 return true;
996 }
997 // mismatch that cannot be incrementally fixed
998 return false;
999 }
1000
1001 /**
1002 * Return `true` if the given node can be resolved independently of any other
1003 * nodes.
1004 *
1005 * *Note*: This method needs to be kept in sync with
1006 * [ScopeBuilder.ContextBuilder].
1007 *
1008 * [node] - the node being tested.
1009 */
1010 bool _canBeResolved(AstNode node) => node is ClassDeclaration ||
1011 node is ClassTypeAlias ||
1012 node is CompilationUnit ||
1013 node is ConstructorDeclaration ||
1014 node is FunctionDeclaration ||
1015 node is FunctionTypeAlias ||
1016 node is MethodDeclaration ||
1017 node is TopLevelVariableDeclaration;
1018
1019 /**
1020 * Compute a value for all of the constants in the given [node].
1021 */
1022 void _computeConstants(AstNode node) {
1023 // compute values
1024 {
1025 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
1026 ConstantValueComputer computer = new ConstantValueComputer(
1027 _context, _typeProvider, _context.declaredVariables);
1028 computer.add(unit, _source, _librarySource);
1029 computer.computeValues();
1030 }
1031 // validate
1032 {
1033 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
1034 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
1035 _definingLibrary, _typeProvider, _context.declaredVariables);
1036 node.accept(constantVerifier);
1037 }
1038 }
1039
1040 /**
1041 * Starting at [node], find the smallest AST node that can be resolved
1042 * independently of any other nodes. Return the node that was found.
1043 *
1044 * [node] - the node at which the search is to begin
1045 *
1046 * Throws [AnalysisException] if there is no such node.
1047 */
1048 AstNode _findResolutionRoot(AstNode node) {
1049 while (node != null) {
1050 if (_canBeResolved(node)) {
1051 return node;
1052 }
1053 node = node.parent;
1054 }
1055 throw new AnalysisException("Cannot resolve node: no resolvable node");
1056 }
1057
1058 /**
1059 * Return the element defined by [node], or `null` if the node does not
1060 * define an element.
1061 */
1062 Element _getElement(AstNode node) {
1063 if (node is Declaration) {
1064 return node.element;
1065 } else if (node is CompilationUnit) {
1066 return node.element;
1067 }
1068 return null;
1069 }
1070
1071 void _prepareResolutionContext(AstNode node) {
1072 if (_resolutionContext == null) {
1073 _resolutionContext =
1074 ResolutionContextBuilder.contextFor(node, errorListener);
1075 }
1076 }
1077
1078 _resolveReferences(AstNode node) {
1079 LoggingTimer timer = logger.startTimer();
1080 try {
1081 _prepareResolutionContext(node);
1082 Scope scope = _resolutionContext.scope;
1083 // resolve types
1084 {
1085 TypeResolverVisitor visitor = new TypeResolverVisitor(
1086 _definingLibrary, _source, _typeProvider, errorListener,
1087 nameScope: scope);
1088 node.accept(visitor);
1089 }
1090 // resolve variables
1091 {
1092 VariableResolverVisitor visitor = new VariableResolverVisitor(
1093 _definingLibrary, _source, _typeProvider, errorListener,
1094 nameScope: scope);
1095 node.accept(visitor);
1096 }
1097 // resolve references
1098 {
1099 ResolverVisitor visitor = new ResolverVisitor(
1100 _definingLibrary, _source, _typeProvider, errorListener,
1101 nameScope: scope);
1102 if (_resolutionContext.enclosingClassDeclaration != null) {
1103 visitor.visitClassDeclarationIncrementally(
1104 _resolutionContext.enclosingClassDeclaration);
1105 }
1106 if (node is Comment) {
1107 visitor.resolveOnlyCommentInFunctionBody = true;
1108 node = node.parent;
1109 }
1110 visitor.initForIncrementalResolution();
1111 node.accept(visitor);
1112 }
1113 } finally {
1114 timer.stop('resolve references');
1115 }
1116 }
1117
1118 void _shiftEntryErrors() {
1119 if (oldEntry != null) {
1120 _shiftEntryErrors_OLD();
1121 } else {
1122 _shiftEntryErrors_NEW();
1123 }
1124 }
1125
1126 void _shiftEntryErrors_NEW() {
1127 _shiftErrors_NEW(HINTS);
1128 _shiftErrors_NEW(RESOLVE_REFERENCES_ERRORS);
1129 _shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
1130 _shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
1131 _shiftErrors_NEW(VERIFY_ERRORS);
1132 }
1133
1134 void _shiftEntryErrors_OLD() {
1135 _shiftErrors_OLD(DartEntry.RESOLUTION_ERRORS);
1136 _shiftErrors_OLD(DartEntry.VERIFICATION_ERRORS);
1137 _shiftErrors_OLD(DartEntry.HINTS);
1138 _shiftErrors_OLD(DartEntry.LINTS);
1139 }
1140
1141 void _shiftErrors(List<AnalysisError> errors) {
1142 for (AnalysisError error in errors) {
1143 int errorOffset = error.offset;
1144 if (errorOffset > _updateOffset) {
1145 error.offset += _updateDelta;
1146 }
1147 }
1148 }
1149
1150 void _shiftErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor) {
1151 List<AnalysisError> errors = newUnitEntry.getValue(descriptor);
1152 _shiftErrors(errors);
1153 }
1154
1155 void _shiftErrors_OLD(DataDescriptor<List<AnalysisError>> descriptor) {
1156 List<AnalysisError> errors =
1157 oldEntry.getValueInLibrary(descriptor, _librarySource);
1158 _shiftErrors(errors);
1159 }
1160
1161 void _updateElementNameOffsets() {
1162 LoggingTimer timer = logger.startTimer();
1163 try {
1164 _definingUnit
1165 .accept(new _ElementNameOffsetUpdater(_updateOffset, _updateDelta));
1166 } finally {
1167 timer.stop('update element offsets');
1168 }
1169 }
1170
1171 void _updateEntry() {
1172 if (oldEntry != null) {
1173 _updateEntry_OLD();
1174 } else {
1175 _updateEntry_NEW();
1176 }
1177 }
1178
1179 void _updateEntry_NEW() {
1180 _updateErrors_NEW(RESOLVE_REFERENCES_ERRORS, _resolveErrors);
1181 _updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
1182 _updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
1183 _updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
1184 // invalidate results we don't update incrementally
1185 newUnitEntry.setState(USED_IMPORTED_ELEMENTS, CacheState.INVALID);
1186 newUnitEntry.setState(USED_LOCAL_ELEMENTS, CacheState.INVALID);
1187 newUnitEntry.setState(HINTS, CacheState.INVALID);
1188 }
1189
1190 void _updateEntry_OLD() {
1191 _updateErrors_OLD(DartEntry.RESOLUTION_ERRORS, _resolveErrors);
1192 _updateErrors_OLD(DartEntry.VERIFICATION_ERRORS, _verifyErrors);
1193 }
1194
1195 List<AnalysisError> _updateErrors(
1196 List<AnalysisError> oldErrors, List<AnalysisError> newErrors) {
1197 List<AnalysisError> errors = new List<AnalysisError>();
1198 // add updated old errors
1199 for (AnalysisError error in oldErrors) {
1200 int errorOffset = error.offset;
1201 if (errorOffset < _updateOffset) {
1202 errors.add(error);
1203 } else if (errorOffset > _updateEndOld) {
1204 error.offset += _updateDelta;
1205 errors.add(error);
1206 }
1207 }
1208 // add new errors
1209 for (AnalysisError error in newErrors) {
1210 int errorOffset = error.offset;
1211 if (errorOffset > _updateOffset && errorOffset < _updateEndNew) {
1212 errors.add(error);
1213 }
1214 }
1215 // done
1216 return errors;
1217 }
1218
1219 void _updateErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor,
1220 List<AnalysisError> newErrors) {
1221 List<AnalysisError> oldErrors = newUnitEntry.getValue(descriptor);
1222 List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
1223 newUnitEntry.setValueIncremental(descriptor, errors);
1224 }
1225
1226 void _updateErrors_OLD(DataDescriptor<List<AnalysisError>> descriptor,
1227 List<AnalysisError> newErrors) {
1228 List<AnalysisError> oldErrors =
1229 oldEntry.getValueInLibrary(descriptor, _librarySource);
1230 List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
1231 oldEntry.setValueInLibrary(descriptor, _librarySource, errors);
1232 }
1233
1234 void _verify(AstNode node) {
1235 LoggingTimer timer = logger.startTimer();
1236 try {
1237 RecordingErrorListener errorListener = new RecordingErrorListener();
1238 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
1239 ErrorVerifier errorVerifier = new ErrorVerifier(
1240 errorReporter,
1241 _definingLibrary,
1242 _typeProvider,
1243 new InheritanceManager(_definingLibrary),
1244 _context.analysisOptions.enableSuperMixins);
1245 if (_resolutionContext.enclosingClassDeclaration != null) {
1246 errorVerifier.visitClassDeclarationIncrementally(
1247 _resolutionContext.enclosingClassDeclaration);
1248 }
1249 node.accept(errorVerifier);
1250 _verifyErrors = errorListener.getErrorsForSource(_source);
1251 } finally {
1252 timer.stop('verify');
1253 }
1254 }
1255 }
1256
1257 class PoorMansIncrementalResolver {
1258 final TypeProvider _typeProvider;
1259 final Source _unitSource;
1260
1261 /**
1262 * The [DartEntry] corresponding to the source being resolved.
1263 */
1264 DartEntry _oldEntry;
1265
1266 /**
1267 * The [CacheEntry] corresponding to the source being resolved.
1268 */
1269 CacheEntry _newSourceEntry;
1270
1271 /**
1272 * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
1273 */
1274 CacheEntry _newUnitEntry;
1275
1276 final CompilationUnit _oldUnit;
1277 final AnalysisOptions _options;
1278 CompilationUnitElement _unitElement;
1279
1280 int _updateOffset;
1281 int _updateDelta;
1282 int _updateEndOld;
1283 int _updateEndNew;
1284
1285 LineInfo _newLineInfo;
1286 List<AnalysisError> _newScanErrors = <AnalysisError>[];
1287 List<AnalysisError> _newParseErrors = <AnalysisError>[];
1288
1289 PoorMansIncrementalResolver(
1290 this._typeProvider,
1291 this._unitSource,
1292 this._oldEntry,
1293 this._newSourceEntry,
1294 this._newUnitEntry,
1295 this._oldUnit,
1296 bool resolveApiChanges,
1297 this._options) {
1298 _resolveApiChanges = resolveApiChanges;
1299 }
1300
1301 /**
1302 * Attempts to update [_oldUnit] to the state corresponding to [newCode].
1303 * Returns `true` if success, or `false` otherwise.
1304 * The [_oldUnit] might be damaged.
1305 */
1306 bool resolve(String newCode) {
1307 logger.enter('diff/resolve $_unitSource');
1308 try {
1309 // prepare old unit
1310 if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) {
1311 logger.log('Unbalanced number of curly brackets in the old unit.');
1312 return false;
1313 }
1314 _unitElement = _oldUnit.element;
1315 // prepare new unit
1316 CompilationUnit newUnit = _parseUnit(newCode);
1317 if (!_areCurlyBracketsBalanced(newUnit.beginToken)) {
1318 logger.log('Unbalanced number of curly brackets in the new unit.');
1319 return false;
1320 }
1321 // find difference
1322 _TokenPair firstPair =
1323 _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken);
1324 _TokenPair lastPair =
1325 _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken);
1326 if (firstPair != null && lastPair != null) {
1327 int firstOffsetOld = firstPair.oldToken.offset;
1328 int firstOffsetNew = firstPair.newToken.offset;
1329 int lastOffsetOld = lastPair.oldToken.end;
1330 int lastOffsetNew = lastPair.newToken.end;
1331 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld);
1332 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld);
1333 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew);
1334 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew);
1335 // check for a whitespace only change
1336 if (identical(lastPair.oldToken, firstPair.oldToken) &&
1337 identical(lastPair.newToken, firstPair.newToken)) {
1338 _updateOffset = beginOffsetOld - 1;
1339 _updateEndOld = endOffsetOld;
1340 _updateEndNew = endOffsetNew;
1341 _updateDelta = newUnit.length - _oldUnit.length;
1342 // A Dart documentation comment change.
1343 if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) {
1344 bool success = _resolveCommentDoc(newUnit, firstPair);
1345 logger.log('Documentation comment resolved: $success');
1346 return success;
1347 }
1348 // A pure whitespace change.
1349 if (firstPair.kind == _TokenDifferenceKind.OFFSET) {
1350 logger.log('Whitespace change.');
1351 _shiftTokens(firstPair.oldToken);
1352 {
1353 IncrementalResolver incrementalResolver = new IncrementalResolver(
1354 _oldEntry,
1355 _newSourceEntry,
1356 _newUnitEntry,
1357 _unitElement,
1358 _updateOffset,
1359 _updateEndOld,
1360 _updateEndNew);
1361 incrementalResolver._updateElementNameOffsets();
1362 incrementalResolver._shiftEntryErrors();
1363 }
1364 _updateEntry();
1365 logger.log('Success.');
1366 return true;
1367 }
1368 // fall-through, end-of-line comment
1369 }
1370 // Find nodes covering the "old" and "new" token ranges.
1371 AstNode oldNode =
1372 _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld - 1);
1373 AstNode newNode =
1374 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew - 1);
1375 logger.log(() => 'oldNode: $oldNode');
1376 logger.log(() => 'newNode: $newNode');
1377 // Try to find the smallest common node, a FunctionBody currently.
1378 {
1379 List<AstNode> oldParents = _getParents(oldNode);
1380 List<AstNode> newParents = _getParents(newNode);
1381 int length = math.min(oldParents.length, newParents.length);
1382 bool found = false;
1383 for (int i = 0; i < length; i++) {
1384 AstNode oldParent = oldParents[i];
1385 AstNode newParent = newParents[i];
1386 if (oldParent is ConstructorInitializer ||
1387 newParent is ConstructorInitializer) {
1388 logger.log('Failure: changes in constant constructor initializers'
1389 ' may cause external changes in constant objects.');
1390 return false;
1391 }
1392 if (oldParent is FunctionDeclaration &&
1393 newParent is FunctionDeclaration ||
1394 oldParent is MethodDeclaration &&
1395 newParent is MethodDeclaration ||
1396 oldParent is ConstructorDeclaration &&
1397 newParent is ConstructorDeclaration) {
1398 Element oldElement = (oldParent as Declaration).element;
1399 if (new DeclarationMatcher().matches(newParent, oldElement) ==
1400 DeclarationMatchKind.MATCH) {
1401 oldNode = oldParent;
1402 newNode = newParent;
1403 found = true;
1404 }
1405 }
1406 if (oldParent is BlockFunctionBody &&
1407 newParent is BlockFunctionBody) {
1408 oldNode = oldParent;
1409 newNode = newParent;
1410 found = true;
1411 break;
1412 }
1413 }
1414 if (!found) {
1415 logger.log('Failure: no enclosing function body or executable.');
1416 return false;
1417 }
1418 // fail if a comment change outside the bodies
1419 if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
1420 if (beginOffsetOld <= oldNode.offset ||
1421 beginOffsetNew <= newNode.offset) {
1422 logger.log('Failure: comment outside a function body.');
1423 return false;
1424 }
1425 }
1426 }
1427 logger.log(() => 'oldNode: $oldNode');
1428 logger.log(() => 'newNode: $newNode');
1429 // prepare update range
1430 _updateOffset = oldNode.offset;
1431 _updateEndOld = oldNode.end;
1432 _updateEndNew = newNode.end;
1433 _updateDelta = _updateEndNew - _updateEndOld;
1434 // replace node
1435 NodeReplacer.replace(oldNode, newNode);
1436 // update token references
1437 {
1438 Token oldBeginToken = _getBeginTokenNotComment(oldNode);
1439 Token newBeginToken = _getBeginTokenNotComment(newNode);
1440 if (oldBeginToken.previous.type == TokenType.EOF) {
1441 _oldUnit.beginToken = newBeginToken;
1442 } else {
1443 oldBeginToken.previous.setNext(newBeginToken);
1444 }
1445 newNode.endToken.setNext(oldNode.endToken.next);
1446 _shiftTokens(oldNode.endToken.next);
1447 }
1448 // perform incremental resolution
1449 IncrementalResolver incrementalResolver = new IncrementalResolver(
1450 _oldEntry,
1451 _newSourceEntry,
1452 _newUnitEntry,
1453 _unitElement,
1454 _updateOffset,
1455 _updateEndOld,
1456 _updateEndNew);
1457 bool success = incrementalResolver.resolve(newNode);
1458 // check if success
1459 if (!success) {
1460 logger.log('Failure: element model changed.');
1461 return false;
1462 }
1463 // update DartEntry
1464 _updateEntry();
1465 logger.log('Success.');
1466 return true;
1467 }
1468 } catch (e, st) {
1469 logger.log(e);
1470 logger.log(st);
1471 logger.log('Failure: exception.');
1472 } finally {
1473 logger.exit();
1474 }
1475 return false;
1476 }
1477
1478 CompilationUnit _parseUnit(String code) {
1479 LoggingTimer timer = logger.startTimer();
1480 try {
1481 Token token = _scan(code);
1482 RecordingErrorListener errorListener = new RecordingErrorListener();
1483 Parser parser = new Parser(_unitSource, errorListener);
1484 AnalysisOptions options = _unitElement.context.analysisOptions;
1485 parser.parseGenericMethods = options.enableGenericMethods;
1486 CompilationUnit unit = parser.parseCompilationUnit(token);
1487 _newParseErrors = errorListener.errors;
1488 return unit;
1489 } finally {
1490 timer.stop('parse');
1491 }
1492 }
1493
1494 /**
1495 * Attempts to resolve a documentation comment change.
1496 * Returns `true` if success.
1497 */
1498 bool _resolveCommentDoc(CompilationUnit newUnit, _TokenPair firstPair) {
1499 Token oldToken = firstPair.oldToken;
1500 Token newToken = firstPair.newToken;
1501 CommentToken oldComments = oldToken.precedingComments;
1502 CommentToken newComments = newToken.precedingComments;
1503 if (oldComments == null || newComments == null) {
1504 return false;
1505 }
1506 // find nodes
1507 int offset = oldComments.offset;
1508 logger.log('offset: $offset');
1509 Comment oldComment = _findNodeCovering(_oldUnit, offset, offset);
1510 Comment newComment = _findNodeCovering(newUnit, offset, offset);
1511 logger.log('oldComment.beginToken: ${oldComment.beginToken}');
1512 logger.log('newComment.beginToken: ${newComment.beginToken}');
1513 _updateOffset = oldToken.offset - 1;
1514 // update token references
1515 _shiftTokens(firstPair.oldToken);
1516 _setPrecedingComments(oldToken, newComment.tokens.first);
1517 // replace node
1518 NodeReplacer.replace(oldComment, newComment);
1519 // update elements
1520 IncrementalResolver incrementalResolver = new IncrementalResolver(
1521 _oldEntry,
1522 _newSourceEntry,
1523 _newUnitEntry,
1524 _unitElement,
1525 _updateOffset,
1526 _updateEndOld,
1527 _updateEndNew);
1528 incrementalResolver._updateElementNameOffsets();
1529 incrementalResolver._shiftEntryErrors();
1530 _updateEntry();
1531 // resolve references in the comment
1532 incrementalResolver._resolveReferences(newComment);
1533 // OK
1534 return true;
1535 }
1536
1537 Token _scan(String code) {
1538 RecordingErrorListener errorListener = new RecordingErrorListener();
1539 CharSequenceReader reader = new CharSequenceReader(code);
1540 Scanner scanner = new Scanner(_unitSource, reader, errorListener);
1541 Token token = scanner.tokenize();
1542 _newLineInfo = new LineInfo(scanner.lineStarts);
1543 _newScanErrors = errorListener.errors;
1544 return token;
1545 }
1546
1547 /**
1548 * Set the given [comment] as a "precedingComments" for [token].
1549 */
1550 void _setPrecedingComments(Token token, CommentToken comment) {
1551 if (token is BeginTokenWithComment) {
1552 token.precedingComments = comment;
1553 } else if (token is KeywordTokenWithComment) {
1554 token.precedingComments = comment;
1555 } else if (token is StringTokenWithComment) {
1556 token.precedingComments = comment;
1557 } else if (token is TokenWithComment) {
1558 token.precedingComments = comment;
1559 } else {
1560 Type parentType = token != null ? token.runtimeType : null;
1561 throw new AnalysisException('Uknown parent token type: $parentType');
1562 }
1563 }
1564
1565 void _shiftTokens(Token token) {
1566 while (token != null) {
1567 if (token.offset > _updateOffset) {
1568 token.offset += _updateDelta;
1569 }
1570 // comments
1571 _shiftTokens(token.precedingComments);
1572 if (token is DocumentationCommentToken) {
1573 for (Token reference in token.references) {
1574 _shiftTokens(reference);
1575 }
1576 }
1577 // next
1578 if (token.type == TokenType.EOF) {
1579 break;
1580 }
1581 token = token.next;
1582 }
1583 }
1584
1585 void _updateEntry() {
1586 if (_oldEntry != null) {
1587 _updateEntry_OLD();
1588 } else {
1589 _updateEntry_NEW();
1590 }
1591 }
1592
1593 void _updateEntry_NEW() {
1594 _newSourceEntry.setState(DART_ERRORS, CacheState.INVALID);
1595 // scan results
1596 _newSourceEntry.setState(SCAN_ERRORS, CacheState.INVALID);
1597 List<TargetedResult> scanDeps = <TargetedResult>[
1598 new TargetedResult(_unitSource, CONTENT)
1599 ];
1600 _newSourceEntry.setValue(LINE_INFO, _newLineInfo, scanDeps);
1601 _newSourceEntry.setValue(SCAN_ERRORS, _newScanErrors, scanDeps);
1602 // parse results
1603 List<TargetedResult> parseDeps = <TargetedResult>[
1604 new TargetedResult(_unitSource, TOKEN_STREAM)
1605 ];
1606 _newSourceEntry.setState(PARSE_ERRORS, CacheState.INVALID);
1607 _newSourceEntry.setValue(PARSE_ERRORS, _newParseErrors, parseDeps);
1608 _newSourceEntry.setValue(PARSED_UNIT, _oldUnit, parseDeps);
1609 }
1610
1611 void _updateEntry_OLD() {
1612 _oldEntry.setValue(SourceEntry.LINE_INFO, _newLineInfo);
1613 _oldEntry.setValue(DartEntry.SCAN_ERRORS, _newScanErrors);
1614 _oldEntry.setValue(DartEntry.PARSE_ERRORS, _newParseErrors);
1615 }
1616
1617 /**
1618 * Checks if [token] has a balanced number of open and closed curly brackets.
1619 */
1620 static bool _areCurlyBracketsBalanced(Token token) {
1621 int numOpen = _getTokenCount(token, TokenType.OPEN_CURLY_BRACKET);
1622 int numOpen2 =
1623 _getTokenCount(token, TokenType.STRING_INTERPOLATION_EXPRESSION);
1624 int numClosed = _getTokenCount(token, TokenType.CLOSE_CURLY_BRACKET);
1625 return numOpen + numOpen2 == numClosed;
1626 }
1627
1628 static _TokenDifferenceKind _compareToken(
1629 Token oldToken, Token newToken, int delta, bool forComment) {
1630 while (true) {
1631 if (oldToken == null && newToken == null) {
1632 return null;
1633 }
1634 if (oldToken == null || newToken == null) {
1635 return _TokenDifferenceKind.CONTENT;
1636 }
1637 if (oldToken.type != newToken.type) {
1638 return _TokenDifferenceKind.CONTENT;
1639 }
1640 if (oldToken.lexeme != newToken.lexeme) {
1641 return _TokenDifferenceKind.CONTENT;
1642 }
1643 if (newToken.offset - oldToken.offset != delta) {
1644 return _TokenDifferenceKind.OFFSET;
1645 }
1646 // continue if comment tokens are being checked
1647 if (!forComment) {
1648 break;
1649 }
1650 oldToken = oldToken.next;
1651 newToken = newToken.next;
1652 }
1653 return null;
1654 }
1655
1656 static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
1657 while (true) {
1658 if (oldToken.type == TokenType.EOF && newToken.type == TokenType.EOF) {
1659 return null;
1660 }
1661 if (oldToken.type == TokenType.EOF || newToken.type == TokenType.EOF) {
1662 return new _TokenPair(_TokenDifferenceKind.CONTENT, oldToken, newToken);
1663 }
1664 // compare comments
1665 {
1666 Token oldComment = oldToken.precedingComments;
1667 Token newComment = newToken.precedingComments;
1668 if (_compareToken(oldComment, newComment, 0, 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 // compare tokens
1678 _TokenDifferenceKind diffKind =
1679 _compareToken(oldToken, newToken, 0, false);
1680 if (diffKind != null) {
1681 return new _TokenPair(diffKind, oldToken, newToken);
1682 }
1683 // next tokens
1684 oldToken = oldToken.next;
1685 newToken = newToken.next;
1686 }
1687 // no difference
1688 return null;
1689 }
1690
1691 static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
1692 int delta = newToken.offset - oldToken.offset;
1693 while (oldToken.previous != oldToken && newToken.previous != newToken) {
1694 // compare tokens
1695 _TokenDifferenceKind diffKind =
1696 _compareToken(oldToken, newToken, delta, false);
1697 if (diffKind != null) {
1698 return new _TokenPair(diffKind, oldToken.next, newToken.next);
1699 }
1700 // compare comments
1701 {
1702 Token oldComment = oldToken.precedingComments;
1703 Token newComment = newToken.precedingComments;
1704 if (_compareToken(oldComment, newComment, delta, true) != null) {
1705 _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
1706 if (oldComment is DocumentationCommentToken ||
1707 newComment is DocumentationCommentToken) {
1708 diffKind = _TokenDifferenceKind.COMMENT_DOC;
1709 }
1710 return new _TokenPair(diffKind, oldToken, newToken);
1711 }
1712 }
1713 // next tokens
1714 oldToken = oldToken.previous;
1715 newToken = newToken.previous;
1716 }
1717 return null;
1718 }
1719
1720 static AstNode _findNodeCovering(AstNode root, int offset, int end) {
1721 NodeLocator nodeLocator = new NodeLocator(offset, end);
1722 return nodeLocator.searchWithin(root);
1723 }
1724
1725 static Token _getBeginTokenNotComment(AstNode node) {
1726 Token oldBeginToken = node.beginToken;
1727 if (oldBeginToken is CommentToken) {
1728 oldBeginToken = (oldBeginToken as CommentToken).parent;
1729 }
1730 return oldBeginToken;
1731 }
1732
1733 static List<AstNode> _getParents(AstNode node) {
1734 List<AstNode> parents = <AstNode>[];
1735 while (node != null) {
1736 parents.insert(0, node);
1737 node = node.parent;
1738 }
1739 return parents;
1740 }
1741
1742 /**
1743 * Returns number of tokens with the given [type].
1744 */
1745 static int _getTokenCount(Token token, TokenType type) {
1746 int count = 0;
1747 while (token.type != TokenType.EOF) {
1748 if (token.type == type) {
1749 count++;
1750 }
1751 token = token.next;
1752 }
1753 return count;
1754 }
1755 }
1756
1757 /**
1758 * The context to resolve an [AstNode] in.
1759 */
1760 class ResolutionContext {
1761 CompilationUnitElement enclosingUnit;
1762 ClassDeclaration enclosingClassDeclaration;
1763 ClassElement enclosingClass;
1764 Scope scope;
1765 }
1766
1767 /**
1768 * Instances of the class [ResolutionContextBuilder] build the context for a
1769 * given node in an AST structure. At the moment, this class only handles
1770 * top-level and class-level declarations.
1771 */
1772 class ResolutionContextBuilder {
1773 /**
1774 * The listener to which analysis errors will be reported.
1775 */
1776 final AnalysisErrorListener _errorListener;
1777
1778 /**
1779 * The class containing the enclosing [CompilationUnitElement].
1780 */
1781 CompilationUnitElement _enclosingUnit;
1782
1783 /**
1784 * The class containing the enclosing [ClassDeclaration], or `null` if we are
1785 * not in the scope of a class.
1786 */
1787 ClassDeclaration _enclosingClassDeclaration;
1788
1789 /**
1790 * The class containing the enclosing [ClassElement], or `null` if we are not
1791 * in the scope of a class.
1792 */
1793 ClassElement _enclosingClass;
1794
1795 /**
1796 * Initialize a newly created scope builder to generate a scope that will
1797 * report errors to the given listener.
1798 */
1799 ResolutionContextBuilder(this._errorListener);
1800
1801 Scope _scopeFor(AstNode node) {
1802 if (node is CompilationUnit) {
1803 return _scopeForAstNode(node);
1804 }
1805 AstNode parent = node.parent;
1806 if (parent == null) {
1807 throw new AnalysisException(
1808 "Cannot create scope: node is not part of a CompilationUnit");
1809 }
1810 return _scopeForAstNode(parent);
1811 }
1812
1813 /**
1814 * Return the scope in which the given AST structure should be resolved.
1815 *
1816 * *Note:* This method needs to be kept in sync with
1817 * [IncrementalResolver.canBeResolved].
1818 *
1819 * [node] - the root of the AST structure to be resolved.
1820 *
1821 * Throws [AnalysisException] if the AST structure has not been resolved or
1822 * is not part of a [CompilationUnit]
1823 */
1824 Scope _scopeForAstNode(AstNode node) {
1825 if (node is CompilationUnit) {
1826 return _scopeForCompilationUnit(node);
1827 }
1828 AstNode parent = node.parent;
1829 if (parent == null) {
1830 throw new AnalysisException(
1831 "Cannot create scope: node is not part of a CompilationUnit");
1832 }
1833 Scope scope = _scopeForAstNode(parent);
1834 if (node is ClassDeclaration) {
1835 _enclosingClassDeclaration = node;
1836 _enclosingClass = node.element;
1837 if (_enclosingClass == null) {
1838 throw new AnalysisException(
1839 "Cannot build a scope for an unresolved class");
1840 }
1841 scope = new ClassScope(
1842 new TypeParameterScope(scope, _enclosingClass), _enclosingClass);
1843 } else if (node is ClassTypeAlias) {
1844 ClassElement element = node.element;
1845 if (element == null) {
1846 throw new AnalysisException(
1847 "Cannot build a scope for an unresolved class type alias");
1848 }
1849 scope = new ClassScope(new TypeParameterScope(scope, element), element);
1850 } else if (node is ConstructorDeclaration) {
1851 ConstructorElement element = node.element;
1852 if (element == null) {
1853 throw new AnalysisException(
1854 "Cannot build a scope for an unresolved constructor");
1855 }
1856 FunctionScope functionScope = new FunctionScope(scope, element);
1857 functionScope.defineParameters();
1858 scope = functionScope;
1859 } else if (node is FunctionDeclaration) {
1860 ExecutableElement element = node.element;
1861 if (element == null) {
1862 throw new AnalysisException(
1863 "Cannot build a scope for an unresolved function");
1864 }
1865 FunctionScope functionScope = new FunctionScope(scope, element);
1866 functionScope.defineParameters();
1867 scope = functionScope;
1868 } else if (node is FunctionTypeAlias) {
1869 scope = new FunctionTypeScope(scope, node.element);
1870 } else if (node is MethodDeclaration) {
1871 ExecutableElement element = node.element;
1872 if (element == null) {
1873 throw new AnalysisException(
1874 "Cannot build a scope for an unresolved method");
1875 }
1876 FunctionScope functionScope = new FunctionScope(scope, element);
1877 functionScope.defineParameters();
1878 scope = functionScope;
1879 }
1880 return scope;
1881 }
1882
1883 Scope _scopeForCompilationUnit(CompilationUnit node) {
1884 _enclosingUnit = node.element;
1885 if (_enclosingUnit == null) {
1886 throw new AnalysisException(
1887 "Cannot create scope: compilation unit is not resolved");
1888 }
1889 LibraryElement libraryElement = _enclosingUnit.library;
1890 if (libraryElement == null) {
1891 throw new AnalysisException(
1892 "Cannot create scope: compilation unit is not part of a library");
1893 }
1894 return new LibraryScope(libraryElement, _errorListener);
1895 }
1896
1897 /**
1898 * Return the context in which the given AST structure should be resolved.
1899 *
1900 * [node] - the root of the AST structure to be resolved.
1901 * [errorListener] - the listener to which analysis errors will be reported.
1902 *
1903 * Throws [AnalysisException] if the AST structure has not been resolved or
1904 * is not part of a [CompilationUnit]
1905 */
1906 static ResolutionContext contextFor(
1907 AstNode node, AnalysisErrorListener errorListener) {
1908 if (node == null) {
1909 throw new AnalysisException("Cannot create context: node is null");
1910 }
1911 // build scope
1912 ResolutionContextBuilder builder =
1913 new ResolutionContextBuilder(errorListener);
1914 Scope scope = builder._scopeFor(node);
1915 // prepare context
1916 ResolutionContext context = new ResolutionContext();
1917 context.scope = scope;
1918 context.enclosingUnit = builder._enclosingUnit;
1919 context.enclosingClassDeclaration = builder._enclosingClassDeclaration;
1920 context.enclosingClass = builder._enclosingClass;
1921 return context;
1922 }
1923 }
1924
1925 /**
1926 * Instances of the class [_DeclarationMismatchException] represent an exception
1927 * that is thrown when the element model defined by a given AST structure does
1928 * not match an existing element model.
1929 */
1930 class _DeclarationMismatchException {}
1931
1932 class _ElementNameOffsetUpdater extends GeneralizingElementVisitor {
1933 final int updateOffset;
1934 final int updateDelta;
1935
1936 _ElementNameOffsetUpdater(this.updateOffset, this.updateDelta);
1937
1938 @override
1939 visitElement(Element element) {
1940 int nameOffset = element.nameOffset;
1941 if (nameOffset > updateOffset) {
1942 (element as ElementImpl).nameOffset = nameOffset + updateDelta;
1943 }
1944 super.visitElement(element);
1945 }
1946 }
1947
1948 class _ElementsGatherer extends GeneralizingElementVisitor {
1949 final DeclarationMatcher matcher;
1950
1951 _ElementsGatherer(this.matcher);
1952
1953 void addElements(List<Element> elements) {
1954 for (Element element in elements) {
1955 if (!element.isSynthetic) {
1956 _addElement(element);
1957 }
1958 }
1959 }
1960
1961 @override
1962 visitElement(Element element) {
1963 _addElement(element);
1964 super.visitElement(element);
1965 }
1966
1967 @override
1968 visitExecutableElement(ExecutableElement element) {
1969 _addElement(element);
1970 }
1971
1972 @override
1973 visitParameterElement(ParameterElement element) {}
1974
1975 @override
1976 visitPropertyAccessorElement(PropertyAccessorElement element) {
1977 if (!element.isSynthetic) {
1978 _addElement(element);
1979 }
1980 // Don't visit children (such as synthetic setter parameters).
1981 }
1982
1983 @override
1984 visitPropertyInducingElement(PropertyInducingElement element) {
1985 if (!element.isSynthetic) {
1986 _addElement(element);
1987 }
1988 // Don't visit children (such as property accessors).
1989 }
1990
1991 @override
1992 visitTypeParameterElement(TypeParameterElement element) {}
1993
1994 void _addElement(Element element) {
1995 if (element != null) {
1996 matcher._allElements.add(element);
1997 matcher._removedElements.add(element);
1998 }
1999 }
2000 }
2001
2002 /**
2003 * Describes how two [Token]s are different.
2004 */
2005 class _TokenDifferenceKind {
2006 static const COMMENT = const _TokenDifferenceKind('COMMENT');
2007 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC');
2008 static const CONTENT = const _TokenDifferenceKind('CONTENT');
2009 static const OFFSET = const _TokenDifferenceKind('OFFSET');
2010
2011 final String name;
2012
2013 const _TokenDifferenceKind(this.name);
2014
2015 @override
2016 String toString() => name;
2017 }
2018
2019 class _TokenPair {
2020 final _TokenDifferenceKind kind;
2021 final Token oldToken;
2022 final Token newToken;
2023 _TokenPair(this.kind, this.oldToken, this.newToken);
2024 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698