| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library services.src.index.index_contributor; |
| |
| import 'dart:collection' show Queue; |
| |
| import 'package:analysis_server/src/services/correction/namespace.dart'; |
| import 'package:analysis_server/src/services/index/index.dart'; |
| import 'package:analysis_server/src/services/index/index_store.dart'; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/html.dart' as ht; |
| import 'package:analyzer/src/generated/java_engine.dart'; |
| import 'package:analyzer/src/generated/scanner.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| |
| |
| /** |
| * Adds data to [store] based on the resolved Dart [unit]. |
| */ |
| void indexDartUnit(IndexStore store, AnalysisContext context, |
| CompilationUnit unit) { |
| // check unit |
| if (unit == null) { |
| return; |
| } |
| // prepare unit element |
| CompilationUnitElement unitElement = unit.element; |
| if (unitElement == null) { |
| return; |
| } |
| // about to index |
| bool mayIndex = store.aboutToIndexDart(context, unitElement); |
| if (!mayIndex) { |
| return; |
| } |
| // do index |
| unit.accept(new _IndexContributor(store)); |
| unit.accept(new _AngularDartIndexContributor(store)); |
| store.doneIndex(); |
| } |
| |
| |
| /** |
| * Adds data to [store] based on the resolved HTML [unit]. |
| */ |
| void indexHtmlUnit(IndexStore store, AnalysisContext context, ht.HtmlUnit unit) |
| { |
| // check unit |
| if (unit == null) { |
| return; |
| } |
| // prepare unit element |
| HtmlElement unitElement = unit.element; |
| if (unitElement == null) { |
| return; |
| } |
| // about to index |
| bool mayIndex = store.aboutToIndexHtml(context, unitElement); |
| if (!mayIndex) { |
| return; |
| } |
| // do index |
| unit.accept(new _AngularHtmlIndexContributor(store)); |
| store.doneIndex(); |
| } |
| |
| |
| /** |
| * Visits resolved [CompilationUnit] and adds Angular specific relationships |
| * into [IndexStore]. |
| */ |
| class _AngularDartIndexContributor extends GeneralizingAstVisitor<Object> { |
| final IndexStore _store; |
| |
| _AngularDartIndexContributor(this._store); |
| |
| @override |
| Object visitClassDeclaration(ClassDeclaration node) { |
| ClassElement classElement = node.element; |
| if (classElement != null) { |
| List<ToolkitObjectElement> toolkitObjects = classElement.toolkitObjects; |
| for (ToolkitObjectElement object in toolkitObjects) { |
| if (object is AngularComponentElement) { |
| _indexComponent(object); |
| } |
| if (object is AngularDecoratorElement) { |
| AngularDecoratorElement directive = object; |
| _indexDirective(directive); |
| } |
| } |
| } |
| // stop visiting |
| return null; |
| } |
| |
| @override |
| Object visitCompilationUnitMember(CompilationUnitMember node) => null; |
| |
| void _indexComponent(AngularComponentElement component) { |
| _indexProperties(component.properties); |
| } |
| |
| void _indexDirective(AngularDecoratorElement directive) { |
| _indexProperties(directive.properties); |
| } |
| |
| /** |
| * Index [FieldElement] references from [AngularPropertyElement]s. |
| */ |
| void _indexProperties(List<AngularPropertyElement> properties) { |
| for (AngularPropertyElement property in properties) { |
| FieldElement field = property.field; |
| if (field != null) { |
| int offset = property.fieldNameOffset; |
| if (offset == -1) { |
| continue; |
| } |
| int length = field.name.length; |
| Location location = new Location(property, offset, length); |
| // getter reference |
| if (property.propertyKind.callsGetter()) { |
| PropertyAccessorElement getter = field.getter; |
| if (getter != null) { |
| _store.recordRelationship( |
| getter, |
| IndexConstants.IS_REFERENCED_BY, |
| location); |
| } |
| } |
| // setter reference |
| if (property.propertyKind.callsSetter()) { |
| PropertyAccessorElement setter = field.setter; |
| if (setter != null) { |
| _store.recordRelationship( |
| setter, |
| IndexConstants.IS_REFERENCED_BY, |
| location); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Visits resolved [HtmlUnit] and adds relationships into [IndexStore]. |
| */ |
| class _AngularHtmlIndexContributor extends _ExpressionVisitor { |
| /** |
| * The [IndexStore] to record relations into. |
| */ |
| final IndexStore _store; |
| |
| /** |
| * The index contributor used to index Dart [Expression]s. |
| */ |
| _IndexContributor _indexContributor; |
| |
| HtmlElement _htmlUnitElement; |
| |
| /** |
| * Initialize a newly created Angular HTML index contributor. |
| * |
| * [store] - the [IndexStore] to record relations into. |
| */ |
| _AngularHtmlIndexContributor(this._store) { |
| _indexContributor = new _AngularHtmlIndexContributor_forEmbeddedDart( |
| _store, |
| this); |
| } |
| |
| @override |
| void visitExpression(Expression expression) { |
| // Formatter |
| if (expression is SimpleIdentifier) { |
| Element element = expression.bestElement; |
| if (element is AngularElement) { |
| _store.recordRelationship( |
| element, |
| IndexConstants.ANGULAR_REFERENCE, |
| _createLocationForIdentifier(expression)); |
| return; |
| } |
| } |
| // index as a normal Dart expression |
| expression.accept(_indexContributor); |
| } |
| |
| @override |
| Object visitHtmlUnit(ht.HtmlUnit node) { |
| _htmlUnitElement = node.element; |
| CompilationUnitElement dartUnitElement = |
| _htmlUnitElement.angularCompilationUnit; |
| _indexContributor.enterScope(dartUnitElement); |
| return super.visitHtmlUnit(node); |
| } |
| |
| @override |
| Object visitXmlAttributeNode(ht.XmlAttributeNode node) { |
| Element element = node.element; |
| if (element != null) { |
| ht.Token nameToken = node.nameToken; |
| Location location = _createLocationForToken(nameToken); |
| _store.recordRelationship( |
| element, |
| IndexConstants.ANGULAR_REFERENCE, |
| location); |
| } |
| return super.visitXmlAttributeNode(node); |
| } |
| |
| @override |
| Object visitXmlTagNode(ht.XmlTagNode node) { |
| Element element = node.element; |
| if (element != null) { |
| // tag |
| { |
| ht.Token tagToken = node.tagToken; |
| Location location = _createLocationForToken(tagToken); |
| _store.recordRelationship( |
| element, |
| IndexConstants.ANGULAR_REFERENCE, |
| location); |
| } |
| // maybe add closing tag range |
| ht.Token closingTag = node.closingTag; |
| if (closingTag != null) { |
| Location location = _createLocationForToken(closingTag); |
| _store.recordRelationship( |
| element, |
| IndexConstants.ANGULAR_CLOSING_TAG_REFERENCE, |
| location); |
| } |
| } |
| return super.visitXmlTagNode(node); |
| } |
| |
| Location _createLocationForIdentifier(SimpleIdentifier identifier) => |
| new Location(_htmlUnitElement, identifier.offset, identifier.length); |
| |
| Location _createLocationForToken(ht.Token token) => |
| new Location(_htmlUnitElement, token.offset, token.length); |
| } |
| |
| |
| class _AngularHtmlIndexContributor_forEmbeddedDart extends _IndexContributor { |
| final _AngularHtmlIndexContributor angularContributor; |
| |
| _AngularHtmlIndexContributor_forEmbeddedDart(IndexStore store, |
| this.angularContributor) : super( |
| store); |
| |
| @override |
| Element peekElement() => angularContributor._htmlUnitElement; |
| |
| @override |
| void recordRelationship(Element element, Relationship relationship, |
| Location location) { |
| AngularElement angularElement = |
| AngularHtmlUnitResolver.getAngularElement(element); |
| if (angularElement != null) { |
| element = angularElement; |
| relationship = IndexConstants.ANGULAR_REFERENCE; |
| } |
| super.recordRelationship(element, relationship, location); |
| } |
| } |
| |
| |
| /** |
| * Recursively visits an [HtmlUnit] and every embedded [Expression]. |
| */ |
| abstract class _ExpressionVisitor extends ht.RecursiveXmlVisitor<Object> { |
| /** |
| * Visits the given [Expression]s embedded into tag or attribute. |
| * |
| * [expression] - the [Expression] to visit, not `null` |
| */ |
| void visitExpression(Expression expression); |
| |
| @override |
| Object visitXmlAttributeNode(ht.XmlAttributeNode node) { |
| _visitExpressions(node.expressions); |
| return super.visitXmlAttributeNode(node); |
| } |
| |
| @override |
| Object visitXmlTagNode(ht.XmlTagNode node) { |
| _visitExpressions(node.expressions); |
| return super.visitXmlTagNode(node); |
| } |
| |
| /** |
| * Visits [Expression]s of the given [XmlExpression]s. |
| */ |
| void _visitExpressions(List<ht.XmlExpression> expressions) { |
| for (ht.XmlExpression xmlExpression in expressions) { |
| if (xmlExpression is AngularXmlExpression) { |
| AngularXmlExpression angularXmlExpression = xmlExpression; |
| List<Expression> dartExpressions = |
| angularXmlExpression.expression.expressions; |
| for (Expression dartExpression in dartExpressions) { |
| visitExpression(dartExpression); |
| } |
| } |
| if (xmlExpression is ht.RawXmlExpression) { |
| ht.RawXmlExpression rawXmlExpression = xmlExpression; |
| visitExpression(rawXmlExpression.expression); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Visits a resolved AST and adds relationships into [IndexStore]. |
| */ |
| class _IndexContributor extends GeneralizingAstVisitor<Object> { |
| final IndexStore _store; |
| |
| LibraryElement _libraryElement; |
| |
| Map<ImportElement, Set<Element>> _importElementsMap = {}; |
| |
| /** |
| * A stack whose top element (the element with the largest index) is an element representing the |
| * inner-most enclosing scope. |
| */ |
| Queue<Element> _elementStack = new Queue(); |
| |
| _IndexContributor(this._store); |
| |
| /** |
| * Enter a new scope represented by the given [Element]. |
| */ |
| void enterScope(Element element) { |
| _elementStack.addFirst(element); |
| } |
| |
| /** |
| * @return the inner-most enclosing [Element], may be `null`. |
| */ |
| Element peekElement() { |
| for (Element element in _elementStack) { |
| if (element != null) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Record the given relationship between the given [Element] and [Location]. |
| */ |
| void recordRelationship(Element element, Relationship relationship, |
| Location location) { |
| if (element != null && location != null) { |
| _store.recordRelationship(element, relationship, location); |
| } |
| } |
| |
| @override |
| Object visitAssignmentExpression(AssignmentExpression node) { |
| _recordOperatorReference(node.operator, node.bestElement); |
| return super.visitAssignmentExpression(node); |
| } |
| |
| @override |
| Object visitBinaryExpression(BinaryExpression node) { |
| _recordOperatorReference(node.operator, node.bestElement); |
| return super.visitBinaryExpression(node); |
| } |
| |
| @override |
| Object visitClassDeclaration(ClassDeclaration node) { |
| ClassElement element = node.element; |
| enterScope(element); |
| try { |
| _recordElementDefinition(element); |
| { |
| ExtendsClause extendsClause = node.extendsClause; |
| if (extendsClause != null) { |
| TypeName superclassNode = extendsClause.superclass; |
| _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); |
| } else { |
| InterfaceType superType = element.supertype; |
| if (superType != null) { |
| ClassElement objectElement = superType.element; |
| recordRelationship( |
| objectElement, |
| IndexConstants.IS_EXTENDED_BY, |
| _createLocationForOffset(node.name.offset, 0)); |
| } |
| } |
| } |
| { |
| WithClause withClause = node.withClause; |
| if (withClause != null) { |
| for (TypeName mixinNode in withClause.mixinTypes) { |
| _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); |
| } |
| } |
| } |
| { |
| ImplementsClause implementsClause = node.implementsClause; |
| if (implementsClause != null) { |
| for (TypeName interfaceNode in implementsClause.interfaces) { |
| _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); |
| } |
| } |
| } |
| return super.visitClassDeclaration(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitClassTypeAlias(ClassTypeAlias node) { |
| ClassElement element = node.element; |
| enterScope(element); |
| try { |
| _recordElementDefinition(element); |
| { |
| TypeName superclassNode = node.superclass; |
| if (superclassNode != null) { |
| _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); |
| } |
| } |
| { |
| WithClause withClause = node.withClause; |
| if (withClause != null) { |
| for (TypeName mixinNode in withClause.mixinTypes) { |
| _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); |
| } |
| } |
| } |
| { |
| ImplementsClause implementsClause = node.implementsClause; |
| if (implementsClause != null) { |
| for (TypeName interfaceNode in implementsClause.interfaces) { |
| _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); |
| } |
| } |
| } |
| return super.visitClassTypeAlias(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitCompilationUnit(CompilationUnit node) { |
| CompilationUnitElement unitElement = node.element; |
| if (unitElement != null) { |
| _elementStack.add(unitElement); |
| _libraryElement = unitElement.enclosingElement; |
| if (_libraryElement != null) { |
| return super.visitCompilationUnit(node); |
| } |
| } |
| return null; |
| } |
| |
| @override |
| Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| ConstructorElement element = node.element; |
| // define |
| { |
| Location location; |
| if (node.name != null) { |
| int start = node.period.offset; |
| int end = node.name.end; |
| location = _createLocationForOffset(start, end - start); |
| } else { |
| int start = node.returnType.end; |
| location = _createLocationForOffset(start, 0); |
| } |
| recordRelationship(element, IndexConstants.NAME_IS_DEFINED_BY, location); |
| } |
| // visit children |
| enterScope(element); |
| try { |
| return super.visitConstructorDeclaration(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitConstructorName(ConstructorName node) { |
| ConstructorElement element = node.staticElement; |
| // in 'class B = A;' actually A constructors are invoked |
| if (element != null && |
| element.isSynthetic && |
| element.redirectedConstructor != null) { |
| element = element.redirectedConstructor; |
| } |
| // prepare location |
| Location location; |
| if (node.name != null) { |
| int start = node.period.offset; |
| int end = node.name.end; |
| location = _createLocationForOffset(start, end - start); |
| } else { |
| int start = node.type.end; |
| location = _createLocationForOffset(start, 0); |
| } |
| // record relationship |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| return super.visitConstructorName(node); |
| } |
| |
| @override |
| Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| LocalVariableElement element = node.element; |
| enterScope(element); |
| try { |
| return super.visitDeclaredIdentifier(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitExportDirective(ExportDirective node) { |
| ExportElement element = node.element; |
| if (element != null) { |
| LibraryElement expLibrary = element.exportedLibrary; |
| _recordLibraryReference(node, expLibrary); |
| } |
| return super.visitExportDirective(node); |
| } |
| |
| @override |
| Object visitFormalParameter(FormalParameter node) { |
| ParameterElement element = node.element; |
| enterScope(element); |
| try { |
| return super.visitFormalParameter(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitFunctionDeclaration(FunctionDeclaration node) { |
| Element element = node.element; |
| _recordElementDefinition(element); |
| enterScope(element); |
| try { |
| return super.visitFunctionDeclaration(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| Element element = node.element; |
| _recordElementDefinition(element); |
| return super.visitFunctionTypeAlias(node); |
| } |
| |
| @override |
| Object visitImportDirective(ImportDirective node) { |
| ImportElement element = node.element; |
| if (element != null) { |
| LibraryElement impLibrary = element.importedLibrary; |
| _recordLibraryReference(node, impLibrary); |
| } |
| return super.visitImportDirective(node); |
| } |
| |
| @override |
| Object visitIndexExpression(IndexExpression node) { |
| MethodElement element = node.bestElement; |
| if (element is MethodElement) { |
| Token operator = node.leftBracket; |
| Location location = _createLocationForToken(operator, element != null); |
| recordRelationship(element, IndexConstants.IS_INVOKED_BY, location); |
| } |
| return super.visitIndexExpression(node); |
| } |
| |
| @override |
| Object visitMethodDeclaration(MethodDeclaration node) { |
| ExecutableElement element = node.element; |
| enterScope(element); |
| try { |
| return super.visitMethodDeclaration(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitMethodInvocation(MethodInvocation node) { |
| SimpleIdentifier name = node.methodName; |
| Location location = _createLocationForNode(name); |
| // element invocation |
| Element element = name.bestElement; |
| if (element is MethodElement || |
| element is PropertyAccessorElement || |
| element is FunctionElement || |
| element is VariableElement) { |
| recordRelationship(element, IndexConstants.IS_INVOKED_BY, location); |
| } |
| // name invocation |
| { |
| Element nameElement = new NameElement(name.name); |
| _store.recordRelationship( |
| nameElement, |
| IndexConstants.IS_INVOKED_BY, |
| location); |
| } |
| _recordImportElementReferenceWithoutPrefix(name); |
| return super.visitMethodInvocation(node); |
| } |
| |
| @override |
| Object visitPartDirective(PartDirective node) { |
| Element element = node.element; |
| Location location = _createLocationForNode(node.uri); |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| return super.visitPartDirective(node); |
| } |
| |
| @override |
| Object visitPartOfDirective(PartOfDirective node) { |
| Location location = _createLocationForNode(node.libraryName); |
| recordRelationship(node.element, IndexConstants.IS_REFERENCED_BY, location); |
| return null; |
| } |
| |
| @override |
| Object visitPostfixExpression(PostfixExpression node) { |
| _recordOperatorReference(node.operator, node.bestElement); |
| return super.visitPostfixExpression(node); |
| } |
| |
| @override |
| Object visitPrefixExpression(PrefixExpression node) { |
| _recordOperatorReference(node.operator, node.bestElement); |
| return super.visitPrefixExpression(node); |
| } |
| |
| @override |
| Object visitSimpleIdentifier(SimpleIdentifier node) { |
| Element nameElement = new NameElement(node.name); |
| Location location = _createLocationForNode(node); |
| // name in declaration |
| if (node.inDeclarationContext()) { |
| recordRelationship( |
| nameElement, |
| IndexConstants.NAME_IS_DEFINED_BY, |
| location); |
| return null; |
| } |
| // prepare information |
| Element element = node.bestElement; |
| // stop if already handled |
| if (_isAlreadyHandledName(node)) { |
| return null; |
| } |
| // record name read/write |
| if (element != null && element.enclosingElement is ClassElement || |
| element == null && location.isQualified) { |
| bool inGetterContext = node.inGetterContext(); |
| bool inSetterContext = node.inSetterContext(); |
| if (inGetterContext && inSetterContext) { |
| _store.recordRelationship( |
| nameElement, |
| IndexConstants.IS_READ_WRITTEN_BY, |
| location); |
| } else if (inGetterContext) { |
| _store.recordRelationship( |
| nameElement, |
| IndexConstants.IS_READ_BY, |
| location); |
| } else if (inSetterContext) { |
| _store.recordRelationship( |
| nameElement, |
| IndexConstants.IS_WRITTEN_BY, |
| location); |
| } |
| } |
| // this.field parameter |
| if (element is FieldFormalParameterElement) { |
| element = (element as FieldFormalParameterElement).field; |
| } |
| // record specific relations |
| if (element is ClassElement || |
| element is FunctionElement || |
| element is FunctionTypeAliasElement || |
| element is LabelElement || |
| element is MethodElement || |
| element is PropertyAccessorElement || |
| element is PropertyInducingElement || |
| element is TypeParameterElement) { |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| } else if (element is PrefixElement) { |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| _recordImportElementReferenceWithPrefix(node); |
| } else if (element is ParameterElement || element is LocalVariableElement) { |
| bool inGetterContext = node.inGetterContext(); |
| bool inSetterContext = node.inSetterContext(); |
| if (inGetterContext && inSetterContext) { |
| recordRelationship( |
| element, |
| IndexConstants.IS_READ_WRITTEN_BY, |
| location); |
| } else if (inGetterContext) { |
| recordRelationship(element, IndexConstants.IS_READ_BY, location); |
| } else if (inSetterContext) { |
| recordRelationship(element, IndexConstants.IS_WRITTEN_BY, location); |
| } else { |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| } |
| } |
| _recordImportElementReferenceWithoutPrefix(node); |
| return super.visitSimpleIdentifier(node); |
| } |
| |
| @override |
| Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| ConstructorElement element = node.staticElement; |
| Location location; |
| if (node.constructorName != null) { |
| int start = node.period.offset; |
| int end = node.constructorName.end; |
| location = _createLocationForOffset(start, end - start); |
| } else { |
| int start = node.keyword.end; |
| location = _createLocationForOffset(start, 0); |
| } |
| recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); |
| return super.visitSuperConstructorInvocation(node); |
| } |
| |
| @override |
| Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| VariableDeclarationList variables = node.variables; |
| for (VariableDeclaration variableDeclaration in variables.variables) { |
| Element element = variableDeclaration.element; |
| _recordElementDefinition(element); |
| } |
| return super.visitTopLevelVariableDeclaration(node); |
| } |
| |
| @override |
| Object visitTypeParameter(TypeParameter node) { |
| TypeParameterElement element = node.element; |
| enterScope(element); |
| try { |
| return super.visitTypeParameter(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitVariableDeclaration(VariableDeclaration node) { |
| VariableElement element = node.element; |
| // record declaration |
| { |
| SimpleIdentifier name = node.name; |
| Location location = _createLocationForNode(name); |
| location = _getLocationWithExpressionType(location, node.initializer); |
| recordRelationship(element, IndexConstants.NAME_IS_DEFINED_BY, location); |
| } |
| // visit |
| enterScope(element); |
| try { |
| return super.visitVariableDeclaration(node); |
| } finally { |
| _exitScope(); |
| } |
| } |
| |
| @override |
| Object visitVariableDeclarationList(VariableDeclarationList node) { |
| NodeList<VariableDeclaration> variables = node.variables; |
| if (variables != null) { |
| // use first VariableDeclaration as Element for Location(s) in type |
| { |
| TypeName type = node.type; |
| if (type != null) { |
| for (VariableDeclaration variableDeclaration in variables) { |
| enterScope(variableDeclaration.element); |
| try { |
| type.accept(this); |
| } finally { |
| _exitScope(); |
| } |
| // only one iteration |
| break; |
| } |
| } |
| } |
| // visit variables |
| variables.accept(this); |
| } |
| return null; |
| } |
| |
| /** |
| * @return the [Location] representing location of the [AstNode]. |
| */ |
| Location _createLocationForNode(AstNode node) { |
| bool isQualified = _isQualifiedClassMemberAccess(node); |
| bool isResolved = true; |
| if (node is SimpleIdentifier) { |
| isResolved = node.bestElement != null; |
| } |
| Element element = peekElement(); |
| return new Location( |
| element, |
| node.offset, |
| node.length, |
| isQualified: isQualified, |
| isResolved: isResolved); |
| } |
| |
| /** |
| * [offset] - the offset of the location within [Source]. |
| * [length] - the length of the location. |
| * |
| * Returns the [Location] representing the given offset and length within the |
| * inner-most [Element]. |
| */ |
| Location _createLocationForOffset(int offset, int length) { |
| Element element = peekElement(); |
| return new Location(element, offset, length); |
| } |
| |
| /** |
| * @return the [Location] representing location of the [Token]. |
| */ |
| Location _createLocationForToken(Token token, bool isResolved) { |
| Element element = peekElement(); |
| return new Location( |
| element, |
| token.offset, |
| token.length, |
| isQualified: true, |
| isResolved: isResolved); |
| } |
| |
| /** |
| * Exit the current scope. |
| */ |
| void _exitScope() { |
| _elementStack.removeFirst(); |
| } |
| |
| /** |
| * @return `true` if given node already indexed as more interesting reference, so it should |
| * not be indexed again. |
| */ |
| bool _isAlreadyHandledName(SimpleIdentifier node) { |
| AstNode parent = node.parent; |
| if (parent is MethodInvocation) { |
| return parent.methodName == node; |
| } |
| return false; |
| } |
| |
| bool _isQualifiedClassMemberAccess(AstNode node) { |
| if (node is SimpleIdentifier) { |
| AstNode parent = node.parent; |
| if (parent is PrefixedIdentifier && parent.identifier == node) { |
| return parent.prefix.staticElement is! PrefixElement; |
| } |
| if (parent is PropertyAccess && parent.propertyName == node) { |
| return parent.realTarget != null; |
| } |
| if (parent is MethodInvocation && parent.methodName == node) { |
| Expression target = parent.realTarget; |
| if (target is SimpleIdentifier && |
| target.staticElement is PrefixElement) { |
| return false; |
| } |
| return target != null; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Records the [Element] definition in the library and universe. |
| */ |
| void _recordElementDefinition(Element element) { |
| Location location = createLocation(element); |
| Relationship relationship = IndexConstants.DEFINES; |
| recordRelationship(_libraryElement, relationship, location); |
| recordRelationship(UniverseElement.INSTANCE, relationship, location); |
| } |
| |
| /** |
| * Records [ImportElement] that declares given prefix and imports library with element used |
| * with given prefix node. |
| */ |
| void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) { |
| ImportElementInfo info = internal_getImportElementInfo(prefixNode); |
| if (info != null) { |
| int offset = prefixNode.offset; |
| int length = info.periodEnd - offset; |
| Location location = _createLocationForOffset(offset, length); |
| recordRelationship( |
| info.element, |
| IndexConstants.IS_REFERENCED_BY, |
| location); |
| } |
| } |
| |
| /** |
| * Records [ImportElement] reference if given [SimpleIdentifier] references some |
| * top-level element and not qualified with import prefix. |
| */ |
| void _recordImportElementReferenceWithoutPrefix(SimpleIdentifier node) { |
| if (_isIdentifierInImportCombinator(node)) { |
| return; |
| } |
| if (_isIdentifierInPrefixedIdentifier(node)) { |
| return; |
| } |
| Element element = node.staticElement; |
| ImportElement importElement = |
| internal_getImportElement(_libraryElement, null, element, _importElementsMap); |
| if (importElement != null) { |
| Location location = _createLocationForOffset(node.offset, 0); |
| recordRelationship( |
| importElement, |
| IndexConstants.IS_REFERENCED_BY, |
| location); |
| } |
| } |
| |
| /** |
| * Records reference to defining [CompilationUnitElement] of the given |
| * [LibraryElement]. |
| */ |
| void _recordLibraryReference(UriBasedDirective node, LibraryElement library) { |
| if (library != null) { |
| Location location = _createLocationForNode(node.uri); |
| recordRelationship( |
| library.definingCompilationUnit, |
| IndexConstants.IS_REFERENCED_BY, |
| location); |
| } |
| } |
| |
| /** |
| * Record reference to the given operator [Element] and name. |
| */ |
| void _recordOperatorReference(Token operator, Element element) { |
| // prepare location |
| Location location = _createLocationForToken(operator, element != null); |
| // record name reference |
| { |
| String name = operator.lexeme; |
| if (name == "++") { |
| name = "+"; |
| } |
| if (name == "--") { |
| name = "-"; |
| } |
| if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") { |
| name = name.substring(0, name.length - 1); |
| } |
| Element nameElement = new NameElement(name); |
| recordRelationship(nameElement, IndexConstants.IS_INVOKED_BY, location); |
| } |
| // record element reference |
| if (element != null) { |
| recordRelationship(element, IndexConstants.IS_INVOKED_BY, location); |
| } |
| } |
| |
| /** |
| * Records a relation between [superNode] and its [Element]. |
| */ |
| void _recordSuperType(TypeName superNode, Relationship relationship) { |
| if (superNode != null) { |
| Identifier superName = superNode.name; |
| if (superName != null) { |
| Element superElement = superName.staticElement; |
| recordRelationship( |
| superElement, |
| relationship, |
| _createLocationForNode(superNode)); |
| } |
| } |
| } |
| |
| /** |
| * Creates a [Location] representing declaration of the [Element]. |
| */ |
| static Location createLocation(Element element) { |
| if (element != null) { |
| int offset = element.nameOffset; |
| int length = element.displayName.length; |
| return new Location(element, offset, length); |
| } |
| return null; |
| } |
| |
| /** |
| * If the given expression has resolved type, returns the new location with this type. |
| * |
| * [location] - the base location |
| * [expression] - the expression assigned at the given location |
| */ |
| static Location _getLocationWithExpressionType(Location location, |
| Expression expression) { |
| if (expression != null) { |
| return new LocationWithData<DartType>(location, expression.bestType); |
| } |
| return location; |
| } |
| |
| /** |
| * @return `true` if given "node" is part of an import [Combinator]. |
| */ |
| static bool _isIdentifierInImportCombinator(SimpleIdentifier node) { |
| AstNode parent = node.parent; |
| return parent is Combinator; |
| } |
| |
| /** |
| * @return `true` if given "node" is part of [PrefixedIdentifier] "prefix.node". |
| */ |
| static bool _isIdentifierInPrefixedIdentifier(SimpleIdentifier node) { |
| AstNode parent = node.parent; |
| return parent is PrefixedIdentifier && parent.identifier == node; |
| } |
| } |