OLD | NEW |
| (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 } | |
OLD | NEW |