/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.index;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.dart.engine.ast.AssignmentExpression;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassTypeAlias;
import com.google.dart.engine.ast.Combinator;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.ConstructorFieldInitializer;
import com.google.dart.engine.ast.ConstructorName;
import com.google.dart.engine.ast.ExportDirective;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.ExtendsClause;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.FunctionDeclaration;
import com.google.dart.engine.ast.FunctionTypeAlias;
import com.google.dart.engine.ast.Identifier;
import com.google.dart.engine.ast.ImplementsClause;
import com.google.dart.engine.ast.ImportDirective;
import com.google.dart.engine.ast.IndexExpression;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.PartDirective;
import com.google.dart.engine.ast.PartOfDirective;
import com.google.dart.engine.ast.PostfixExpression;
import com.google.dart.engine.ast.PrefixExpression;
import com.google.dart.engine.ast.PrefixedIdentifier;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SuperConstructorInvocation;
import com.google.dart.engine.ast.TopLevelVariableDeclaration;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.TypeParameter;
import com.google.dart.engine.ast.UriBasedDirective;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.VariableDeclarationList;
import com.google.dart.engine.ast.WithClause;
import com.google.dart.engine.ast.visitor.GeneralizingAstVisitor;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ExecutableElement;
import com.google.dart.engine.element.ExportElement;
import com.google.dart.engine.element.FieldElement;
import com.google.dart.engine.element.FieldFormalParameterElement;
import com.google.dart.engine.element.FunctionElement;
import com.google.dart.engine.element.FunctionTypeAliasElement;
import com.google.dart.engine.element.ImportElement;
import com.google.dart.engine.element.LabelElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.LocalVariableElement;
import com.google.dart.engine.element.MethodElement;
import com.google.dart.engine.element.ParameterElement;
import com.google.dart.engine.element.PrefixElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.element.TypeParameterElement;
import com.google.dart.engine.element.VariableElement;
import com.google.dart.engine.index.IndexStore;
import com.google.dart.engine.index.Location;
import com.google.dart.engine.index.LocationWithData;
import com.google.dart.engine.index.Relationship;
import com.google.dart.engine.internal.scope.Namespace;
import com.google.dart.engine.internal.scope.NamespaceBuilder;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.type.Type;
import com.google.dart.engine.utilities.general.StringUtilities;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Visits resolved AST and adds relationships into {@link IndexStore}.
*
* @coverage dart.engine.index
*/
public class IndexContributor extends GeneralizingAstVisitor<Void> {
/**
* Information about {@link ImportElement} and place where it is referenced using
* {@link PrefixElement}.
*/
private static class ImportElementInfo {
ImportElement element;
int periodEnd;
}
/**
* @return the {@link Location} representing location of the {@link Element}.
*/
@VisibleForTesting
public static Location createLocation(Element element) {
if (element != null) {
int offset = element.getNameOffset();
int length = element.getDisplayName().length();
return new Location(element, offset, length);
}
return null;
}
/**
* @return the {@link ImportElement} that is referenced by this node with {@link PrefixElement},
* may be {@code null}.
*/
public static ImportElement getImportElement(SimpleIdentifier prefixNode) {
ImportElementInfo info = getImportElementInfo(prefixNode);
return info != null ? info.element : null;
}
/**
* @return the {@link ImportElementInfo} with {@link ImportElement} that is referenced by this
* node with {@link PrefixElement}, may be {@code null}.
*/
public static ImportElementInfo getImportElementInfo(SimpleIdentifier prefixNode) {
ImportElementInfo info = new ImportElementInfo();
// prepare environment
AstNode parent = prefixNode.getParent();
CompilationUnit unit = prefixNode.getAncestor(CompilationUnit.class);
LibraryElement libraryElement = unit.getElement().getLibrary();
// prepare used element
Element usedElement = null;
if (parent instanceof PrefixedIdentifier) {
PrefixedIdentifier prefixed = (PrefixedIdentifier) parent;
if (prefixed.getPrefix() == prefixNode) {
usedElement = prefixed.getStaticElement();
info.periodEnd = prefixed.getPeriod().getEnd();
}
}
if (parent instanceof MethodInvocation) {
MethodInvocation invocation = (MethodInvocation) parent;
if (invocation.getTarget() == prefixNode) {
usedElement = invocation.getMethodName().getStaticElement();
info.periodEnd = invocation.getPeriod().getEnd();
}
}
// we need used Element
if (usedElement == null) {
return null;
}
// find ImportElement
String prefix = prefixNode.getName();
Map<ImportElement, Set<Element>> importElementsMap = Maps.newHashMap();
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
*/
private static Location getLocationWithExpressionType(Location location, Expression expression) {
if (expression != null) {
return new LocationWithData<Type>(location, expression.getBestType());
}
return location;
}
/**
* If the given node is the part of the {@link ConstructorFieldInitializer}, returns location with
* type of the initializer expression.
*/
private static Location getLocationWithInitializerType(SimpleIdentifier node, Location location) {
if (node.getParent() instanceof ConstructorFieldInitializer) {
ConstructorFieldInitializer initializer = (ConstructorFieldInitializer) node.getParent();
if (initializer.getFieldName() == node) {
location = getLocationWithExpressionType(location, initializer.getExpression());
}
}
return location;
}
/**
* If the given identifier has a synthetic {@link PropertyAccessorElement}, i.e. accessor for
* normal field, and it is LHS of assignment, then include {@link Type} of the assigned value into
* the {@link Location}.
*
* @param identifier the identifier to record location
* @param element the element of the identifier
* @param location the raw location
* @return the {@link Location} with the type of the assigned value
*/
private static Location getLocationWithTypeAssignedToField(SimpleIdentifier identifier,
Element element, Location location) {
// we need accessor
if (!(element instanceof PropertyAccessorElement)) {
return location;
}
PropertyAccessorElement accessor = (PropertyAccessorElement) element;
// 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.getParent();
// new T().field = x;
if (parent instanceof PropertyAccess) {
PropertyAccess propertyAccess = (PropertyAccess) parent;
if (propertyAccess.getPropertyName() == node) {
node = propertyAccess;
parent = propertyAccess.getParent();
}
}
// obj.field = x;
if (parent instanceof PrefixedIdentifier) {
PrefixedIdentifier prefixedIdentifier = (PrefixedIdentifier) parent;
if (prefixedIdentifier.getIdentifier() == node) {
node = prefixedIdentifier;
parent = prefixedIdentifier.getParent();
}
}
}
// OK, remember the type
if (parent instanceof AssignmentExpression) {
AssignmentExpression assignment = (AssignmentExpression) parent;
Expression rhs = assignment.getRightHandSide();
location = getLocationWithExpressionType(location, rhs);
}
// done
return location;
}
/**
* @return the {@link ImportElement} that declares given {@link PrefixElement} and imports library
* with given "usedElement".
*/
private static ImportElement internalGetImportElement(LibraryElement libraryElement,
String prefix, Element usedElement, Map<ImportElement, Set<Element>> importElementsMap) {
// validate Element
if (usedElement == null) {
return null;
}
if (!(usedElement.getEnclosingElement() instanceof CompilationUnitElement)) {
return null;
}
LibraryElement usedLibrary = usedElement.getLibrary();
// find ImportElement that imports used library with used prefix
List<ImportElement> candidates = null;
for (ImportElement importElement : libraryElement.getImports()) {
// required library
if (!Objects.equal(importElement.getImportedLibrary(), usedLibrary)) {
continue;
}
// required prefix
PrefixElement prefixElement = importElement.getPrefix();
if (prefix == null) {
if (prefixElement != null) {
continue;
}
} else {
if (prefixElement == null) {
continue;
}
if (!prefix.equals(prefixElement.getName())) {
continue;
}
}
// no combinators => only possible candidate
if (importElement.getCombinators().length == 0) {
return importElement;
}
// OK, we have candidate
if (candidates == null) {
candidates = Lists.newArrayList();
}
candidates.add(importElement);
}
// no candidates, probably element is defined in this library
if (candidates == null) {
return null;
}
// one candidate
if (candidates.size() == 1) {
return candidates.get(0);
}
// ensure that each ImportElement has set of elements
for (ImportElement importElement : candidates) {
if (importElementsMap.containsKey(importElement)) {
continue;
}
Namespace namespace = new NamespaceBuilder().createImportNamespaceForDirective(importElement);
Set<Element> elements = Sets.newHashSet(namespace.getDefinedNames().values());
importElementsMap.put(importElement, elements);
}
// use import namespace to choose correct one
for (Entry<ImportElement, Set<Element>> entry : importElementsMap.entrySet()) {
if (entry.getValue().contains(usedElement)) {
return entry.getKey();
}
}
// not found
return null;
}
/**
* @return {@code true} if given "node" is part of an import {@link Combinator}.
*/
private static boolean isIdentifierInImportCombinator(SimpleIdentifier node) {
AstNode parent = node.getParent();
return parent instanceof Combinator;
}
/**
* @return {@code true} if given "node" is part of {@link PrefixedIdentifier} "prefix.node".
*/
private static boolean isIdentifierInPrefixedIdentifier(SimpleIdentifier node) {
AstNode parent = node.getParent();
return parent instanceof PrefixedIdentifier
&& ((PrefixedIdentifier) parent).getIdentifier() == node;
}
private final IndexStore store;
private LibraryElement libraryElement;
private final Map<ImportElement, Set<Element>> importElementsMap = Maps.newHashMap();
/**
* 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();
public IndexContributor(IndexStore store) {
this.store = store;
}
/**
* Enter a new scope represented by the given {@link Element}.
*/
public void enterScope(Element element) {
elementStack.addFirst(element);
}
/**
* @return the inner-most enclosing {@link Element}, may be {@code null}.
*/
@VisibleForTesting
public Element peekElement() {
for (Element element : elementStack) {
if (element != null) {
return element;
}
}
return null;
}
@Override
public Void visitAssignmentExpression(AssignmentExpression node) {
recordOperatorReference(node.getOperator(), node.getBestElement());
return super.visitAssignmentExpression(node);
}
@Override
public Void visitBinaryExpression(BinaryExpression node) {
recordOperatorReference(node.getOperator(), node.getBestElement());
return super.visitBinaryExpression(node);
}
@Override
public Void visitClassDeclaration(ClassDeclaration node) {
ClassElement element = node.getElement();
enterScope(element);
try {
recordElementDefinition(element, IndexConstants.DEFINES_CLASS);
{
ExtendsClause extendsClause = node.getExtendsClause();
if (extendsClause != null) {
TypeName superclassNode = extendsClause.getSuperclass();
recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY);
} else {
InterfaceType superType = element.getSupertype();
if (superType != null) {
ClassElement objectElement = superType.getElement();
recordRelationship(
objectElement,
IndexConstants.IS_EXTENDED_BY,
createLocationFromOffset(node.getName().getOffset(), 0));
}
}
}
{
WithClause withClause = node.getWithClause();
if (withClause != null) {
for (TypeName mixinNode : withClause.getMixinTypes()) {
recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY);
}
}
}
{
ImplementsClause implementsClause = node.getImplementsClause();
if (implementsClause != null) {
for (TypeName interfaceNode : implementsClause.getInterfaces()) {
recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY);
}
}
}
return super.visitClassDeclaration(node);
} finally {
exitScope();
}
}
@Override
public Void visitClassTypeAlias(ClassTypeAlias node) {
ClassElement element = node.getElement();
enterScope(element);
try {
recordElementDefinition(element, IndexConstants.DEFINES_CLASS_ALIAS);
{
TypeName superclassNode = node.getSuperclass();
if (superclassNode != null) {
recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY);
}
}
{
WithClause withClause = node.getWithClause();
if (withClause != null) {
for (TypeName mixinNode : withClause.getMixinTypes()) {
recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY);
}
}
}
{
ImplementsClause implementsClause = node.getImplementsClause();
if (implementsClause != null) {
for (TypeName interfaceNode : implementsClause.getInterfaces()) {
recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY);
}
}
}
return super.visitClassTypeAlias(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 visitConstructorDeclaration(ConstructorDeclaration node) {
ConstructorElement element = node.getElement();
// define
{
Location location;
if (node.getName() != null) {
int start = node.getPeriod().getOffset();
int end = node.getName().getEnd();
location = createLocationFromOffset(start, end - start);
} else {
int start = node.getReturnType().getEnd();
location = createLocationFromOffset(start, 0);
}
recordRelationship(element, IndexConstants.IS_DEFINED_BY, location);
}
// visit children
enterScope(element);
try {
return super.visitConstructorDeclaration(node);
} finally {
exitScope();
}
}
@Override
public Void visitConstructorName(ConstructorName node) {
ConstructorElement element = node.getStaticElement();
// in 'class B = A;' actually A constructors are invoked
if (element != null && element.isSynthetic() && element.getRedirectedConstructor() != null) {
element = element.getRedirectedConstructor();
}
// prepare location
Location location;
if (node.getName() != null) {
int start = node.getPeriod().getOffset();
int end = node.getName().getEnd();
location = createLocationFromOffset(start, end - start);
} else {
int start = node.getType().getEnd();
location = createLocationFromOffset(start, 0);
}
// record relationship
recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
return super.visitConstructorName(node);
}
@Override
public Void visitExportDirective(ExportDirective node) {
ExportElement element = node.getElement();
if (element != null) {
LibraryElement expLibrary = element.getExportedLibrary();
recordLibraryReference(node, expLibrary);
}
return super.visitExportDirective(node);
}
@Override
public Void visitFormalParameter(FormalParameter node) {
ParameterElement element = node.getElement();
enterScope(element);
try {
return super.visitFormalParameter(node);
} finally {
exitScope();
}
}
@Override
public Void visitFunctionDeclaration(FunctionDeclaration node) {
Element element = node.getElement();
recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION);
enterScope(element);
try {
return super.visitFunctionDeclaration(node);
} finally {
exitScope();
}
}
@Override
public Void visitFunctionTypeAlias(FunctionTypeAlias node) {
Element element = node.getElement();
recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION_TYPE);
return super.visitFunctionTypeAlias(node);
}
@Override
public Void visitImportDirective(ImportDirective node) {
ImportElement element = node.getElement();
if (element != null) {
LibraryElement impLibrary = element.getImportedLibrary();
recordLibraryReference(node, impLibrary);
}
return super.visitImportDirective(node);
}
@Override
public Void visitIndexExpression(IndexExpression node) {
MethodElement element = node.getBestElement();
if (element instanceof MethodElement) {
Token operator = node.getLeftBracket();
Location location = createLocationFromToken(operator);
recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED, location);
}
return super.visitIndexExpression(node);
}
@Override
public Void visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement element = node.getElement();
enterScope(element);
try {
return super.visitMethodDeclaration(node);
} finally {
exitScope();
}
}
@Override
public Void visitMethodInvocation(MethodInvocation node) {
SimpleIdentifier name = node.getMethodName();
Element element = name.getBestElement();
if (element instanceof MethodElement || element instanceof PropertyAccessorElement) {
Location location = createLocationFromNode(name);
Relationship relationship;
if (node.getTarget() != null) {
relationship = IndexConstants.IS_INVOKED_BY_QUALIFIED;
} else {
relationship = IndexConstants.IS_INVOKED_BY_UNQUALIFIED;
}
recordRelationship(element, relationship, location);
}
if (element instanceof FunctionElement || element instanceof VariableElement) {
Location location = createLocationFromNode(name);
recordRelationship(element, IndexConstants.IS_INVOKED_BY, location);
}
// name invocation
{
Element nameElement = new NameElementImpl(name.getName());
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
public Void visitPartDirective(PartDirective node) {
Element element = node.getElement();
Location location = createLocationFromNode(node.getUri());
recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
return super.visitPartDirective(node);
}
@Override
public Void visitPartOfDirective(PartOfDirective node) {
Location location = createLocationFromNode(node.getLibraryName());
recordRelationship(node.getElement(), IndexConstants.IS_REFERENCED_BY, location);
return null;
}
@Override
public Void visitPostfixExpression(PostfixExpression node) {
recordOperatorReference(node.getOperator(), node.getBestElement());
return super.visitPostfixExpression(node);
}
@Override
public Void visitPrefixExpression(PrefixExpression node) {
recordOperatorReference(node.getOperator(), node.getBestElement());
return super.visitPrefixExpression(node);
}
@Override
public Void visitSimpleIdentifier(SimpleIdentifier node) {
Element nameElement = new NameElementImpl(node.getName());
Location location = createLocationFromNode(node);
// name in declaration
if (node.inDeclarationContext()) {
recordRelationship(nameElement, IndexConstants.IS_DEFINED_BY, location);
return null;
}
// prepare information
Element element = node.getBestElement();
// qualified name reference
recordQualifiedMemberReference(node, element, nameElement, location);
// stop if already handled
if (isAlreadyHandledName(node)) {
return null;
}
// record name read/write
{
boolean inGetterContext = node.inGetterContext();
boolean 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 instanceof ClassElement || element instanceof FunctionElement
|| element instanceof FunctionTypeAliasElement || element instanceof LabelElement
|| element instanceof TypeParameterElement) {
recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
} else if (element instanceof FieldElement) {
location = getLocationWithInitializerType(node, location);
recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
} else if (element instanceof FieldFormalParameterElement) {
FieldFormalParameterElement fieldParameter = (FieldFormalParameterElement) element;
FieldElement field = fieldParameter.getField();
recordRelationship(field, IndexConstants.IS_REFERENCED_BY_QUALIFIED, location);
} else if (element instanceof PrefixElement) {
recordImportElementReferenceWithPrefix(node);
} else if (element instanceof PropertyAccessorElement || element instanceof 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 instanceof ParameterElement || element instanceof LocalVariableElement) {
boolean inGetterContext = node.inGetterContext();
boolean 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
public Void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
ConstructorElement element = node.getStaticElement();
Location location;
if (node.getConstructorName() != null) {
int start = node.getPeriod().getOffset();
int end = node.getConstructorName().getEnd();
location = createLocationFromOffset(start, end - start);
} else {
int start = node.getKeyword().getEnd();
location = createLocationFromOffset(start, 0);
}
recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location);
return super.visitSuperConstructorInvocation(node);
}
@Override
public Void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
VariableDeclarationList variables = node.getVariables();
for (VariableDeclaration variableDeclaration : variables.getVariables()) {
Element element = variableDeclaration.getElement();
recordElementDefinition(element, IndexConstants.DEFINES_VARIABLE);
}
return super.visitTopLevelVariableDeclaration(node);
}
@Override
public Void visitTypeParameter(TypeParameter node) {
TypeParameterElement element = node.getElement();
enterScope(element);
try {
return super.visitTypeParameter(node);
} finally {
exitScope();
}
}
@Override
public Void visitVariableDeclaration(VariableDeclaration node) {
VariableElement element = node.getElement();
// record declaration
{
SimpleIdentifier name = node.getName();
Location location = createLocationFromNode(name);
location = getLocationWithExpressionType(location, node.getInitializer());
recordRelationship(element, IndexConstants.IS_DEFINED_BY, location);
}
// visit
enterScope(element);
try {
return super.visitVariableDeclaration(node);
} finally {
exitScope();
}
}
@Override
public Void visitVariableDeclarationList(VariableDeclarationList node) {
NodeList<VariableDeclaration> variables = node.getVariables();
if (variables != null) {
// use first VariableDeclaration as Element for Location(s) in type
{
TypeName type = node.getType();
if (type != null) {
for (VariableDeclaration variableDeclaration : variables) {
enterScope(variableDeclaration.getElement());
try {
type.accept(this);
} finally {
exitScope();
}
// only one iteration
break;
}
}
}
// visit variables
variables.accept(this);
}
return null;
}
/**
* Record the given relationship between the given {@link Element} and {@link Location}.
*/
protected void recordRelationship(Element element, Relationship relationship, Location location) {
if (element != null && location != null) {
store.recordRelationship(element, relationship, location);
}
}
/**
* @return the {@link Location} representing location of the {@link AstNode}.
*/
private Location createLocationFromNode(AstNode node) {
return createLocationFromOffset(node.getOffset(), node.getLength());
}
/**
* @param offset the offset of the location within {@link Source}
* @param length the length of the location
* @return the {@link Location} representing the given offset and length within the inner-most
* {@link Element}.
*/
private Location createLocationFromOffset(int offset, int length) {
Element element = peekElement();
return new Location(element, offset, length);
}
/**
* @return the {@link Location} representing location of the {@link Token}.
*/
private Location createLocationFromToken(Token token) {
return createLocationFromOffset(token.getOffset(), token.getLength());
}
/**
* Exit the current scope.
*/
private void exitScope() {
elementStack.removeFirst();
}
/**
* @return {@code true} if given node already indexed as more interesting reference, so it should
* not be indexed again.
*/
private boolean isAlreadyHandledName(SimpleIdentifier node) {
AstNode parent = node.getParent();
if (parent instanceof MethodInvocation) {
return ((MethodInvocation) parent).getMethodName() == node;
}
return false;
}
/**
* Records the {@link Element} definition in the library and universe.
*/
private void recordElementDefinition(Element element, Relationship relationship) {
Location location = createLocation(element);
recordRelationship(libraryElement, relationship, location);
recordRelationship(IndexConstants.UNIVERSE, relationship, location);
}
/**
* Records {@link ImportElement} reference if given {@link SimpleIdentifier} references some
* top-level element and not qualified with import prefix.
*/
private void recordImportElementReferenceWithoutPrefix(SimpleIdentifier node) {
if (isIdentifierInImportCombinator(node)) {
return;
}
if (isIdentifierInPrefixedIdentifier(node)) {
return;
}
Element element = node.getStaticElement();
ImportElement importElement = internalGetImportElement(
libraryElement,
null,
element,
importElementsMap);
if (importElement != null) {
Location location = createLocationFromOffset(node.getOffset(), 0);
recordRelationship(importElement, IndexConstants.IS_REFERENCED_BY, location);
}
}
/**
* Records {@link ImportElement} that declares given prefix and imports library with element used
* with given prefix node.
*/
private void recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) {
ImportElementInfo info = getImportElementInfo(prefixNode);
if (info != null) {
int offset = prefixNode.getOffset();
int length = info.periodEnd - offset;
Location location = createLocationFromOffset(offset, length);
recordRelationship(info.element, IndexConstants.IS_REFERENCED_BY, location);
}
}
/**
* Records reference to defining {@link CompilationUnitElement} of the given
* {@link LibraryElement}.
*/
private void recordLibraryReference(UriBasedDirective node, LibraryElement library) {
if (library != null) {
Location location = createLocationFromNode(node.getUri());
recordRelationship(
library.getDefiningCompilationUnit(),
IndexConstants.IS_REFERENCED_BY,
location);
}
}
/**
* Record reference to the given operator {@link Element} and name.
*/
private void recordOperatorReference(Token operator, Element element) {
// prepare location
Location location = createLocationFromToken(operator);
// record name reference
{
String name = operator.getLexeme();
if (name.equals("++")) {
name = "+";
}
if (name.equals("--")) {
name = "-";
}
if (StringUtilities.endsWithChar(name, '=') && !name.equals("==")) {
name = name.substring(0, name.length() - 1);
}
Element nameElement = new NameElementImpl(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 {@link SimpleIdentifier} looks like a qualified property access
* or method invocation.
*/
private 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 {@link ClassElement} and {@link Type} of
* "superNode".
*/
private void recordSuperType(TypeName superNode, Relationship relationship) {
if (superNode != null) {
Identifier superName = superNode.getName();
if (superName != null) {
Element superElement = superName.getStaticElement();
recordRelationship(superElement, relationship, createLocationFromNode(superNode));
}
}
}
}