blob: 3a1c97e863ed2b34498326a136ece897d693f9f5 [file] [log] [blame]
// 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 services.src.index.index_contributor;
import 'dart:collection' show Queue;
import 'package:analysis_services/index/index.dart';
import 'package:analysis_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_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) {
// 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_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.
*
* [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);
}
}
}
}
/**
* Information about [ImportElement] and place where it is referenced using
* [PrefixElement].
*/
class _ImportElementInfo {
ImportElement _element;
int _periodEnd = 0;
}
/**
* 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, 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;
// TODO(scheglov) fix resolver to resolve to Field, not an accessor
if (node.parent is Combinator && element is PropertyAccessorElement) {
element = (element as PropertyAccessorElement).variable;
}
// 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 PropertyInducingElement) {
location = _getLocationWithInitializerType(node, location);
recordRelationship(element,
IndexConstants.IS_REFERENCED_BY_QUALIFIED,
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);
/**
* [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 _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.
*
* [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;
}
/**
* 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].
*
* [identifier] - the identifier to record location.
* [element] - the [Element] of the identifier.
* [location] - the raw location
*
* Returns the [Location] with the type of the assigned value
*/
static Location
_getLocationWithTypeAssignedToField(SimpleIdentifier identifier,
Element element, Location location) {
// TODO(scheglov) decide if we want to remember assigned types
// 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.from(namespace.definedNames.values);
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);
}
}