Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Unified Diff: pkg/analyzer/lib/src/index/index.dart

Issue 363373002: IndexStore, Index and SearchEngine APIs and partial implementatons. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer/lib/src/index/index.dart
diff --git a/pkg/analyzer/lib/src/index/index.dart b/pkg/analyzer/lib/src/index/index.dart
new file mode 100644
index 0000000000000000000000000000000000000000..68577a5ca525e8c66fd79992c6fc9f6bc446ce23
--- /dev/null
+++ b/pkg/analyzer/lib/src/index/index.dart
@@ -0,0 +1,1192 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.src.index;
+
+import 'dart:collection' show Queue;
+
+import 'package:analyzer/index/index.dart';
+import 'package:analyzer/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_core.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/resolver.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) {
+ CompilationUnitElement unitElement = unit.element;
+ bool mayIndex = store.aboutToIndexDart(context, unitElement);
+ if (!mayIndex) {
+ return;
+ }
+ 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)
+ {
+ HtmlElement unitElement = unit.element;
+ bool mayIndex = store.aboutToIndexHtml(context, unitElement);
+ if (!mayIndex) {
+ return;
+ }
+ 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_QUALIFIED, location);
+ }
+ }
+ // setter reference
+ if (property.propertyKind.callsSetter()) {
+ PropertyAccessorElement setter = field.setter;
+ if (setter != null) {
+ _store.recordRelationship(setter,
+ IndexConstants.IS_REFERENCED_BY_QUALIFIED, 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.
+ *
+ * @param 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) {
+ SimpleIdentifier identifier = expression;
Brian Wilkerson 2014/07/03 18:58:13 I'm not sure what the value of this local variable
scheglov 2014/07/03 19:06:16 Fixed. Should it be a hint?
Brian Wilkerson 2014/07/03 20:48:19 Perhaps. If the original variable and the new vari
+ Element element = identifier.bestElement;
+ if (element is AngularElement) {
+ _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE,
+ _createLocationForIdentifier(identifier));
+ 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 [HtmlUnit] and every embedded [Expression].
Brian Wilkerson 2014/07/03 18:58:13 "visits" --> "visits an"
scheglov 2014/07/03 19:06:16 Done.
+ */
+abstract class _ExpressionVisitor extends ht.RecursiveXmlVisitor<Object> {
+ /**
+ * Visits the given [Expression]s embedded into tag or attribute.
+ *
+ * @param 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);
+ }
+ }
+ }
+}
+
+
+/**
+ * Information about [ImportElement] and place where it is referenced using
+ * [PrefixElement].
+ */
+class _ImportElementInfo {
+ ImportElement _element;
+
+ int _periodEnd = 0;
+}
+
+
+/**
+ * Visits 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, IndexConstants.DEFINES_CLASS);
+ {
+ 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,
+ _createLocationFromOffset(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, IndexConstants.DEFINES_CLASS_ALIAS);
+ {
+ 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 = _createLocationFromOffset(start, end - start);
+ } else {
+ int start = node.returnType.end;
+ location = _createLocationFromOffset(start, 0);
+ }
+ recordRelationship(element, IndexConstants.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 = _createLocationFromOffset(start, end - start);
+ } else {
+ int start = node.type.end;
+ location = _createLocationFromOffset(start, 0);
+ }
+ // record relationship
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
+ return super.visitConstructorName(node);
+ }
+
+ @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, IndexConstants.DEFINES_FUNCTION);
+ enterScope(element);
+ try {
+ return super.visitFunctionDeclaration(node);
+ } finally {
+ _exitScope();
+ }
+ }
+
+ @override
+ Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+ Element element = node.element;
+ _recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION_TYPE);
+ 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 = _createLocationFromToken(operator);
+ recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED,
+ 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;
+ Element element = name.bestElement;
+ if (element is MethodElement || element is PropertyAccessorElement) {
+ Location location = _createLocationFromNode(name);
+ Relationship relationship;
+ if (node.target != null) {
+ relationship = IndexConstants.IS_INVOKED_BY_QUALIFIED;
+ } else {
+ relationship = IndexConstants.IS_INVOKED_BY_UNQUALIFIED;
+ }
+ recordRelationship(element, relationship, location);
+ }
+ if (element is FunctionElement || element is VariableElement) {
+ Location location = _createLocationFromNode(name);
+ recordRelationship(element, IndexConstants.IS_INVOKED_BY, location);
+ }
+ // name invocation
+ {
+ Element nameElement = new NameElement(name.name);
+ Location location = _createLocationFromNode(name);
+ Relationship kind = element != null ?
+ IndexConstants.NAME_IS_INVOKED_BY_RESOLVED :
+ IndexConstants.NAME_IS_INVOKED_BY_UNRESOLVED;
+ _store.recordRelationship(nameElement, kind, location);
+ }
+ _recordImportElementReferenceWithoutPrefix(name);
+ return super.visitMethodInvocation(node);
+ }
+
+ @override
+ Object visitPartDirective(PartDirective node) {
+ Element element = node.element;
+ Location location = _createLocationFromNode(node.uri);
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
+ return super.visitPartDirective(node);
+ }
+
+ @override
+ Object visitPartOfDirective(PartOfDirective node) {
+ Location location = _createLocationFromNode(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 = _createLocationFromNode(node);
+ // name in declaration
+ if (node.inDeclarationContext()) {
+ recordRelationship(nameElement, IndexConstants.IS_DEFINED_BY, location);
+ return null;
+ }
+ // prepare information
+ Element element = node.bestElement;
+ // qualified name reference
+ _recordQualifiedMemberReference(node, element, nameElement, location);
+ // stop if already handled
+ if (_isAlreadyHandledName(node)) {
+ return null;
+ }
+ // record name read/write
+ {
+ bool inGetterContext = node.inGetterContext();
+ bool inSetterContext = node.inSetterContext();
+ if (inGetterContext && inSetterContext) {
+ Relationship kind = element != null ?
+ IndexConstants.NAME_IS_READ_WRITTEN_BY_RESOLVED :
+ IndexConstants.NAME_IS_READ_WRITTEN_BY_UNRESOLVED;
+ _store.recordRelationship(nameElement, kind, location);
+ } else if (inGetterContext) {
+ Relationship kind = element != null ?
+ IndexConstants.NAME_IS_READ_BY_RESOLVED :
+ IndexConstants.NAME_IS_READ_BY_UNRESOLVED;
+ _store.recordRelationship(nameElement, kind, location);
+ } else if (inSetterContext) {
+ Relationship kind = element != null ?
+ IndexConstants.NAME_IS_WRITTEN_BY_RESOLVED :
+ IndexConstants.NAME_IS_WRITTEN_BY_UNRESOLVED;
+ _store.recordRelationship(nameElement, kind, location);
+ }
+ }
+ // record specific relations
+ if (element is ClassElement || element is FunctionElement || element is
+ FunctionTypeAliasElement || element is LabelElement || element is
+ TypeParameterElement) {
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
+ } else if (element is FieldElement) {
+ location = _getLocationWithInitializerType(node, location);
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
+ } else if (element is FieldFormalParameterElement) {
+ FieldFormalParameterElement fieldParameter = element;
+ FieldElement field = fieldParameter.field;
+ recordRelationship(field, IndexConstants.IS_REFERENCED_BY_QUALIFIED,
+ location);
+ } else if (element is PrefixElement) {
+ _recordImportElementReferenceWithPrefix(node);
+ } else if (element is PropertyAccessorElement || element is MethodElement) {
+ location = _getLocationWithTypeAssignedToField(node, element, location);
+ if (node.isQualified) {
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY_QUALIFIED,
+ location);
+ } else {
+ recordRelationship(element, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED,
+ location);
+ }
+ } 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 = _createLocationFromOffset(start, end - start);
+ } else {
+ int start = node.keyword.end;
+ location = _createLocationFromOffset(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, IndexConstants.DEFINES_VARIABLE);
+ }
+ 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 = _createLocationFromNode(name);
+ location = _getLocationWithExpressionType(location, node.initializer);
+ recordRelationship(element, IndexConstants.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 _createLocationFromNode(AstNode node) => _createLocationFromOffset(
+ node.offset, node.length);
+
+ /**
+ * @param offset the offset of the location within [Source]
+ * @param length the length of the location
+ * @return the [Location] representing the given offset and length within the inner-most
+ * [Element].
+ */
+ Location _createLocationFromOffset(int offset, int length) {
+ Element element = peekElement();
+ return new Location(element, offset, length);
+ }
+
+ /**
+ * @return the [Location] representing location of the [Token].
+ */
+ Location _createLocationFromToken(Token token) => _createLocationFromOffset(
+ token.offset, token.length);
+
+ /**
+ * 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 identical(parent.methodName, node);
+ }
+ return false;
+ }
+
+ /**
+ * Records the [Element] definition in the library and universe.
+ */
+ void _recordElementDefinition(Element element, Relationship relationship) {
+ Location location = createLocation(element);
+ recordRelationship(_libraryElement, relationship, location);
+ recordRelationship(IndexConstants.UNIVERSE, relationship, location);
+ }
+
+ /**
+ * Records [ImportElement] that declares given prefix and imports library with element used
+ * with given prefix node.
+ */
+ void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) {
+ _ImportElementInfo info = getImportElementInfo(prefixNode);
+ if (info != null) {
+ int offset = prefixNode.offset;
+ int length = info._periodEnd - offset;
+ Location location = _createLocationFromOffset(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 = _internalGetImportElement(_libraryElement,
+ null, element, _importElementsMap);
+ if (importElement != null) {
+ Location location = _createLocationFromOffset(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 = _createLocationFromNode(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 = _createLocationFromToken(operator);
+ // 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);
+ Relationship relationship = element != null ?
+ IndexConstants.IS_REFERENCED_BY_QUALIFIED_RESOLVED :
+ IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED;
+ recordRelationship(nameElement, relationship, location);
+ }
+ // record element reference
+ if (element != null) {
+ recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED,
+ location);
+ }
+ }
+
+ /**
+ * Records reference if the given [SimpleIdentifier] looks like a qualified property access
+ * or method invocation.
+ */
+ void _recordQualifiedMemberReference(SimpleIdentifier node, Element element,
+ Element nameElement, Location location) {
+ if (node.isQualified) {
+ Relationship relationship = element != null ?
+ IndexConstants.IS_REFERENCED_BY_QUALIFIED_RESOLVED :
+ IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED;
+ recordRelationship(nameElement, relationship, location);
+ }
+ }
+
+ /**
+ * Records extends/implements relationships between given [ClassElement] and [Type] of
+ * "superNode".
+ */
+ void _recordSuperType(TypeName superNode, Relationship relationship) {
+ if (superNode != null) {
+ Identifier superName = superNode.name;
+ if (superName != null) {
+ Element superElement = superName.staticElement;
+ recordRelationship(superElement, relationship, _createLocationFromNode(
+ superNode));
+ }
+ }
+ }
+
+ /**
+ * @return the [Location] representing location 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;
+ }
+
+ /**
+ * @return the [ImportElement] that is referenced by this node with [PrefixElement],
+ * may be `null`.
+ */
+ static ImportElement getImportElement(SimpleIdentifier prefixNode) {
+ _ImportElementInfo info = getImportElementInfo(prefixNode);
+ return info != null ? info._element : null;
+ }
+
+ /**
+ * @return the [ImportElementInfo] with [ImportElement] that is referenced by this
+ * node with [PrefixElement], may be `null`.
+ */
+ static _ImportElementInfo getImportElementInfo(SimpleIdentifier prefixNode) {
+ _ImportElementInfo info = new _ImportElementInfo();
+ // prepare environment
+ AstNode parent = prefixNode.parent;
+ CompilationUnit unit = prefixNode.getAncestor((node) => node is
+ CompilationUnit);
+ LibraryElement libraryElement = unit.element.library;
+ // prepare used element
+ Element usedElement = null;
+ if (parent is PrefixedIdentifier) {
+ PrefixedIdentifier prefixed = parent;
+ if (identical(prefixed.prefix, prefixNode)) {
+ usedElement = prefixed.staticElement;
+ info._periodEnd = prefixed.period.end;
+ }
+ }
+ if (parent is MethodInvocation) {
+ MethodInvocation invocation = parent;
+ if (identical(invocation.target, prefixNode)) {
+ usedElement = invocation.methodName.staticElement;
+ info._periodEnd = invocation.period.end;
+ }
+ }
+ // we need used Element
+ if (usedElement == null) {
+ return null;
+ }
+ // find ImportElement
+ String prefix = prefixNode.name;
+ Map<ImportElement, Set<Element>> importElementsMap = {};
+ info._element = _internalGetImportElement(libraryElement, prefix,
+ usedElement, importElementsMap);
+ if (info._element == null) {
+ return null;
+ }
+ return info;
+ }
+
+ /**
+ * If the given expression has resolved type, returns the new location with this type.
+ *
+ * @param location the base location
+ * @param 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;
+ }
+
+ /**
+ * If the given node is the part of the [ConstructorFieldInitializer], returns location with
+ * type of the initializer expression.
+ */
+ static Location _getLocationWithInitializerType(SimpleIdentifier node,
+ Location location) {
+ if (node.parent is ConstructorFieldInitializer) {
+ ConstructorFieldInitializer initializer = node.parent as
+ ConstructorFieldInitializer;
+ if (identical(initializer.fieldName, node)) {
+ location = _getLocationWithExpressionType(location,
+ initializer.expression);
+ }
+ }
+ return location;
+ }
+
+ /**
+ * If the given identifier has a synthetic [PropertyAccessorElement], i.e. accessor for
+ * normal field, and it is LHS of assignment, then include [Type] of the assigned value into
+ * the [Location].
+ *
+ * @param identifier the identifier to record location
+ * @param element the element of the identifier
+ * @param location the raw location
+ * @return the [Location] with the type of the assigned value
+ */
+ static Location
+ _getLocationWithTypeAssignedToField(SimpleIdentifier identifier,
+ Element element, Location location) {
+ // we need accessor
+ if (element is! PropertyAccessorElement) {
+ return location;
+ }
+ PropertyAccessorElement accessor = element as PropertyAccessorElement;
+ // should be setter
+ if (!accessor.isSetter) {
+ return location;
+ }
+ // accessor should be synthetic, i.e. field normal
+ if (!accessor.isSynthetic) {
+ return location;
+ }
+ // should be LHS of assignment
+ AstNode parent;
+ {
+ AstNode node = identifier;
+ parent = node.parent;
+ // new T().field = x;
+ if (parent is PropertyAccess) {
+ PropertyAccess propertyAccess = parent as PropertyAccess;
+ if (identical(propertyAccess.propertyName, node)) {
+ node = propertyAccess;
+ parent = propertyAccess.parent;
+ }
+ }
+ // obj.field = x;
+ if (parent is PrefixedIdentifier) {
+ PrefixedIdentifier prefixedIdentifier = parent as PrefixedIdentifier;
+ if (identical(prefixedIdentifier.identifier, node)) {
+ node = prefixedIdentifier;
+ parent = prefixedIdentifier.parent;
+ }
+ }
+ }
+ // OK, remember the type
+ if (parent is AssignmentExpression) {
+ AssignmentExpression assignment = parent as AssignmentExpression;
+ Expression rhs = assignment.rightHandSide;
+ location = _getLocationWithExpressionType(location, rhs);
+ }
+ // done
+ return location;
+ }
+
+ /**
+ * @return the [ImportElement] that declares given [PrefixElement] and imports library
+ * with given "usedElement".
+ */
+ static ImportElement _internalGetImportElement(LibraryElement libraryElement,
+ String prefix, Element usedElement, Map<ImportElement,
+ Set<Element>> importElementsMap) {
+ // validate Element
+ if (usedElement == null) {
+ return null;
+ }
+ if (usedElement.enclosingElement is! CompilationUnitElement) {
+ return null;
+ }
+ LibraryElement usedLibrary = usedElement.library;
+ // find ImportElement that imports used library with used prefix
+ List<ImportElement> candidates = null;
+ for (ImportElement importElement in libraryElement.imports) {
+ // required library
+ if (importElement.importedLibrary != usedLibrary) {
+ continue;
+ }
+ // required prefix
+ PrefixElement prefixElement = importElement.prefix;
+ if (prefix == null) {
+ if (prefixElement != null) {
+ continue;
+ }
+ } else {
+ if (prefixElement == null) {
+ continue;
+ }
+ if (prefix != prefixElement.name) {
+ continue;
+ }
+ }
+ // no combinators => only possible candidate
+ if (importElement.combinators.length == 0) {
+ return importElement;
+ }
+ // OK, we have candidate
+ if (candidates == null) {
+ candidates = [];
+ }
+ candidates.add(importElement);
+ }
+ // no candidates, probably element is defined in this library
+ if (candidates == null) {
+ return null;
+ }
+ // one candidate
+ if (candidates.length == 1) {
+ return candidates[0];
+ }
+ // ensure that each ImportElement has set of elements
+ for (ImportElement importElement in candidates) {
+ if (importElementsMap.containsKey(importElement)) {
+ continue;
+ }
+ Namespace namespace = new NamespaceBuilder(
+ ).createImportNamespaceForDirective(importElement);
+ Set<Element> elements = new Set();
+ importElementsMap[importElement] = elements;
+ }
+ // use import namespace to choose correct one
+ for (MapEntry<ImportElement, Set<Element>> entry in getMapEntrySet(
+ importElementsMap)) {
+ if (entry.getValue().contains(usedElement)) {
+ return entry.getKey();
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ * @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 && identical(parent.identifier, node);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698