| 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 |