Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
index 323fa7c1cb28fd17337cf4c0f52a1808f89bb63c..24c26845593519dfeae6131435cf5449d301dad4 100644 |
--- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
+++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
@@ -14,18 +14,263 @@ |
package com.google.dart.engine.internal.index; |
+import com.google.common.annotations.VisibleForTesting; |
+import com.google.common.collect.Lists; |
+import com.google.dart.engine.ast.ASTNode; |
+import com.google.dart.engine.ast.ClassDeclaration; |
+import com.google.dart.engine.ast.CompilationUnit; |
+import com.google.dart.engine.ast.ExtendsClause; |
+import com.google.dart.engine.ast.FunctionDeclaration; |
+import com.google.dart.engine.ast.FunctionTypeAlias; |
+import com.google.dart.engine.ast.ImplementsClause; |
+import com.google.dart.engine.ast.MethodInvocation; |
+import com.google.dart.engine.ast.PrefixedIdentifier; |
+import com.google.dart.engine.ast.SimpleIdentifier; |
+import com.google.dart.engine.ast.TopLevelVariableDeclaration; |
+import com.google.dart.engine.ast.TypeName; |
+import com.google.dart.engine.ast.VariableDeclaration; |
+import com.google.dart.engine.ast.VariableDeclarationList; |
import com.google.dart.engine.ast.visitor.RecursiveASTVisitor; |
+import com.google.dart.engine.element.ClassElement; |
+import com.google.dart.engine.element.CompilationUnitElement; |
+import com.google.dart.engine.element.Element; |
+import com.google.dart.engine.element.ElementProxy; |
+import com.google.dart.engine.element.FieldElement; |
+import com.google.dart.engine.element.LibraryElement; |
import com.google.dart.engine.index.IndexStore; |
+import com.google.dart.engine.index.Location; |
+import com.google.dart.engine.index.Relationship; |
+import com.google.dart.engine.source.Source; |
+import com.google.dart.engine.type.Type; |
+import com.google.dart.engine.utilities.collection.IntStack; |
+ |
+import java.util.LinkedList; |
/** |
* Visits resolved AST and adds relationships into {@link IndexStore}. |
*/ |
public class IndexContributor extends RecursiveASTVisitor<Void> { |
+ /** |
+ * @return the {@link Location} representing location of the {@link Element}. |
+ */ |
+ @VisibleForTesting |
+ static Location createElementLocation(Element element) { |
+ if (element != null) { |
+ int offset = element.getNameOffset(); |
+ int length = element.getName().length(); |
+ String prefix = null; |
+ return new Location(new ElementProxy(element), offset, length, prefix); |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * @return <code>true</code> if given {@link SimpleIdentifier} is "name" part of prefixed |
+ * identifier or method invocation. |
+ */ |
+ private static boolean isQualified(SimpleIdentifier node) { |
+ if (node.getParent() instanceof PrefixedIdentifier) { |
+ return ((PrefixedIdentifier) node.getParent()).getIdentifier() == node; |
+ } |
+ if (node.getParent() instanceof MethodInvocation) { |
+ return ((MethodInvocation) node.getParent()).getMethodName() == node; |
+ } |
+ return false; |
+ } |
+ |
private final IndexStore store; |
+ private LibraryElement libraryElement; |
+ |
+ /** |
+ * A stack whose top element (the element with the largest index) is an element representing the |
+ * inner-most enclosing scope. |
+ */ |
+ private LinkedList<Element> elementStack = Lists.newLinkedList(); |
+ |
+ /** |
+ * A stack containing one value for each name scope that has been entered, where the values are a |
+ * count of the number of unnamed functions that have been found within that scope. These counts |
+ * are used to synthesize a name for those functions. The innermost scope is at the top of the |
+ * stack. |
+ */ |
+ private IntStack unnamedFunctionCount = new IntStack(); |
+ |
public IndexContributor(IndexStore store) { |
this.store = store; |
} |
+ @Override |
+ public Void visitClassDeclaration(ClassDeclaration node) { |
+ // TODO(scheglov) mixins |
+ ClassElement element = node.getElement(); |
+ enterScope(element); |
+ try { |
+ recordRelationship( |
+ libraryElement, |
+ IndexConstants.DEFINES_CLASS, |
+ createElementLocation(element)); |
+ { |
+ ExtendsClause extendsClause = node.getExtendsClause(); |
+ if (extendsClause != null) { |
+ TypeName superclassNode = extendsClause.getSuperclass(); |
+ recordSuperType(element, superclassNode, IndexConstants.IS_EXTENDED_BY); |
+ } |
+ } |
+ { |
+ ImplementsClause implementsClause = node.getImplementsClause(); |
+ if (implementsClause != null) { |
+ for (TypeName interfaceNode : implementsClause.getInterfaces()) { |
+ recordSuperType(element, interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); |
+ } |
+ } |
+ } |
+ return super.visitClassDeclaration(node); |
+ } finally { |
+ exitScope(); |
+ } |
+ } |
+ |
+ @Override |
+ public Void visitCompilationUnit(CompilationUnit node) { |
+ CompilationUnitElement unitElement = node.getElement(); |
+ if (unitElement != null) { |
+ elementStack.add(unitElement); |
+ libraryElement = unitElement.getEnclosingElement(); |
+ if (libraryElement != null) { |
+ return super.visitCompilationUnit(node); |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @Override |
+ public Void visitFunctionDeclaration(FunctionDeclaration node) { |
+ Element element = node.getElement(); |
+ Location location = createElementLocation(element); |
+ recordRelationship(libraryElement, IndexConstants.DEFINES_FUNCTION, location); |
+ return super.visitFunctionDeclaration(node); |
+ } |
+ |
+ @Override |
+ public Void visitFunctionTypeAlias(FunctionTypeAlias node) { |
+ Element element = node.getElement(); |
+ Location location = createElementLocation(element); |
+ recordRelationship(libraryElement, IndexConstants.DEFINES_FUNCTION_TYPE, location); |
+ return super.visitFunctionTypeAlias(node); |
+ } |
+ |
+ @Override |
+ public Void visitSimpleIdentifier(SimpleIdentifier node) { |
+ Element element = node.getElement(); |
+ if (element instanceof FieldElement) { |
+ FieldElement fieldElement = (FieldElement) element; |
+ Location location = createLocation(node); |
+ if (node.inGetterContext()) { |
+ if (isQualified(node)) { |
+ recordRelationship(fieldElement, IndexConstants.IS_ACCESSED_BY_QUALIFIED, location); |
+ } else { |
+ recordRelationship(fieldElement, IndexConstants.IS_ACCESSED_BY_UNQUALIFIED, location); |
+ } |
+ } else { |
+ // TODO(scheglov) |
+ throw new UnsupportedOperationException(); |
+ } |
+ } |
+ return super.visitSimpleIdentifier(node); |
+ } |
+ |
+ @Override |
+ public Void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
+ VariableDeclarationList variables = node.getVariables(); |
+ for (VariableDeclaration variableDeclaration : variables.getVariables()) { |
+ Element element = variableDeclaration.getElement(); |
+ Location location = createElementLocation(element); |
+ recordRelationship(libraryElement, IndexConstants.DEFINES_VARIABLE, location); |
+ } |
+ return super.visitTopLevelVariableDeclaration(node); |
+ } |
+ |
+ /** |
+ * Enter a new scope represented by the given {@link Element}. |
+ */ |
+ @VisibleForTesting |
+ void enterScope(Element element) { |
+ elementStack.addFirst(element); |
+ unnamedFunctionCount.push(0); |
+ } |
+ |
+// /** |
+// * @return the {@link Location} representing the location of the name of the given class. |
+// */ |
+// private Location createNameLocation(ClassDeclaration node) { |
+// return createNameLocation(node.getName()); |
+// } |
+ |
+ /** |
+ * @return the inner-most enclosing {@link Element}, may be <code>null</code>. |
+ */ |
+ @VisibleForTesting |
+ Element peekElement() { |
+ for (Element element : elementStack) { |
+ if (element != null) { |
+ return element; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * @return the {@link Location} representing location of the {@link ASTNode}. |
+ */ |
+ private Location createLocation(ASTNode node) { |
+ // TODO(scheglov) get actual prefix |
+ String prefix = null; |
+ return createLocation(node.getOffset(), node.getLength(), prefix); |
+ } |
+ |
+ /** |
+ * @param offset the offset of the location within {@link Source} |
+ * @param length the length of the location |
+ * @param prefix the import prefix of top-level element, may be <code>null</code> |
+ * @return the {@link Location} representing the given offset and length within the inner-most |
+ * {@link Element}. |
+ */ |
+ private Location createLocation(int offset, int length, String prefix) { |
+ Element element = peekElement(); |
+ return new Location(new ElementProxy(element), offset, length, prefix); |
+ } |
+ |
+ /** |
+ * Exit the current scope. |
+ */ |
+ private void exitScope() { |
+ elementStack.removeFirst(); |
+ unnamedFunctionCount.pop(); |
+ } |
+ |
+ /** |
+ * Record the given relationship between the given {@link Element} and {@link Location}. |
+ */ |
+ private void recordRelationship(Element element, Relationship relationship, Location location) { |
+ if (element != null && location != null) { |
+ store.recordRelationship(new ElementProxy(element), relationship, location); |
+ } |
+ } |
+ |
+ /** |
+ * Records extends/implements relationships between given {@link ClassElement} and {@link Type} of |
+ * "superNode". |
+ */ |
+ private void recordSuperType(ClassElement element, TypeName superNode, Relationship relationship) { |
+ if (element != null) { |
+ Type superType = superNode.getType(); |
+ if (superType != null) { |
+ Element superElement = superType.getElement(); |
+ recordRelationship(superElement, relationship, createLocation(superNode)); |
+ } |
+ } |
+ } |
+ |
} |