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_server/src/provisional/index/index_core.dart'; | |
10 import 'package:analysis_server/src/services/correction/namespace.dart'; | |
11 import 'package:analysis_server/src/services/index/index.dart'; | |
12 import 'package:analysis_server/src/services/index/index_store.dart'; | |
13 import 'package:analysis_server/src/services/index/indexable_element.dart'; | |
14 import 'package:analysis_server/src/services/index/indexable_file.dart'; | |
15 import 'package:analyzer/dart/ast/ast.dart'; | |
16 import 'package:analyzer/dart/ast/token.dart'; | |
17 import 'package:analyzer/dart/ast/visitor.dart'; | |
18 import 'package:analyzer/dart/element/element.dart'; | |
19 import 'package:analyzer/dart/element/type.dart'; | |
20 import 'package:analyzer/src/generated/engine.dart'; | |
21 import 'package:analyzer/src/generated/java_engine.dart'; | |
22 import 'package:analyzer/src/generated/source.dart'; | |
23 | |
24 /** | |
25 * An [IndexContributor] that contributes relationships for Dart files. | |
26 */ | |
27 class DartIndexContributor extends IndexContributor { | |
28 @override | |
29 void contributeTo(IndexStore store, AnalysisContext context, Object object) { | |
30 if (store is InternalIndexStore && object is CompilationUnit) { | |
31 _IndexContributor contributor = new _IndexContributor(store); | |
32 object.accept(contributor); | |
33 } | |
34 } | |
35 } | |
36 | |
37 /** | |
38 * Visits a resolved AST and adds relationships into [InternalIndexStore]. | |
39 */ | |
40 class _IndexContributor extends GeneralizingAstVisitor { | |
41 final InternalIndexStore _store; | |
42 | |
43 CompilationUnitElement _unitElement; | |
44 LibraryElement _libraryElement; | |
45 | |
46 Map<ImportElement, Set<Element>> _importElementsMap = {}; | |
47 | |
48 /** | |
49 * A stack whose top element (the element with the largest index) is an | |
50 * element representing the inner-most enclosing scope. | |
51 */ | |
52 Queue<Element> _elementStack = new Queue(); | |
53 | |
54 _IndexContributor(this._store); | |
55 | |
56 /** | |
57 * Enter a new scope represented by the given [Element]. | |
58 */ | |
59 void enterScope(Element element) { | |
60 _elementStack.addFirst(element); | |
61 } | |
62 | |
63 /** | |
64 * Return the inner-most enclosing [Element], wrapped as an indexable object, | |
65 * or `null` if there is no enclosing element. | |
66 */ | |
67 IndexableElement peekElement() { | |
68 for (Element element in _elementStack) { | |
69 if (element != null) { | |
70 return new IndexableElement(element); | |
71 } | |
72 } | |
73 return null; | |
74 } | |
75 | |
76 /** | |
77 * Record the given relationship between the given [Element] and | |
78 * [LocationImpl]. | |
79 */ | |
80 void recordRelationshipElement( | |
81 Element element, RelationshipImpl relationship, LocationImpl location) { | |
82 if (element != null && location != null) { | |
83 _store.recordRelationship( | |
84 new IndexableElement(element), relationship, location); | |
85 } | |
86 } | |
87 | |
88 /** | |
89 * Record the given relationship between the given [IndexableObject] and | |
90 * [LocationImpl]. | |
91 */ | |
92 void recordRelationshipIndexable(IndexableObject indexable, | |
93 RelationshipImpl relationship, LocationImpl location) { | |
94 if (indexable != null && location != null) { | |
95 _store.recordRelationship(indexable, relationship, location); | |
96 } | |
97 } | |
98 | |
99 @override | |
100 visitAssignmentExpression(AssignmentExpression node) { | |
101 _recordOperatorReference(node.operator, node.bestElement); | |
102 super.visitAssignmentExpression(node); | |
103 } | |
104 | |
105 @override | |
106 visitBinaryExpression(BinaryExpression node) { | |
107 _recordOperatorReference(node.operator, node.bestElement); | |
108 super.visitBinaryExpression(node); | |
109 } | |
110 | |
111 @override | |
112 visitClassDeclaration(ClassDeclaration node) { | |
113 ClassElement element = node.element; | |
114 enterScope(element); | |
115 try { | |
116 _recordTopLevelElementDefinition(element); | |
117 _recordHasAncestor(element); | |
118 { | |
119 ExtendsClause extendsClause = node.extendsClause; | |
120 if (extendsClause != null) { | |
121 TypeName superclassNode = extendsClause.superclass; | |
122 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
123 } else { | |
124 InterfaceType superType = element.supertype; | |
125 if (superType != null) { | |
126 ClassElement objectElement = superType.element; | |
127 recordRelationshipElement( | |
128 objectElement, | |
129 IndexConstants.IS_EXTENDED_BY, | |
130 _createLocationForOffset(node.name.offset, 0)); | |
131 } | |
132 } | |
133 } | |
134 { | |
135 WithClause withClause = node.withClause; | |
136 if (withClause != null) { | |
137 for (TypeName mixinNode in withClause.mixinTypes) { | |
138 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
139 } | |
140 } | |
141 } | |
142 { | |
143 ImplementsClause implementsClause = node.implementsClause; | |
144 if (implementsClause != null) { | |
145 for (TypeName interfaceNode in implementsClause.interfaces) { | |
146 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
147 } | |
148 } | |
149 } | |
150 super.visitClassDeclaration(node); | |
151 } finally { | |
152 _exitScope(); | |
153 } | |
154 } | |
155 | |
156 @override | |
157 visitClassTypeAlias(ClassTypeAlias node) { | |
158 ClassElement element = node.element; | |
159 enterScope(element); | |
160 try { | |
161 _recordTopLevelElementDefinition(element); | |
162 _recordHasAncestor(element); | |
163 { | |
164 TypeName superclassNode = node.superclass; | |
165 if (superclassNode != null) { | |
166 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
167 } | |
168 } | |
169 { | |
170 WithClause withClause = node.withClause; | |
171 if (withClause != null) { | |
172 for (TypeName mixinNode in withClause.mixinTypes) { | |
173 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
174 } | |
175 } | |
176 } | |
177 { | |
178 ImplementsClause implementsClause = node.implementsClause; | |
179 if (implementsClause != null) { | |
180 for (TypeName interfaceNode in implementsClause.interfaces) { | |
181 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
182 } | |
183 } | |
184 } | |
185 super.visitClassTypeAlias(node); | |
186 } finally { | |
187 _exitScope(); | |
188 } | |
189 } | |
190 | |
191 @override | |
192 visitCompilationUnit(CompilationUnit node) { | |
193 _unitElement = node.element; | |
194 if (_unitElement != null) { | |
195 _elementStack.add(_unitElement); | |
196 _libraryElement = _unitElement.enclosingElement; | |
197 if (_libraryElement != null) { | |
198 super.visitCompilationUnit(node); | |
199 } | |
200 } | |
201 } | |
202 | |
203 @override | |
204 visitConstructorDeclaration(ConstructorDeclaration node) { | |
205 ConstructorElement element = node.element; | |
206 enterScope(element); | |
207 try { | |
208 super.visitConstructorDeclaration(node); | |
209 } finally { | |
210 _exitScope(); | |
211 } | |
212 } | |
213 | |
214 @override | |
215 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
216 SimpleIdentifier fieldName = node.fieldName; | |
217 Expression expression = node.expression; | |
218 // field reference is write here | |
219 if (fieldName != null) { | |
220 Element element = fieldName.staticElement; | |
221 LocationImpl location = _createLocationForNode(fieldName); | |
222 recordRelationshipElement( | |
223 element, IndexConstants.IS_WRITTEN_BY, location); | |
224 } | |
225 // index expression | |
226 if (expression != null) { | |
227 expression.accept(this); | |
228 } | |
229 } | |
230 | |
231 @override | |
232 visitConstructorName(ConstructorName node) { | |
233 ConstructorElement element = node.staticElement; | |
234 // in 'class B = A;' actually A constructors are invoked | |
235 if (element != null && | |
236 element.isSynthetic && | |
237 element.redirectedConstructor != null) { | |
238 element = element.redirectedConstructor; | |
239 } | |
240 // prepare location | |
241 LocationImpl location; | |
242 if (node.name != null) { | |
243 int start = node.period.offset; | |
244 int end = node.name.end; | |
245 location = _createLocationForOffset(start, end - start); | |
246 } else { | |
247 int start = node.type.end; | |
248 location = _createLocationForOffset(start, 0); | |
249 } | |
250 // record relationship | |
251 recordRelationshipElement( | |
252 element, IndexConstants.IS_REFERENCED_BY, location); | |
253 super.visitConstructorName(node); | |
254 } | |
255 | |
256 @override | |
257 visitDeclaredIdentifier(DeclaredIdentifier node) { | |
258 LocalVariableElement element = node.element; | |
259 enterScope(element); | |
260 try { | |
261 super.visitDeclaredIdentifier(node); | |
262 } finally { | |
263 _exitScope(); | |
264 } | |
265 } | |
266 | |
267 @override | |
268 visitEnumDeclaration(EnumDeclaration node) { | |
269 ClassElement element = node.element; | |
270 enterScope(element); | |
271 try { | |
272 _recordTopLevelElementDefinition(element); | |
273 super.visitEnumDeclaration(node); | |
274 } finally { | |
275 _exitScope(); | |
276 } | |
277 } | |
278 | |
279 @override | |
280 visitExportDirective(ExportDirective node) { | |
281 ExportElement element = node.element; | |
282 if (element != null) { | |
283 LibraryElement expLibrary = element.exportedLibrary; | |
284 _recordLibraryReference(node, expLibrary); | |
285 } | |
286 _recordUriFileReference(node); | |
287 super.visitExportDirective(node); | |
288 } | |
289 | |
290 @override | |
291 visitFormalParameter(FormalParameter node) { | |
292 ParameterElement element = node.element; | |
293 enterScope(element); | |
294 try { | |
295 super.visitFormalParameter(node); | |
296 } finally { | |
297 _exitScope(); | |
298 } | |
299 } | |
300 | |
301 @override | |
302 visitFunctionDeclaration(FunctionDeclaration node) { | |
303 Element element = node.element; | |
304 _recordTopLevelElementDefinition(element); | |
305 enterScope(element); | |
306 try { | |
307 super.visitFunctionDeclaration(node); | |
308 } finally { | |
309 _exitScope(); | |
310 } | |
311 } | |
312 | |
313 @override | |
314 visitFunctionTypeAlias(FunctionTypeAlias node) { | |
315 Element element = node.element; | |
316 _recordTopLevelElementDefinition(element); | |
317 super.visitFunctionTypeAlias(node); | |
318 } | |
319 | |
320 @override | |
321 visitImportDirective(ImportDirective node) { | |
322 ImportElement element = node.element; | |
323 if (element != null) { | |
324 LibraryElement impLibrary = element.importedLibrary; | |
325 _recordLibraryReference(node, impLibrary); | |
326 } | |
327 _recordUriFileReference(node); | |
328 super.visitImportDirective(node); | |
329 } | |
330 | |
331 @override | |
332 visitIndexExpression(IndexExpression node) { | |
333 MethodElement element = node.bestElement; | |
334 if (element is MethodElement) { | |
335 Token operator = node.leftBracket; | |
336 LocationImpl location = | |
337 _createLocationForToken(operator, element != null); | |
338 recordRelationshipElement( | |
339 element, IndexConstants.IS_INVOKED_BY, location); | |
340 } | |
341 super.visitIndexExpression(node); | |
342 } | |
343 | |
344 @override | |
345 visitMethodDeclaration(MethodDeclaration node) { | |
346 ExecutableElement element = node.element; | |
347 enterScope(element); | |
348 try { | |
349 super.visitMethodDeclaration(node); | |
350 } finally { | |
351 _exitScope(); | |
352 } | |
353 } | |
354 | |
355 @override | |
356 visitMethodInvocation(MethodInvocation node) { | |
357 SimpleIdentifier name = node.methodName; | |
358 LocationImpl location = _createLocationForNode(name); | |
359 // name invocation | |
360 recordRelationshipIndexable( | |
361 new IndexableName(name.name), IndexConstants.IS_INVOKED_BY, location); | |
362 // element invocation | |
363 Element element = name.bestElement; | |
364 if (element is MethodElement || | |
365 element is PropertyAccessorElement || | |
366 element is FunctionElement || | |
367 element is VariableElement) { | |
368 recordRelationshipElement( | |
369 element, IndexConstants.IS_INVOKED_BY, location); | |
370 } else if (element is ClassElement) { | |
371 recordRelationshipElement( | |
372 element, IndexConstants.IS_REFERENCED_BY, location); | |
373 } | |
374 _recordImportElementReferenceWithoutPrefix(name); | |
375 super.visitMethodInvocation(node); | |
376 } | |
377 | |
378 @override | |
379 visitPartDirective(PartDirective node) { | |
380 Element element = node.element; | |
381 LocationImpl location = _createLocationForNode(node.uri); | |
382 recordRelationshipElement( | |
383 element, IndexConstants.IS_REFERENCED_BY, location); | |
384 _recordUriFileReference(node); | |
385 super.visitPartDirective(node); | |
386 } | |
387 | |
388 @override | |
389 visitPartOfDirective(PartOfDirective node) { | |
390 LocationImpl location = _createLocationForNode(node.libraryName); | |
391 recordRelationshipElement( | |
392 node.element, IndexConstants.IS_REFERENCED_BY, location); | |
393 } | |
394 | |
395 @override | |
396 visitPostfixExpression(PostfixExpression node) { | |
397 _recordOperatorReference(node.operator, node.bestElement); | |
398 super.visitPostfixExpression(node); | |
399 } | |
400 | |
401 @override | |
402 visitPrefixExpression(PrefixExpression node) { | |
403 _recordOperatorReference(node.operator, node.bestElement); | |
404 super.visitPrefixExpression(node); | |
405 } | |
406 | |
407 @override | |
408 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { | |
409 ConstructorElement element = node.staticElement; | |
410 LocationImpl location; | |
411 if (node.constructorName != null) { | |
412 int start = node.period.offset; | |
413 int end = node.constructorName.end; | |
414 location = _createLocationForOffset(start, end - start); | |
415 } else { | |
416 int start = node.thisKeyword.end; | |
417 location = _createLocationForOffset(start, 0); | |
418 } | |
419 recordRelationshipElement( | |
420 element, IndexConstants.IS_REFERENCED_BY, location); | |
421 super.visitRedirectingConstructorInvocation(node); | |
422 } | |
423 | |
424 @override | |
425 visitSimpleIdentifier(SimpleIdentifier node) { | |
426 IndexableName indexableName = new IndexableName(node.name); | |
427 LocationImpl location = _createLocationForNode(node); | |
428 if (location == null) { | |
429 return; | |
430 } | |
431 // name in declaration | |
432 if (node.inDeclarationContext()) { | |
433 recordRelationshipIndexable( | |
434 indexableName, IndexConstants.NAME_IS_DEFINED_BY, location); | |
435 return; | |
436 } | |
437 // prepare information | |
438 Element element = node.bestElement; | |
439 // stop if already handled | |
440 if (_isAlreadyHandledName(node)) { | |
441 return; | |
442 } | |
443 // record name read/write | |
444 if (element != null && element.enclosingElement is ClassElement || | |
445 element == null && location.isQualified) { | |
446 bool inGetterContext = node.inGetterContext(); | |
447 bool inSetterContext = node.inSetterContext(); | |
448 if (inGetterContext && inSetterContext) { | |
449 recordRelationshipIndexable( | |
450 indexableName, IndexConstants.IS_READ_WRITTEN_BY, location); | |
451 } else if (inGetterContext) { | |
452 recordRelationshipIndexable( | |
453 indexableName, IndexConstants.IS_READ_BY, location); | |
454 } else if (inSetterContext) { | |
455 recordRelationshipIndexable( | |
456 indexableName, IndexConstants.IS_WRITTEN_BY, location); | |
457 } | |
458 } | |
459 // this.field parameter | |
460 if (element is FieldFormalParameterElement) { | |
461 RelationshipImpl relationship = peekElement().element == element | |
462 ? IndexConstants.IS_WRITTEN_BY | |
463 : IndexConstants.IS_REFERENCED_BY; | |
464 recordRelationshipElement(element.field, relationship, location); | |
465 return; | |
466 } | |
467 // record specific relations | |
468 if (element is ClassElement || | |
469 element is FunctionElement || | |
470 element is FunctionTypeAliasElement || | |
471 element is LabelElement || | |
472 element is MethodElement || | |
473 element is PropertyAccessorElement || | |
474 element is PropertyInducingElement || | |
475 element is TypeParameterElement) { | |
476 recordRelationshipElement( | |
477 element, IndexConstants.IS_REFERENCED_BY, location); | |
478 } else if (element is PrefixElement) { | |
479 recordRelationshipElement( | |
480 element, IndexConstants.IS_REFERENCED_BY, location); | |
481 _recordImportElementReferenceWithPrefix(node); | |
482 } else if (element is ParameterElement || element is LocalVariableElement) { | |
483 bool inGetterContext = node.inGetterContext(); | |
484 bool inSetterContext = node.inSetterContext(); | |
485 if (inGetterContext && inSetterContext) { | |
486 recordRelationshipElement( | |
487 element, IndexConstants.IS_READ_WRITTEN_BY, location); | |
488 } else if (inGetterContext) { | |
489 recordRelationshipElement(element, IndexConstants.IS_READ_BY, location); | |
490 } else if (inSetterContext) { | |
491 recordRelationshipElement( | |
492 element, IndexConstants.IS_WRITTEN_BY, location); | |
493 } else { | |
494 recordRelationshipElement( | |
495 element, IndexConstants.IS_REFERENCED_BY, location); | |
496 } | |
497 } | |
498 _recordImportElementReferenceWithoutPrefix(node); | |
499 super.visitSimpleIdentifier(node); | |
500 } | |
501 | |
502 @override | |
503 visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
504 ConstructorElement element = node.staticElement; | |
505 LocationImpl location; | |
506 if (node.constructorName != null) { | |
507 int start = node.period.offset; | |
508 int end = node.constructorName.end; | |
509 location = _createLocationForOffset(start, end - start); | |
510 } else { | |
511 int start = node.superKeyword.end; | |
512 location = _createLocationForOffset(start, 0); | |
513 } | |
514 recordRelationshipElement( | |
515 element, IndexConstants.IS_REFERENCED_BY, location); | |
516 super.visitSuperConstructorInvocation(node); | |
517 } | |
518 | |
519 @override | |
520 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
521 VariableDeclarationList variables = node.variables; | |
522 for (VariableDeclaration variableDeclaration in variables.variables) { | |
523 Element element = variableDeclaration.element; | |
524 _recordTopLevelElementDefinition(element); | |
525 } | |
526 super.visitTopLevelVariableDeclaration(node); | |
527 } | |
528 | |
529 @override | |
530 visitTypeParameter(TypeParameter node) { | |
531 TypeParameterElement element = node.element; | |
532 enterScope(element); | |
533 try { | |
534 super.visitTypeParameter(node); | |
535 } finally { | |
536 _exitScope(); | |
537 } | |
538 } | |
539 | |
540 @override | |
541 visitVariableDeclaration(VariableDeclaration node) { | |
542 VariableElement element = node.element; | |
543 // record declaration | |
544 { | |
545 SimpleIdentifier name = node.name; | |
546 LocationImpl location = _createLocationForNode(name); | |
547 location = _getLocationWithExpressionType(location, node.initializer); | |
548 recordRelationshipElement( | |
549 element, IndexConstants.NAME_IS_DEFINED_BY, location); | |
550 } | |
551 // visit | |
552 enterScope(element); | |
553 try { | |
554 super.visitVariableDeclaration(node); | |
555 } finally { | |
556 _exitScope(); | |
557 } | |
558 } | |
559 | |
560 @override | |
561 visitVariableDeclarationList(VariableDeclarationList node) { | |
562 NodeList<VariableDeclaration> variables = node.variables; | |
563 if (variables != null) { | |
564 // use first VariableDeclaration as Element for Location(s) in type | |
565 { | |
566 TypeName type = node.type; | |
567 if (type != null) { | |
568 for (VariableDeclaration variableDeclaration in variables) { | |
569 enterScope(variableDeclaration.element); | |
570 try { | |
571 type.accept(this); | |
572 } finally { | |
573 _exitScope(); | |
574 } | |
575 // only one iteration | |
576 break; | |
577 } | |
578 } | |
579 } | |
580 // visit variables | |
581 variables.accept(this); | |
582 } | |
583 } | |
584 | |
585 /** | |
586 * @return the [LocationImpl] representing location of the [AstNode]. | |
587 */ | |
588 LocationImpl _createLocationForNode(AstNode node) { | |
589 bool isQualified = _isQualifiedClassMemberAccess(node); | |
590 bool isResolved = true; | |
591 if (node is SimpleIdentifier) { | |
592 isResolved = node.bestElement != null; | |
593 } | |
594 IndexableObject indexable = peekElement(); | |
595 return new LocationImpl(indexable, node.offset, node.length, | |
596 isQualified: isQualified, isResolved: isResolved); | |
597 } | |
598 | |
599 /** | |
600 * [offset] - the offset of the location within [Source]. | |
601 * [length] - the length of the location. | |
602 * | |
603 * Returns the [LocationImpl] representing the given offset and length within
the | |
604 * inner-most [Element]. | |
605 */ | |
606 LocationImpl _createLocationForOffset(int offset, int length) { | |
607 IndexableObject element = peekElement(); | |
608 return new LocationImpl(element, offset, length); | |
609 } | |
610 | |
611 /** | |
612 * @return the [LocationImpl] representing location of the [Token]. | |
613 */ | |
614 LocationImpl _createLocationForToken(Token token, bool isResolved) { | |
615 IndexableObject element = peekElement(); | |
616 return new LocationImpl(element, token.offset, token.length, | |
617 isQualified: true, isResolved: isResolved); | |
618 } | |
619 | |
620 /** | |
621 * Exit the current scope. | |
622 */ | |
623 void _exitScope() { | |
624 _elementStack.removeFirst(); | |
625 } | |
626 | |
627 /** | |
628 * @return `true` if given node already indexed as more interesting reference,
so it should | |
629 * not be indexed again. | |
630 */ | |
631 bool _isAlreadyHandledName(SimpleIdentifier node) { | |
632 AstNode parent = node.parent; | |
633 if (parent is MethodInvocation) { | |
634 return parent.methodName == node; | |
635 } | |
636 return false; | |
637 } | |
638 | |
639 bool _isQualifiedClassMemberAccess(AstNode node) { | |
640 if (node is SimpleIdentifier) { | |
641 AstNode parent = node.parent; | |
642 if (parent is PrefixedIdentifier && parent.identifier == node) { | |
643 return parent.prefix.staticElement is! PrefixElement; | |
644 } | |
645 if (parent is PropertyAccess && parent.propertyName == node) { | |
646 return parent.realTarget != null; | |
647 } | |
648 if (parent is MethodInvocation && parent.methodName == node) { | |
649 Expression target = parent.realTarget; | |
650 if (target is SimpleIdentifier && | |
651 target.staticElement is PrefixElement) { | |
652 return false; | |
653 } | |
654 return target != null; | |
655 } | |
656 } | |
657 return false; | |
658 } | |
659 | |
660 void _recordHasAncestor(ClassElement element) { | |
661 int offset = element.nameOffset; | |
662 int length = element.nameLength; | |
663 LocationImpl location = _createLocationForOffset(offset, length); | |
664 _recordHasAncestor0(location, element, false, <ClassElement>[]); | |
665 } | |
666 | |
667 void _recordHasAncestor0(LocationImpl location, ClassElement element, | |
668 bool includeThis, List<ClassElement> visitedElements) { | |
669 if (element == null) { | |
670 return; | |
671 } | |
672 if (visitedElements.contains(element)) { | |
673 return; | |
674 } | |
675 visitedElements.add(element); | |
676 if (includeThis) { | |
677 recordRelationshipElement(element, IndexConstants.HAS_ANCESTOR, location); | |
678 } | |
679 { | |
680 InterfaceType superType = element.supertype; | |
681 if (superType != null) { | |
682 _recordHasAncestor0(location, superType.element, true, visitedElements); | |
683 } | |
684 } | |
685 for (InterfaceType mixinType in element.mixins) { | |
686 _recordHasAncestor0(location, mixinType.element, true, visitedElements); | |
687 } | |
688 for (InterfaceType implementedType in element.interfaces) { | |
689 _recordHasAncestor0( | |
690 location, implementedType.element, true, visitedElements); | |
691 } | |
692 } | |
693 | |
694 /** | |
695 * Records [ImportElement] reference if given [SimpleIdentifier] references so
me | |
696 * top-level element and not qualified with import prefix. | |
697 */ | |
698 void _recordImportElementReferenceWithoutPrefix(SimpleIdentifier node) { | |
699 if (_isIdentifierInImportCombinator(node)) { | |
700 return; | |
701 } | |
702 if (_isIdentifierInPrefixedIdentifier(node)) { | |
703 return; | |
704 } | |
705 Element element = node.staticElement; | |
706 ImportElement importElement = internal_getImportElement( | |
707 _libraryElement, null, element, _importElementsMap); | |
708 if (importElement != null) { | |
709 LocationImpl location = _createLocationForOffset(node.offset, 0); | |
710 recordRelationshipElement( | |
711 importElement, IndexConstants.IS_REFERENCED_BY, location); | |
712 } | |
713 } | |
714 | |
715 /** | |
716 * Records [ImportElement] that declares given prefix and imports library with
element used | |
717 * with given prefix node. | |
718 */ | |
719 void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) { | |
720 ImportElementInfo info = internal_getImportElementInfo(prefixNode); | |
721 if (info != null) { | |
722 int offset = prefixNode.offset; | |
723 int length = info.periodEnd - offset; | |
724 LocationImpl location = _createLocationForOffset(offset, length); | |
725 recordRelationshipElement( | |
726 info.element, IndexConstants.IS_REFERENCED_BY, location); | |
727 } | |
728 } | |
729 | |
730 /** | |
731 * Records reference to defining [CompilationUnitElement] of the given | |
732 * [LibraryElement]. | |
733 */ | |
734 void _recordLibraryReference(UriBasedDirective node, LibraryElement library) { | |
735 if (library != null) { | |
736 LocationImpl location = _createLocationForNode(node.uri); | |
737 recordRelationshipElement(library.definingCompilationUnit, | |
738 IndexConstants.IS_REFERENCED_BY, location); | |
739 } | |
740 } | |
741 | |
742 /** | |
743 * Record reference to the given operator [Element] and name. | |
744 */ | |
745 void _recordOperatorReference(Token operator, Element element) { | |
746 // prepare location | |
747 LocationImpl location = _createLocationForToken(operator, element != null); | |
748 // record name reference | |
749 { | |
750 String name = operator.lexeme; | |
751 if (name == "++") { | |
752 name = "+"; | |
753 } | |
754 if (name == "--") { | |
755 name = "-"; | |
756 } | |
757 if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") { | |
758 name = name.substring(0, name.length - 1); | |
759 } | |
760 IndexableName indexableName = new IndexableName(name); | |
761 recordRelationshipIndexable( | |
762 indexableName, IndexConstants.IS_INVOKED_BY, location); | |
763 } | |
764 // record element reference | |
765 if (element != null) { | |
766 recordRelationshipElement( | |
767 element, IndexConstants.IS_INVOKED_BY, location); | |
768 } | |
769 } | |
770 | |
771 /** | |
772 * Records a relation between [superNode] and its [Element]. | |
773 */ | |
774 void _recordSuperType(TypeName superNode, RelationshipImpl relationship) { | |
775 if (superNode != null) { | |
776 Identifier superName = superNode.name; | |
777 if (superName != null) { | |
778 Element superElement = superName.staticElement; | |
779 recordRelationshipElement( | |
780 superElement, relationship, _createLocationForNode(superNode)); | |
781 } | |
782 } | |
783 } | |
784 | |
785 /** | |
786 * Records the [Element] definition in the library and universe. | |
787 */ | |
788 void _recordTopLevelElementDefinition(Element element) { | |
789 if (element != null) { | |
790 IndexableElement indexable = new IndexableElement(element); | |
791 int offset = element.nameOffset; | |
792 int length = element.nameLength; | |
793 LocationImpl location = new LocationImpl(indexable, offset, length); | |
794 recordRelationshipElement( | |
795 _libraryElement, IndexConstants.DEFINES, location); | |
796 _store.recordTopLevelDeclaration(element); | |
797 } | |
798 } | |
799 | |
800 void _recordUriFileReference(UriBasedDirective directive) { | |
801 Source source = directive.source; | |
802 if (source != null) { | |
803 LocationImpl location = new LocationImpl( | |
804 new IndexableFile(_unitElement.source.fullName), | |
805 directive.uri.offset, | |
806 directive.uri.length); | |
807 _store.recordRelationship(new IndexableFile(source.fullName), | |
808 IndexConstants.IS_REFERENCED_BY, location); | |
809 } | |
810 } | |
811 | |
812 /** | |
813 * If the given expression has resolved type, returns the new location with th
is type. | |
814 * | |
815 * [location] - the base location | |
816 * [expression] - the expression assigned at the given location | |
817 */ | |
818 static LocationImpl _getLocationWithExpressionType( | |
819 LocationImpl location, Expression expression) { | |
820 if (expression != null) { | |
821 return new LocationWithData<DartType>(location, expression.bestType); | |
822 } | |
823 return location; | |
824 } | |
825 | |
826 /** | |
827 * @return `true` if given "node" is part of an import [Combinator]. | |
828 */ | |
829 static bool _isIdentifierInImportCombinator(SimpleIdentifier node) { | |
830 AstNode parent = node.parent; | |
831 return parent is Combinator; | |
832 } | |
833 | |
834 /** | |
835 * @return `true` if given "node" is part of [PrefixedIdentifier] "prefix.node
". | |
836 */ | |
837 static bool _isIdentifierInPrefixedIdentifier(SimpleIdentifier node) { | |
838 AstNode parent = node.parent; | |
839 return parent is PrefixedIdentifier && parent.identifier == node; | |
840 } | |
841 } | |
OLD | NEW |