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 // This code was auto-generated, is not intended to be edited, and is subject to | |
6 // significant change. Please see the README file for more information. | |
7 | |
8 library engine.src.index; | |
9 | |
10 import 'dart:collection' show Queue; | |
11 | |
12 import 'package:analyzer/index/index.dart'; | |
13 import 'package:analyzer/index/index_store.dart'; | |
14 import 'package:analyzer/src/generated/ast.dart'; | |
15 import 'package:analyzer/src/generated/element.dart'; | |
16 import 'package:analyzer/src/generated/engine.dart'; | |
17 import 'package:analyzer/src/generated/html.dart' as ht; | |
18 import 'package:analyzer/src/generated/java_core.dart'; | |
19 import 'package:analyzer/src/generated/java_engine.dart'; | |
20 import 'package:analyzer/src/generated/resolver.dart'; | |
21 import 'package:analyzer/src/generated/scanner.dart'; | |
22 import 'package:analyzer/src/generated/source.dart'; | |
23 | |
24 | |
25 /** | |
26 * Adds data to [store] based on the resolved Dart [unit]. | |
27 */ | |
28 void indexDartUnit(IndexStore store, AnalysisContext context, | |
29 CompilationUnit unit) { | |
30 CompilationUnitElement unitElement = unit.element; | |
31 bool mayIndex = store.aboutToIndexDart(context, unitElement); | |
32 if (!mayIndex) { | |
33 return; | |
34 } | |
35 unit.accept(new _IndexContributor(store)); | |
36 unit.accept(new _AngularDartIndexContributor(store)); | |
37 store.doneIndex(); | |
38 } | |
39 | |
40 | |
41 /** | |
42 * Adds data to [store] based on the resolved HTML [unit]. | |
43 */ | |
44 void indexHtmlUnit(IndexStore store, AnalysisContext context, ht.HtmlUnit unit) | |
45 { | |
46 HtmlElement unitElement = unit.element; | |
47 bool mayIndex = store.aboutToIndexHtml(context, unitElement); | |
48 if (!mayIndex) { | |
49 return; | |
50 } | |
51 unit.accept(new _AngularHtmlIndexContributor(store)); | |
52 store.doneIndex(); | |
53 } | |
54 | |
55 | |
56 /** | |
57 * Visits resolved [CompilationUnit] and adds Angular specific relationships | |
58 * into [IndexStore]. | |
59 */ | |
60 class _AngularDartIndexContributor extends GeneralizingAstVisitor<Object> { | |
61 final IndexStore _store; | |
62 | |
63 _AngularDartIndexContributor(this._store); | |
64 | |
65 @override | |
66 Object visitClassDeclaration(ClassDeclaration node) { | |
67 ClassElement classElement = node.element; | |
68 if (classElement != null) { | |
69 List<ToolkitObjectElement> toolkitObjects = classElement.toolkitObjects; | |
70 for (ToolkitObjectElement object in toolkitObjects) { | |
71 if (object is AngularComponentElement) { | |
72 _indexComponent(object); | |
73 } | |
74 if (object is AngularDecoratorElement) { | |
75 AngularDecoratorElement directive = object; | |
76 _indexDirective(directive); | |
77 } | |
78 } | |
79 } | |
80 // stop visiting | |
81 return null; | |
82 } | |
83 | |
84 @override | |
85 Object visitCompilationUnitMember(CompilationUnitMember node) => null; | |
86 | |
87 void _indexComponent(AngularComponentElement component) { | |
88 _indexProperties(component.properties); | |
89 } | |
90 | |
91 void _indexDirective(AngularDecoratorElement directive) { | |
92 _indexProperties(directive.properties); | |
93 } | |
94 | |
95 /** | |
96 * Index [FieldElement] references from [AngularPropertyElement]s. | |
97 */ | |
98 void _indexProperties(List<AngularPropertyElement> properties) { | |
99 for (AngularPropertyElement property in properties) { | |
100 FieldElement field = property.field; | |
101 if (field != null) { | |
102 int offset = property.fieldNameOffset; | |
103 if (offset == -1) { | |
104 continue; | |
105 } | |
106 int length = field.name.length; | |
107 Location location = new Location(property, offset, length); | |
108 // getter reference | |
109 if (property.propertyKind.callsGetter()) { | |
110 PropertyAccessorElement getter = field.getter; | |
111 if (getter != null) { | |
112 _store.recordRelationship(getter, | |
113 IndexConstants.IS_REFERENCED_BY_QUALIFIED, location); | |
114 } | |
115 } | |
116 // setter reference | |
117 if (property.propertyKind.callsSetter()) { | |
118 PropertyAccessorElement setter = field.setter; | |
119 if (setter != null) { | |
120 _store.recordRelationship(setter, | |
121 IndexConstants.IS_REFERENCED_BY_QUALIFIED, location); | |
122 } | |
123 } | |
124 } | |
125 } | |
126 } | |
127 } | |
128 | |
129 | |
130 /** | |
131 * Visits resolved [HtmlUnit] and adds relationships into [IndexStore]. | |
132 */ | |
133 class _AngularHtmlIndexContributor extends _ExpressionVisitor { | |
134 /** | |
135 * The [IndexStore] to record relations into. | |
136 */ | |
137 final IndexStore _store; | |
138 | |
139 /** | |
140 * The index contributor used to index Dart [Expression]s. | |
141 */ | |
142 _IndexContributor _indexContributor; | |
143 | |
144 HtmlElement _htmlUnitElement; | |
145 | |
146 /** | |
147 * Initialize a newly created Angular HTML index contributor. | |
148 * | |
149 * @param store the [IndexStore] to record relations into. | |
150 */ | |
151 _AngularHtmlIndexContributor(this._store) { | |
152 _indexContributor = new _AngularHtmlIndexContributor_forEmbeddedDart(_store, | |
153 this); | |
154 } | |
155 | |
156 @override | |
157 void visitExpression(Expression expression) { | |
158 // Formatter | |
159 if (expression is SimpleIdentifier) { | |
160 SimpleIdentifier identifier = expression; | |
Brian Wilkerson
2014/07/03 18:58:13
I'm not sure what the value of this local variable
scheglov
2014/07/03 19:06:16
Fixed.
Should it be a hint?
Brian Wilkerson
2014/07/03 20:48:19
Perhaps. If the original variable and the new vari
| |
161 Element element = identifier.bestElement; | |
162 if (element is AngularElement) { | |
163 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, | |
164 _createLocationForIdentifier(identifier)); | |
165 return; | |
166 } | |
167 } | |
168 // index as a normal Dart expression | |
169 expression.accept(_indexContributor); | |
170 } | |
171 | |
172 @override | |
173 Object visitHtmlUnit(ht.HtmlUnit node) { | |
174 _htmlUnitElement = node.element; | |
175 CompilationUnitElement dartUnitElement = | |
176 _htmlUnitElement.angularCompilationUnit; | |
177 _indexContributor.enterScope(dartUnitElement); | |
178 return super.visitHtmlUnit(node); | |
179 } | |
180 | |
181 @override | |
182 Object visitXmlAttributeNode(ht.XmlAttributeNode node) { | |
183 Element element = node.element; | |
184 if (element != null) { | |
185 ht.Token nameToken = node.nameToken; | |
186 Location location = _createLocationForToken(nameToken); | |
187 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, | |
188 location); | |
189 } | |
190 return super.visitXmlAttributeNode(node); | |
191 } | |
192 | |
193 @override | |
194 Object visitXmlTagNode(ht.XmlTagNode node) { | |
195 Element element = node.element; | |
196 if (element != null) { | |
197 // tag | |
198 { | |
199 ht.Token tagToken = node.tagToken; | |
200 Location location = _createLocationForToken(tagToken); | |
201 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, | |
202 location); | |
203 } | |
204 // maybe add closing tag range | |
205 ht.Token closingTag = node.closingTag; | |
206 if (closingTag != null) { | |
207 Location location = _createLocationForToken(closingTag); | |
208 _store.recordRelationship(element, | |
209 IndexConstants.ANGULAR_CLOSING_TAG_REFERENCE, location); | |
210 } | |
211 } | |
212 return super.visitXmlTagNode(node); | |
213 } | |
214 | |
215 Location _createLocationForIdentifier(SimpleIdentifier identifier) => | |
216 new Location(_htmlUnitElement, identifier.offset, identifier.length); | |
217 | |
218 Location _createLocationForToken(ht.Token token) => new Location( | |
219 _htmlUnitElement, token.offset, token.length); | |
220 } | |
221 | |
222 | |
223 class _AngularHtmlIndexContributor_forEmbeddedDart extends _IndexContributor { | |
224 final _AngularHtmlIndexContributor angularContributor; | |
225 | |
226 _AngularHtmlIndexContributor_forEmbeddedDart(IndexStore store, | |
227 this.angularContributor) : super(store); | |
228 | |
229 @override | |
230 Element peekElement() => angularContributor._htmlUnitElement; | |
231 | |
232 @override | |
233 void recordRelationship(Element element, Relationship relationship, | |
234 Location location) { | |
235 AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement( | |
236 element); | |
237 if (angularElement != null) { | |
238 element = angularElement; | |
239 relationship = IndexConstants.ANGULAR_REFERENCE; | |
240 } | |
241 super.recordRelationship(element, relationship, location); | |
242 } | |
243 } | |
244 | |
245 | |
246 /** | |
247 * Recursively visits [HtmlUnit] and every embedded [Expression]. | |
Brian Wilkerson
2014/07/03 18:58:13
"visits" --> "visits an"
scheglov
2014/07/03 19:06:16
Done.
| |
248 */ | |
249 abstract class _ExpressionVisitor extends ht.RecursiveXmlVisitor<Object> { | |
250 /** | |
251 * Visits the given [Expression]s embedded into tag or attribute. | |
252 * | |
253 * @param expression the [Expression] to visit, not `null` | |
254 */ | |
255 void visitExpression(Expression expression); | |
256 | |
257 @override | |
258 Object visitXmlAttributeNode(ht.XmlAttributeNode node) { | |
259 _visitExpressions(node.expressions); | |
260 return super.visitXmlAttributeNode(node); | |
261 } | |
262 | |
263 @override | |
264 Object visitXmlTagNode(ht.XmlTagNode node) { | |
265 _visitExpressions(node.expressions); | |
266 return super.visitXmlTagNode(node); | |
267 } | |
268 | |
269 /** | |
270 * Visits [Expression]s of the given [XmlExpression]s. | |
271 */ | |
272 void _visitExpressions(List<ht.XmlExpression> expressions) { | |
273 for (ht.XmlExpression xmlExpression in expressions) { | |
274 if (xmlExpression is AngularXmlExpression) { | |
275 AngularXmlExpression angularXmlExpression = xmlExpression; | |
276 List<Expression> dartExpressions = | |
277 angularXmlExpression.expression.expressions; | |
278 for (Expression dartExpression in dartExpressions) { | |
279 visitExpression(dartExpression); | |
280 } | |
281 } | |
282 if (xmlExpression is ht.RawXmlExpression) { | |
283 ht.RawXmlExpression rawXmlExpression = xmlExpression; | |
284 visitExpression(rawXmlExpression.expression); | |
285 } | |
286 } | |
287 } | |
288 } | |
289 | |
290 | |
291 /** | |
292 * Information about [ImportElement] and place where it is referenced using | |
293 * [PrefixElement]. | |
294 */ | |
295 class _ImportElementInfo { | |
296 ImportElement _element; | |
297 | |
298 int _periodEnd = 0; | |
299 } | |
300 | |
301 | |
302 /** | |
303 * Visits resolved AST and adds relationships into [IndexStore]. | |
304 */ | |
305 class _IndexContributor extends GeneralizingAstVisitor<Object> { | |
306 final IndexStore _store; | |
307 | |
308 LibraryElement _libraryElement; | |
309 | |
310 Map<ImportElement, Set<Element>> _importElementsMap = {}; | |
311 | |
312 /** | |
313 * A stack whose top element (the element with the largest index) is an elemen t representing the | |
314 * inner-most enclosing scope. | |
315 */ | |
316 Queue<Element> _elementStack = new Queue(); | |
317 | |
318 _IndexContributor(this._store); | |
319 | |
320 /** | |
321 * Enter a new scope represented by the given [Element]. | |
322 */ | |
323 void enterScope(Element element) { | |
324 _elementStack.addFirst(element); | |
325 } | |
326 | |
327 /** | |
328 * @return the inner-most enclosing [Element], may be `null`. | |
329 */ | |
330 Element peekElement() { | |
331 for (Element element in _elementStack) { | |
332 if (element != null) { | |
333 return element; | |
334 } | |
335 } | |
336 return null; | |
337 } | |
338 | |
339 /** | |
340 * Record the given relationship between the given [Element] and [Location]. | |
341 */ | |
342 void recordRelationship(Element element, Relationship relationship, | |
343 Location location) { | |
344 if (element != null && location != null) { | |
345 _store.recordRelationship(element, relationship, location); | |
346 } | |
347 } | |
348 | |
349 @override | |
350 Object visitAssignmentExpression(AssignmentExpression node) { | |
351 _recordOperatorReference(node.operator, node.bestElement); | |
352 return super.visitAssignmentExpression(node); | |
353 } | |
354 | |
355 @override | |
356 Object visitBinaryExpression(BinaryExpression node) { | |
357 _recordOperatorReference(node.operator, node.bestElement); | |
358 return super.visitBinaryExpression(node); | |
359 } | |
360 | |
361 @override | |
362 Object visitClassDeclaration(ClassDeclaration node) { | |
363 ClassElement element = node.element; | |
364 enterScope(element); | |
365 try { | |
366 _recordElementDefinition(element, IndexConstants.DEFINES_CLASS); | |
367 { | |
368 ExtendsClause extendsClause = node.extendsClause; | |
369 if (extendsClause != null) { | |
370 TypeName superclassNode = extendsClause.superclass; | |
371 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
372 } else { | |
373 InterfaceType superType = element.supertype; | |
374 if (superType != null) { | |
375 ClassElement objectElement = superType.element; | |
376 recordRelationship(objectElement, IndexConstants.IS_EXTENDED_BY, | |
377 _createLocationFromOffset(node.name.offset, 0)); | |
378 } | |
379 } | |
380 } | |
381 { | |
382 WithClause withClause = node.withClause; | |
383 if (withClause != null) { | |
384 for (TypeName mixinNode in withClause.mixinTypes) { | |
385 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
386 } | |
387 } | |
388 } | |
389 { | |
390 ImplementsClause implementsClause = node.implementsClause; | |
391 if (implementsClause != null) { | |
392 for (TypeName interfaceNode in implementsClause.interfaces) { | |
393 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
394 } | |
395 } | |
396 } | |
397 return super.visitClassDeclaration(node); | |
398 } finally { | |
399 _exitScope(); | |
400 } | |
401 } | |
402 | |
403 @override | |
404 Object visitClassTypeAlias(ClassTypeAlias node) { | |
405 ClassElement element = node.element; | |
406 enterScope(element); | |
407 try { | |
408 _recordElementDefinition(element, IndexConstants.DEFINES_CLASS_ALIAS); | |
409 { | |
410 TypeName superclassNode = node.superclass; | |
411 if (superclassNode != null) { | |
412 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
413 } | |
414 } | |
415 { | |
416 WithClause withClause = node.withClause; | |
417 if (withClause != null) { | |
418 for (TypeName mixinNode in withClause.mixinTypes) { | |
419 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
420 } | |
421 } | |
422 } | |
423 { | |
424 ImplementsClause implementsClause = node.implementsClause; | |
425 if (implementsClause != null) { | |
426 for (TypeName interfaceNode in implementsClause.interfaces) { | |
427 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
428 } | |
429 } | |
430 } | |
431 return super.visitClassTypeAlias(node); | |
432 } finally { | |
433 _exitScope(); | |
434 } | |
435 } | |
436 | |
437 @override | |
438 Object visitCompilationUnit(CompilationUnit node) { | |
439 CompilationUnitElement unitElement = node.element; | |
440 if (unitElement != null) { | |
441 _elementStack.add(unitElement); | |
442 _libraryElement = unitElement.enclosingElement; | |
443 if (_libraryElement != null) { | |
444 return super.visitCompilationUnit(node); | |
445 } | |
446 } | |
447 return null; | |
448 } | |
449 | |
450 @override | |
451 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
452 ConstructorElement element = node.element; | |
453 // define | |
454 { | |
455 Location location; | |
456 if (node.name != null) { | |
457 int start = node.period.offset; | |
458 int end = node.name.end; | |
459 location = _createLocationFromOffset(start, end - start); | |
460 } else { | |
461 int start = node.returnType.end; | |
462 location = _createLocationFromOffset(start, 0); | |
463 } | |
464 recordRelationship(element, IndexConstants.IS_DEFINED_BY, location); | |
465 } | |
466 // visit children | |
467 enterScope(element); | |
468 try { | |
469 return super.visitConstructorDeclaration(node); | |
470 } finally { | |
471 _exitScope(); | |
472 } | |
473 } | |
474 | |
475 @override | |
476 Object visitConstructorName(ConstructorName node) { | |
477 ConstructorElement element = node.staticElement; | |
478 // in 'class B = A;' actually A constructors are invoked | |
479 if (element != null && element.isSynthetic && element.redirectedConstructor | |
480 != null) { | |
481 element = element.redirectedConstructor; | |
482 } | |
483 // prepare location | |
484 Location location; | |
485 if (node.name != null) { | |
486 int start = node.period.offset; | |
487 int end = node.name.end; | |
488 location = _createLocationFromOffset(start, end - start); | |
489 } else { | |
490 int start = node.type.end; | |
491 location = _createLocationFromOffset(start, 0); | |
492 } | |
493 // record relationship | |
494 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
495 return super.visitConstructorName(node); | |
496 } | |
497 | |
498 @override | |
499 Object visitExportDirective(ExportDirective node) { | |
500 ExportElement element = node.element; | |
501 if (element != null) { | |
502 LibraryElement expLibrary = element.exportedLibrary; | |
503 _recordLibraryReference(node, expLibrary); | |
504 } | |
505 return super.visitExportDirective(node); | |
506 } | |
507 | |
508 @override | |
509 Object visitFormalParameter(FormalParameter node) { | |
510 ParameterElement element = node.element; | |
511 enterScope(element); | |
512 try { | |
513 return super.visitFormalParameter(node); | |
514 } finally { | |
515 _exitScope(); | |
516 } | |
517 } | |
518 | |
519 @override | |
520 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
521 Element element = node.element; | |
522 _recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION); | |
523 enterScope(element); | |
524 try { | |
525 return super.visitFunctionDeclaration(node); | |
526 } finally { | |
527 _exitScope(); | |
528 } | |
529 } | |
530 | |
531 @override | |
532 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
533 Element element = node.element; | |
534 _recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION_TYPE); | |
535 return super.visitFunctionTypeAlias(node); | |
536 } | |
537 | |
538 @override | |
539 Object visitImportDirective(ImportDirective node) { | |
540 ImportElement element = node.element; | |
541 if (element != null) { | |
542 LibraryElement impLibrary = element.importedLibrary; | |
543 _recordLibraryReference(node, impLibrary); | |
544 } | |
545 return super.visitImportDirective(node); | |
546 } | |
547 | |
548 @override | |
549 Object visitIndexExpression(IndexExpression node) { | |
550 MethodElement element = node.bestElement; | |
551 if (element is MethodElement) { | |
552 Token operator = node.leftBracket; | |
553 Location location = _createLocationFromToken(operator); | |
554 recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED, | |
555 location); | |
556 } | |
557 return super.visitIndexExpression(node); | |
558 } | |
559 | |
560 @override | |
561 Object visitMethodDeclaration(MethodDeclaration node) { | |
562 ExecutableElement element = node.element; | |
563 enterScope(element); | |
564 try { | |
565 return super.visitMethodDeclaration(node); | |
566 } finally { | |
567 _exitScope(); | |
568 } | |
569 } | |
570 | |
571 @override | |
572 Object visitMethodInvocation(MethodInvocation node) { | |
573 SimpleIdentifier name = node.methodName; | |
574 Element element = name.bestElement; | |
575 if (element is MethodElement || element is PropertyAccessorElement) { | |
576 Location location = _createLocationFromNode(name); | |
577 Relationship relationship; | |
578 if (node.target != null) { | |
579 relationship = IndexConstants.IS_INVOKED_BY_QUALIFIED; | |
580 } else { | |
581 relationship = IndexConstants.IS_INVOKED_BY_UNQUALIFIED; | |
582 } | |
583 recordRelationship(element, relationship, location); | |
584 } | |
585 if (element is FunctionElement || element is VariableElement) { | |
586 Location location = _createLocationFromNode(name); | |
587 recordRelationship(element, IndexConstants.IS_INVOKED_BY, location); | |
588 } | |
589 // name invocation | |
590 { | |
591 Element nameElement = new NameElement(name.name); | |
592 Location location = _createLocationFromNode(name); | |
593 Relationship kind = element != null ? | |
594 IndexConstants.NAME_IS_INVOKED_BY_RESOLVED : | |
595 IndexConstants.NAME_IS_INVOKED_BY_UNRESOLVED; | |
596 _store.recordRelationship(nameElement, kind, location); | |
597 } | |
598 _recordImportElementReferenceWithoutPrefix(name); | |
599 return super.visitMethodInvocation(node); | |
600 } | |
601 | |
602 @override | |
603 Object visitPartDirective(PartDirective node) { | |
604 Element element = node.element; | |
605 Location location = _createLocationFromNode(node.uri); | |
606 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
607 return super.visitPartDirective(node); | |
608 } | |
609 | |
610 @override | |
611 Object visitPartOfDirective(PartOfDirective node) { | |
612 Location location = _createLocationFromNode(node.libraryName); | |
613 recordRelationship(node.element, IndexConstants.IS_REFERENCED_BY, location); | |
614 return null; | |
615 } | |
616 | |
617 @override | |
618 Object visitPostfixExpression(PostfixExpression node) { | |
619 _recordOperatorReference(node.operator, node.bestElement); | |
620 return super.visitPostfixExpression(node); | |
621 } | |
622 | |
623 @override | |
624 Object visitPrefixExpression(PrefixExpression node) { | |
625 _recordOperatorReference(node.operator, node.bestElement); | |
626 return super.visitPrefixExpression(node); | |
627 } | |
628 | |
629 @override | |
630 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
631 Element nameElement = new NameElement(node.name); | |
632 Location location = _createLocationFromNode(node); | |
633 // name in declaration | |
634 if (node.inDeclarationContext()) { | |
635 recordRelationship(nameElement, IndexConstants.IS_DEFINED_BY, location); | |
636 return null; | |
637 } | |
638 // prepare information | |
639 Element element = node.bestElement; | |
640 // qualified name reference | |
641 _recordQualifiedMemberReference(node, element, nameElement, location); | |
642 // stop if already handled | |
643 if (_isAlreadyHandledName(node)) { | |
644 return null; | |
645 } | |
646 // record name read/write | |
647 { | |
648 bool inGetterContext = node.inGetterContext(); | |
649 bool inSetterContext = node.inSetterContext(); | |
650 if (inGetterContext && inSetterContext) { | |
651 Relationship kind = element != null ? | |
652 IndexConstants.NAME_IS_READ_WRITTEN_BY_RESOLVED : | |
653 IndexConstants.NAME_IS_READ_WRITTEN_BY_UNRESOLVED; | |
654 _store.recordRelationship(nameElement, kind, location); | |
655 } else if (inGetterContext) { | |
656 Relationship kind = element != null ? | |
657 IndexConstants.NAME_IS_READ_BY_RESOLVED : | |
658 IndexConstants.NAME_IS_READ_BY_UNRESOLVED; | |
659 _store.recordRelationship(nameElement, kind, location); | |
660 } else if (inSetterContext) { | |
661 Relationship kind = element != null ? | |
662 IndexConstants.NAME_IS_WRITTEN_BY_RESOLVED : | |
663 IndexConstants.NAME_IS_WRITTEN_BY_UNRESOLVED; | |
664 _store.recordRelationship(nameElement, kind, location); | |
665 } | |
666 } | |
667 // record specific relations | |
668 if (element is ClassElement || element is FunctionElement || element is | |
669 FunctionTypeAliasElement || element is LabelElement || element is | |
670 TypeParameterElement) { | |
671 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
672 } else if (element is FieldElement) { | |
673 location = _getLocationWithInitializerType(node, location); | |
674 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
675 } else if (element is FieldFormalParameterElement) { | |
676 FieldFormalParameterElement fieldParameter = element; | |
677 FieldElement field = fieldParameter.field; | |
678 recordRelationship(field, IndexConstants.IS_REFERENCED_BY_QUALIFIED, | |
679 location); | |
680 } else if (element is PrefixElement) { | |
681 _recordImportElementReferenceWithPrefix(node); | |
682 } else if (element is PropertyAccessorElement || element is MethodElement) { | |
683 location = _getLocationWithTypeAssignedToField(node, element, location); | |
684 if (node.isQualified) { | |
685 recordRelationship(element, IndexConstants.IS_REFERENCED_BY_QUALIFIED, | |
686 location); | |
687 } else { | |
688 recordRelationship(element, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED, | |
689 location); | |
690 } | |
691 } else if (element is ParameterElement || element is LocalVariableElement) { | |
692 bool inGetterContext = node.inGetterContext(); | |
693 bool inSetterContext = node.inSetterContext(); | |
694 if (inGetterContext && inSetterContext) { | |
695 recordRelationship(element, IndexConstants.IS_READ_WRITTEN_BY, | |
696 location); | |
697 } else if (inGetterContext) { | |
698 recordRelationship(element, IndexConstants.IS_READ_BY, location); | |
699 } else if (inSetterContext) { | |
700 recordRelationship(element, IndexConstants.IS_WRITTEN_BY, location); | |
701 } else { | |
702 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
703 } | |
704 } | |
705 _recordImportElementReferenceWithoutPrefix(node); | |
706 return super.visitSimpleIdentifier(node); | |
707 } | |
708 | |
709 @override | |
710 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
711 ConstructorElement element = node.staticElement; | |
712 Location location; | |
713 if (node.constructorName != null) { | |
714 int start = node.period.offset; | |
715 int end = node.constructorName.end; | |
716 location = _createLocationFromOffset(start, end - start); | |
717 } else { | |
718 int start = node.keyword.end; | |
719 location = _createLocationFromOffset(start, 0); | |
720 } | |
721 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
722 return super.visitSuperConstructorInvocation(node); | |
723 } | |
724 | |
725 @override | |
726 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
727 VariableDeclarationList variables = node.variables; | |
728 for (VariableDeclaration variableDeclaration in variables.variables) { | |
729 Element element = variableDeclaration.element; | |
730 _recordElementDefinition(element, IndexConstants.DEFINES_VARIABLE); | |
731 } | |
732 return super.visitTopLevelVariableDeclaration(node); | |
733 } | |
734 | |
735 @override | |
736 Object visitTypeParameter(TypeParameter node) { | |
737 TypeParameterElement element = node.element; | |
738 enterScope(element); | |
739 try { | |
740 return super.visitTypeParameter(node); | |
741 } finally { | |
742 _exitScope(); | |
743 } | |
744 } | |
745 | |
746 @override | |
747 Object visitVariableDeclaration(VariableDeclaration node) { | |
748 VariableElement element = node.element; | |
749 // record declaration | |
750 { | |
751 SimpleIdentifier name = node.name; | |
752 Location location = _createLocationFromNode(name); | |
753 location = _getLocationWithExpressionType(location, node.initializer); | |
754 recordRelationship(element, IndexConstants.IS_DEFINED_BY, location); | |
755 } | |
756 // visit | |
757 enterScope(element); | |
758 try { | |
759 return super.visitVariableDeclaration(node); | |
760 } finally { | |
761 _exitScope(); | |
762 } | |
763 } | |
764 | |
765 @override | |
766 Object visitVariableDeclarationList(VariableDeclarationList node) { | |
767 NodeList<VariableDeclaration> variables = node.variables; | |
768 if (variables != null) { | |
769 // use first VariableDeclaration as Element for Location(s) in type | |
770 { | |
771 TypeName type = node.type; | |
772 if (type != null) { | |
773 for (VariableDeclaration variableDeclaration in variables) { | |
774 enterScope(variableDeclaration.element); | |
775 try { | |
776 type.accept(this); | |
777 } finally { | |
778 _exitScope(); | |
779 } | |
780 // only one iteration | |
781 break; | |
782 } | |
783 } | |
784 } | |
785 // visit variables | |
786 variables.accept(this); | |
787 } | |
788 return null; | |
789 } | |
790 | |
791 /** | |
792 * @return the [Location] representing location of the [AstNode]. | |
793 */ | |
794 Location _createLocationFromNode(AstNode node) => _createLocationFromOffset( | |
795 node.offset, node.length); | |
796 | |
797 /** | |
798 * @param offset the offset of the location within [Source] | |
799 * @param length the length of the location | |
800 * @return the [Location] representing the given offset and length within the inner-most | |
801 * [Element]. | |
802 */ | |
803 Location _createLocationFromOffset(int offset, int length) { | |
804 Element element = peekElement(); | |
805 return new Location(element, offset, length); | |
806 } | |
807 | |
808 /** | |
809 * @return the [Location] representing location of the [Token]. | |
810 */ | |
811 Location _createLocationFromToken(Token token) => _createLocationFromOffset( | |
812 token.offset, token.length); | |
813 | |
814 /** | |
815 * Exit the current scope. | |
816 */ | |
817 void _exitScope() { | |
818 _elementStack.removeFirst(); | |
819 } | |
820 | |
821 /** | |
822 * @return `true` if given node already indexed as more interesting reference, so it should | |
823 * not be indexed again. | |
824 */ | |
825 bool _isAlreadyHandledName(SimpleIdentifier node) { | |
826 AstNode parent = node.parent; | |
827 if (parent is MethodInvocation) { | |
828 return identical(parent.methodName, node); | |
829 } | |
830 return false; | |
831 } | |
832 | |
833 /** | |
834 * Records the [Element] definition in the library and universe. | |
835 */ | |
836 void _recordElementDefinition(Element element, Relationship relationship) { | |
837 Location location = createLocation(element); | |
838 recordRelationship(_libraryElement, relationship, location); | |
839 recordRelationship(IndexConstants.UNIVERSE, relationship, location); | |
840 } | |
841 | |
842 /** | |
843 * Records [ImportElement] that declares given prefix and imports library with element used | |
844 * with given prefix node. | |
845 */ | |
846 void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) { | |
847 _ImportElementInfo info = getImportElementInfo(prefixNode); | |
848 if (info != null) { | |
849 int offset = prefixNode.offset; | |
850 int length = info._periodEnd - offset; | |
851 Location location = _createLocationFromOffset(offset, length); | |
852 recordRelationship(info._element, IndexConstants.IS_REFERENCED_BY, | |
853 location); | |
854 } | |
855 } | |
856 | |
857 /** | |
858 * Records [ImportElement] reference if given [SimpleIdentifier] references so me | |
859 * top-level element and not qualified with import prefix. | |
860 */ | |
861 void _recordImportElementReferenceWithoutPrefix(SimpleIdentifier node) { | |
862 if (_isIdentifierInImportCombinator(node)) { | |
863 return; | |
864 } | |
865 if (_isIdentifierInPrefixedIdentifier(node)) { | |
866 return; | |
867 } | |
868 Element element = node.staticElement; | |
869 ImportElement importElement = _internalGetImportElement(_libraryElement, | |
870 null, element, _importElementsMap); | |
871 if (importElement != null) { | |
872 Location location = _createLocationFromOffset(node.offset, 0); | |
873 recordRelationship(importElement, IndexConstants.IS_REFERENCED_BY, | |
874 location); | |
875 } | |
876 } | |
877 | |
878 /** | |
879 * Records reference to defining [CompilationUnitElement] of the given | |
880 * [LibraryElement]. | |
881 */ | |
882 void _recordLibraryReference(UriBasedDirective node, LibraryElement library) { | |
883 if (library != null) { | |
884 Location location = _createLocationFromNode(node.uri); | |
885 recordRelationship(library.definingCompilationUnit, | |
886 IndexConstants.IS_REFERENCED_BY, location); | |
887 } | |
888 } | |
889 | |
890 /** | |
891 * Record reference to the given operator [Element] and name. | |
892 */ | |
893 void _recordOperatorReference(Token operator, Element element) { | |
894 // prepare location | |
895 Location location = _createLocationFromToken(operator); | |
896 // record name reference | |
897 { | |
898 String name = operator.lexeme; | |
899 if (name == "++") { | |
900 name = "+"; | |
901 } | |
902 if (name == "--") { | |
903 name = "-"; | |
904 } | |
905 if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") { | |
906 name = name.substring(0, name.length - 1); | |
907 } | |
908 Element nameElement = new NameElement(name); | |
909 Relationship relationship = element != null ? | |
910 IndexConstants.IS_REFERENCED_BY_QUALIFIED_RESOLVED : | |
911 IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED; | |
912 recordRelationship(nameElement, relationship, location); | |
913 } | |
914 // record element reference | |
915 if (element != null) { | |
916 recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED, | |
917 location); | |
918 } | |
919 } | |
920 | |
921 /** | |
922 * Records reference if the given [SimpleIdentifier] looks like a qualified pr operty access | |
923 * or method invocation. | |
924 */ | |
925 void _recordQualifiedMemberReference(SimpleIdentifier node, Element element, | |
926 Element nameElement, Location location) { | |
927 if (node.isQualified) { | |
928 Relationship relationship = element != null ? | |
929 IndexConstants.IS_REFERENCED_BY_QUALIFIED_RESOLVED : | |
930 IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED; | |
931 recordRelationship(nameElement, relationship, location); | |
932 } | |
933 } | |
934 | |
935 /** | |
936 * Records extends/implements relationships between given [ClassElement] and [ Type] of | |
937 * "superNode". | |
938 */ | |
939 void _recordSuperType(TypeName superNode, Relationship relationship) { | |
940 if (superNode != null) { | |
941 Identifier superName = superNode.name; | |
942 if (superName != null) { | |
943 Element superElement = superName.staticElement; | |
944 recordRelationship(superElement, relationship, _createLocationFromNode( | |
945 superNode)); | |
946 } | |
947 } | |
948 } | |
949 | |
950 /** | |
951 * @return the [Location] representing location of the [Element]. | |
952 */ | |
953 static Location createLocation(Element element) { | |
954 if (element != null) { | |
955 int offset = element.nameOffset; | |
956 int length = element.displayName.length; | |
957 return new Location(element, offset, length); | |
958 } | |
959 return null; | |
960 } | |
961 | |
962 /** | |
963 * @return the [ImportElement] that is referenced by this node with [PrefixEle ment], | |
964 * may be `null`. | |
965 */ | |
966 static ImportElement getImportElement(SimpleIdentifier prefixNode) { | |
967 _ImportElementInfo info = getImportElementInfo(prefixNode); | |
968 return info != null ? info._element : null; | |
969 } | |
970 | |
971 /** | |
972 * @return the [ImportElementInfo] with [ImportElement] that is referenced by this | |
973 * node with [PrefixElement], may be `null`. | |
974 */ | |
975 static _ImportElementInfo getImportElementInfo(SimpleIdentifier prefixNode) { | |
976 _ImportElementInfo info = new _ImportElementInfo(); | |
977 // prepare environment | |
978 AstNode parent = prefixNode.parent; | |
979 CompilationUnit unit = prefixNode.getAncestor((node) => node is | |
980 CompilationUnit); | |
981 LibraryElement libraryElement = unit.element.library; | |
982 // prepare used element | |
983 Element usedElement = null; | |
984 if (parent is PrefixedIdentifier) { | |
985 PrefixedIdentifier prefixed = parent; | |
986 if (identical(prefixed.prefix, prefixNode)) { | |
987 usedElement = prefixed.staticElement; | |
988 info._periodEnd = prefixed.period.end; | |
989 } | |
990 } | |
991 if (parent is MethodInvocation) { | |
992 MethodInvocation invocation = parent; | |
993 if (identical(invocation.target, prefixNode)) { | |
994 usedElement = invocation.methodName.staticElement; | |
995 info._periodEnd = invocation.period.end; | |
996 } | |
997 } | |
998 // we need used Element | |
999 if (usedElement == null) { | |
1000 return null; | |
1001 } | |
1002 // find ImportElement | |
1003 String prefix = prefixNode.name; | |
1004 Map<ImportElement, Set<Element>> importElementsMap = {}; | |
1005 info._element = _internalGetImportElement(libraryElement, prefix, | |
1006 usedElement, importElementsMap); | |
1007 if (info._element == null) { | |
1008 return null; | |
1009 } | |
1010 return info; | |
1011 } | |
1012 | |
1013 /** | |
1014 * If the given expression has resolved type, returns the new location with th is type. | |
1015 * | |
1016 * @param location the base location | |
1017 * @param expression the expression assigned at the given location | |
1018 */ | |
1019 static Location _getLocationWithExpressionType(Location location, | |
1020 Expression expression) { | |
1021 if (expression != null) { | |
1022 return new LocationWithData<DartType>(location, expression.bestType); | |
1023 } | |
1024 return location; | |
1025 } | |
1026 | |
1027 /** | |
1028 * If the given node is the part of the [ConstructorFieldInitializer], returns location with | |
1029 * type of the initializer expression. | |
1030 */ | |
1031 static Location _getLocationWithInitializerType(SimpleIdentifier node, | |
1032 Location location) { | |
1033 if (node.parent is ConstructorFieldInitializer) { | |
1034 ConstructorFieldInitializer initializer = node.parent as | |
1035 ConstructorFieldInitializer; | |
1036 if (identical(initializer.fieldName, node)) { | |
1037 location = _getLocationWithExpressionType(location, | |
1038 initializer.expression); | |
1039 } | |
1040 } | |
1041 return location; | |
1042 } | |
1043 | |
1044 /** | |
1045 * If the given identifier has a synthetic [PropertyAccessorElement], i.e. acc essor for | |
1046 * normal field, and it is LHS of assignment, then include [Type] of the assig ned value into | |
1047 * the [Location]. | |
1048 * | |
1049 * @param identifier the identifier to record location | |
1050 * @param element the element of the identifier | |
1051 * @param location the raw location | |
1052 * @return the [Location] with the type of the assigned value | |
1053 */ | |
1054 static Location | |
1055 _getLocationWithTypeAssignedToField(SimpleIdentifier identifier, | |
1056 Element element, Location location) { | |
1057 // we need accessor | |
1058 if (element is! PropertyAccessorElement) { | |
1059 return location; | |
1060 } | |
1061 PropertyAccessorElement accessor = element as PropertyAccessorElement; | |
1062 // should be setter | |
1063 if (!accessor.isSetter) { | |
1064 return location; | |
1065 } | |
1066 // accessor should be synthetic, i.e. field normal | |
1067 if (!accessor.isSynthetic) { | |
1068 return location; | |
1069 } | |
1070 // should be LHS of assignment | |
1071 AstNode parent; | |
1072 { | |
1073 AstNode node = identifier; | |
1074 parent = node.parent; | |
1075 // new T().field = x; | |
1076 if (parent is PropertyAccess) { | |
1077 PropertyAccess propertyAccess = parent as PropertyAccess; | |
1078 if (identical(propertyAccess.propertyName, node)) { | |
1079 node = propertyAccess; | |
1080 parent = propertyAccess.parent; | |
1081 } | |
1082 } | |
1083 // obj.field = x; | |
1084 if (parent is PrefixedIdentifier) { | |
1085 PrefixedIdentifier prefixedIdentifier = parent as PrefixedIdentifier; | |
1086 if (identical(prefixedIdentifier.identifier, node)) { | |
1087 node = prefixedIdentifier; | |
1088 parent = prefixedIdentifier.parent; | |
1089 } | |
1090 } | |
1091 } | |
1092 // OK, remember the type | |
1093 if (parent is AssignmentExpression) { | |
1094 AssignmentExpression assignment = parent as AssignmentExpression; | |
1095 Expression rhs = assignment.rightHandSide; | |
1096 location = _getLocationWithExpressionType(location, rhs); | |
1097 } | |
1098 // done | |
1099 return location; | |
1100 } | |
1101 | |
1102 /** | |
1103 * @return the [ImportElement] that declares given [PrefixElement] and imports library | |
1104 * with given "usedElement". | |
1105 */ | |
1106 static ImportElement _internalGetImportElement(LibraryElement libraryElement, | |
1107 String prefix, Element usedElement, Map<ImportElement, | |
1108 Set<Element>> importElementsMap) { | |
1109 // validate Element | |
1110 if (usedElement == null) { | |
1111 return null; | |
1112 } | |
1113 if (usedElement.enclosingElement is! CompilationUnitElement) { | |
1114 return null; | |
1115 } | |
1116 LibraryElement usedLibrary = usedElement.library; | |
1117 // find ImportElement that imports used library with used prefix | |
1118 List<ImportElement> candidates = null; | |
1119 for (ImportElement importElement in libraryElement.imports) { | |
1120 // required library | |
1121 if (importElement.importedLibrary != usedLibrary) { | |
1122 continue; | |
1123 } | |
1124 // required prefix | |
1125 PrefixElement prefixElement = importElement.prefix; | |
1126 if (prefix == null) { | |
1127 if (prefixElement != null) { | |
1128 continue; | |
1129 } | |
1130 } else { | |
1131 if (prefixElement == null) { | |
1132 continue; | |
1133 } | |
1134 if (prefix != prefixElement.name) { | |
1135 continue; | |
1136 } | |
1137 } | |
1138 // no combinators => only possible candidate | |
1139 if (importElement.combinators.length == 0) { | |
1140 return importElement; | |
1141 } | |
1142 // OK, we have candidate | |
1143 if (candidates == null) { | |
1144 candidates = []; | |
1145 } | |
1146 candidates.add(importElement); | |
1147 } | |
1148 // no candidates, probably element is defined in this library | |
1149 if (candidates == null) { | |
1150 return null; | |
1151 } | |
1152 // one candidate | |
1153 if (candidates.length == 1) { | |
1154 return candidates[0]; | |
1155 } | |
1156 // ensure that each ImportElement has set of elements | |
1157 for (ImportElement importElement in candidates) { | |
1158 if (importElementsMap.containsKey(importElement)) { | |
1159 continue; | |
1160 } | |
1161 Namespace namespace = new NamespaceBuilder( | |
1162 ).createImportNamespaceForDirective(importElement); | |
1163 Set<Element> elements = new Set(); | |
1164 importElementsMap[importElement] = elements; | |
1165 } | |
1166 // use import namespace to choose correct one | |
1167 for (MapEntry<ImportElement, Set<Element>> entry in getMapEntrySet( | |
1168 importElementsMap)) { | |
1169 if (entry.getValue().contains(usedElement)) { | |
1170 return entry.getKey(); | |
1171 } | |
1172 } | |
1173 // not found | |
1174 return null; | |
1175 } | |
1176 | |
1177 /** | |
1178 * @return `true` if given "node" is part of an import [Combinator]. | |
1179 */ | |
1180 static bool _isIdentifierInImportCombinator(SimpleIdentifier node) { | |
1181 AstNode parent = node.parent; | |
1182 return parent is Combinator; | |
1183 } | |
1184 | |
1185 /** | |
1186 * @return `true` if given "node" is part of [PrefixedIdentifier] "prefix.node ". | |
1187 */ | |
1188 static bool _isIdentifierInPrefixedIdentifier(SimpleIdentifier node) { | |
1189 AstNode parent = node.parent; | |
1190 return parent is PrefixedIdentifier && identical(parent.identifier, node); | |
1191 } | |
1192 } | |
OLD | NEW |