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