blob: b5b5b101d79b91fad82ba80d161d08f6ebd526e6 [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 engine.index;
import 'dart:collection' show Queue;
import 'java_core.dart';
import 'java_engine.dart';
import 'source.dart';
import 'scanner.dart' show Token;
import 'ast.dart';
import 'element.dart';
import 'resolver.dart' show Namespace, NamespaceBuilder;
import 'engine.dart';
import 'html.dart' as ht;
/**
* 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 IndexContributor_AngularHtmlIndexContributor(_store, this);
}
@override
void visitExpression(Expression expression) {
// Formatter
if (expression is SimpleIdentifier) {
SimpleIdentifier identifier = expression;
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);
}
/**
* Instances of the [ClearOperation] implement an operation that removes all of the
* information from the index.
*/
class ClearOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
ClearOperation(this._indexStore);
@override
bool get isQuery => false;
@override
void performOperation() {
_indexStore.clear();
}
@override
bool removeWhenSourceRemoved(Source source) => false;
@override
String toString() => "ClearOperation()";
}
/**
* Recursively visits [HtmlUnit] and every embedded [Expression].
*/
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);
}
}
}
}
/**
* Instances of the [GetRelationshipsOperation] implement an operation used to access the
* locations that have a specified relationship with a specified element.
*/
class GetRelationshipsOperation implements IndexOperation {
final IndexStore _indexStore;
final Element element;
final Relationship relationship;
final RelationshipCallback callback;
/**
* Initialize a newly created operation that will access the locations that have a specified
* relationship with a specified element.
*/
GetRelationshipsOperation(this._indexStore, this.element, this.relationship, this.callback);
@override
bool get isQuery => true;
@override
void performOperation() {
List<Location> locations;
locations = _indexStore.getRelationships(element, relationship);
callback.hasRelationships(element, relationship, locations);
}
@override
bool removeWhenSourceRemoved(Source source) => false;
@override
String toString() => "GetRelationships(${element}, ${relationship})";
}
/**
* The interface [Index] defines the behavior of objects that maintain an index storing
* [Relationship] between [Element]. All of the operations
* defined on the index are asynchronous, and results, when there are any, are provided through a
* callback.
*
* Despite being asynchronous, the results of the operations are guaranteed to be consistent with
* the expectation that operations are performed in the order in which they are requested.
* Modification operations are executed before any read operation. There is no guarantee about the
* order in which the callbacks for read operations will be invoked.
*/
abstract class Index {
/**
* Asynchronously remove from the index all of the information.
*/
void clear();
/**
* Asynchronously invoke the given callback with an array containing all of the locations of the
* elements that have the given relationship with the given element. For example, if the element
* represents a method and the relationship is the is-referenced-by relationship, then the
* locations that will be passed into the callback will be all of the places where the method is
* invoked.
*
* @param element the element that has the relationship with the locations to be returned
* @param relationship the relationship between the given element and the locations to be returned
* @param callback the callback that will be invoked when the locations are found
*/
void getRelationships(Element element, Relationship relationship, RelationshipCallback callback);
/**
* Answer index statistics.
*/
String get statistics;
/**
* Asynchronously process the given [HtmlUnit] in order to record the relationships.
*
* @param context the [AnalysisContext] in which [HtmlUnit] was resolved
* @param unit the [HtmlUnit] being indexed
*/
void indexHtmlUnit(AnalysisContext context, ht.HtmlUnit unit);
/**
* Asynchronously process the given [CompilationUnit] in order to record the relationships.
*
* @param context the [AnalysisContext] in which [CompilationUnit] was resolved
* @param unit the [CompilationUnit] being indexed
*/
void indexUnit(AnalysisContext context, CompilationUnit unit);
/**
* Asynchronously remove from the index all of the information associated with the given context.
*
* This method should be invoked when a context is disposed.
*
* @param context the [AnalysisContext] to remove
*/
void removeContext(AnalysisContext context);
/**
* Asynchronously remove from the index all of the information associated with elements or
* locations in the given source. This includes relationships between an element in the given
* source and any other locations, relationships between any other elements and a location within
* the given source.
*
* This method should be invoked when a source is no longer part of the code base.
*
* @param context the [AnalysisContext] in which [Source] being removed
* @param source the [Source] being removed
*/
void removeSource(AnalysisContext context, Source source);
/**
* Asynchronously remove from the index all of the information associated with elements or
* locations in the given sources. This includes relationships between an element in the given
* sources and any other locations, relationships between any other elements and a location within
* the given sources.
*
* This method should be invoked when multiple sources are no longer part of the code base.
*
* @param the [AnalysisContext] in which [Source]s being removed
* @param container the [SourceContainer] holding the sources being removed
*/
void removeSources(AnalysisContext context, SourceContainer container);
/**
* Should be called in separate [Thread] to process request in this [Index]. Does not
* return until the [stop] method is called.
*/
void run();
/**
* Should be called to stop process running [run], so stop processing requests.
*/
void stop();
}
/**
* Constants used when populating and accessing the index.
*/
abstract class IndexConstants {
/**
* An element used to represent the universe.
*/
static final Element UNIVERSE = UniverseElement.INSTANCE;
/**
* The relationship used to indicate that a container (the left-operand) contains the definition
* of a class at a specific location (the right operand).
*/
static final Relationship DEFINES_CLASS = Relationship.getRelationship("defines-class");
/**
* The relationship used to indicate that a container (the left-operand) contains the definition
* of a function at a specific location (the right operand).
*/
static final Relationship DEFINES_FUNCTION = Relationship.getRelationship("defines-function");
/**
* The relationship used to indicate that a container (the left-operand) contains the definition
* of a class type alias at a specific location (the right operand).
*/
static final Relationship DEFINES_CLASS_ALIAS = Relationship.getRelationship("defines-class-alias");
/**
* The relationship used to indicate that a container (the left-operand) contains the definition
* of a function type at a specific location (the right operand).
*/
static final Relationship DEFINES_FUNCTION_TYPE = Relationship.getRelationship("defines-function-type");
/**
* The relationship used to indicate that a container (the left-operand) contains the definition
* of a method at a specific location (the right operand).
*/
static final Relationship DEFINES_VARIABLE = Relationship.getRelationship("defines-variable");
/**
* The relationship used to indicate that a name (the left-operand) is defined at a specific
* location (the right operand).
*/
static final Relationship IS_DEFINED_BY = Relationship.getRelationship("is-defined-by");
/**
* The relationship used to indicate that a type (the left-operand) is extended by a type at a
* specific location (the right operand).
*/
static final Relationship IS_EXTENDED_BY = Relationship.getRelationship("is-extended-by");
/**
* The relationship used to indicate that a type (the left-operand) is implemented by a type at a
* specific location (the right operand).
*/
static final Relationship IS_IMPLEMENTED_BY = Relationship.getRelationship("is-implemented-by");
/**
* The relationship used to indicate that a type (the left-operand) is mixed into a type at a
* specific location (the right operand).
*/
static final Relationship IS_MIXED_IN_BY = Relationship.getRelationship("is-mixed-in-by");
/**
* The relationship used to indicate that a parameter or variable (the left-operand) is read at a
* specific location (the right operand).
*/
static final Relationship IS_READ_BY = Relationship.getRelationship("is-read-by");
/**
* The relationship used to indicate that a parameter or variable (the left-operand) is both read
* and modified at a specific location (the right operand).
*/
static final Relationship IS_READ_WRITTEN_BY = Relationship.getRelationship("is-read-written-by");
/**
* The relationship used to indicate that a parameter or variable (the left-operand) is modified
* (assigned to) at a specific location (the right operand).
*/
static final Relationship IS_WRITTEN_BY = Relationship.getRelationship("is-written-by");
/**
* The relationship used to indicate that an element (the left-operand) is referenced at a
* specific location (the right operand). This is used for everything except read/write operations
* for fields, parameters, and variables. Those use either [IS_REFERENCED_BY_QUALIFIED],
* [IS_REFERENCED_BY_UNQUALIFIED], [IS_READ_BY], [IS_WRITTEN_BY] or
* [IS_READ_WRITTEN_BY], as appropriate.
*/
static final Relationship IS_REFERENCED_BY = Relationship.getRelationship("is-referenced-by");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is
* referenced at a specific location (the right operand). This is used for qualified resolved
* references to methods and fields.
*/
static final Relationship IS_REFERENCED_BY_QUALIFIED_RESOLVED = Relationship.getRelationship("is-referenced-by-qualified-resolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is
* referenced at a specific location (the right operand). This is used for qualified unresolved
* references to methods and fields.
*/
static final Relationship IS_REFERENCED_BY_QUALIFIED_UNRESOLVED = Relationship.getRelationship("is-referenced-by-qualified-unresolved");
/**
* The relationship used to indicate that an element (the left-operand) is referenced at a
* specific location (the right operand). This is used for field accessors and methods.
*/
static final Relationship IS_REFERENCED_BY_QUALIFIED = Relationship.getRelationship("is-referenced-by-qualified");
/**
* The relationship used to indicate that an element (the left-operand) is referenced at a
* specific location (the right operand). This is used for field accessors and methods.
*/
static final Relationship IS_REFERENCED_BY_UNQUALIFIED = Relationship.getRelationship("is-referenced-by-unqualified");
/**
* The relationship used to indicate that an element (the left-operand) is invoked at a specific
* location (the right operand). This is used for functions.
*/
static final Relationship IS_INVOKED_BY = Relationship.getRelationship("is-invoked-by");
/**
* The relationship used to indicate that an element (the left-operand) is invoked at a specific
* location (the right operand). This is used for methods.
*/
static final Relationship IS_INVOKED_BY_QUALIFIED = Relationship.getRelationship("is-invoked-by-qualified");
/**
* The relationship used to indicate that an element (the left-operand) is invoked at a specific
* location (the right operand). This is used for methods.
*/
static final Relationship IS_INVOKED_BY_UNQUALIFIED = Relationship.getRelationship("is-invoked-by-unqualified");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is invoked
* at a specific location (the right operand). This is used for resolved invocations.
*/
static final Relationship NAME_IS_INVOKED_BY_RESOLVED = Relationship.getRelationship("name-is-invoked-by-resolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is read at
* a specific location (the right operand).
*/
static final Relationship NAME_IS_READ_BY_RESOLVED = Relationship.getRelationship("name-is-read-by-resolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is both
* read and written at a specific location (the right operand).
*/
static final Relationship NAME_IS_READ_WRITTEN_BY_RESOLVED = Relationship.getRelationship("name-is-read-written-by-resolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is written
* at a specific location (the right operand).
*/
static final Relationship NAME_IS_WRITTEN_BY_RESOLVED = Relationship.getRelationship("name-is-written-by-resolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is invoked
* at a specific location (the right operand). This is used for unresolved invocations.
*/
static final Relationship NAME_IS_INVOKED_BY_UNRESOLVED = Relationship.getRelationship("name-is-invoked-by-unresolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is read at
* a specific location (the right operand).
*/
static final Relationship NAME_IS_READ_BY_UNRESOLVED = Relationship.getRelationship("name-is-read-by-unresolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is both
* read and written at a specific location (the right operand).
*/
static final Relationship NAME_IS_READ_WRITTEN_BY_UNRESOLVED = Relationship.getRelationship("name-is-read-written-by-unresolved");
/**
* The relationship used to indicate that an [NameElementImpl] (the left-operand) is written
* at a specific location (the right operand).
*/
static final Relationship NAME_IS_WRITTEN_BY_UNRESOLVED = Relationship.getRelationship("name-is-written-by-unresolved");
/**
* Reference to some [AngularElement].
*/
static final Relationship ANGULAR_REFERENCE = Relationship.getRelationship("angular-reference");
/**
* Reference to some closing tag of an XML element.
*/
static final Relationship ANGULAR_CLOSING_TAG_REFERENCE = Relationship.getRelationship("angular-closing-tag-reference");
}
/**
* Visits resolved AST and adds relationships into [IndexStore].
*/
class IndexContributor extends GeneralizingAstVisitor<Object> {
/**
* @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) {
IndexContributor_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 IndexContributor_ImportElementInfo getImportElementInfo(SimpleIdentifier prefixNode) {
IndexContributor_ImportElementInfo info = new IndexContributor_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>.con1(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);
}
/**
* @return `true` if given [SimpleIdentifier] is "name" part of prefixed identifier or
* method invocation.
*/
static bool _isQualified(SimpleIdentifier node) {
AstNode parent = node.parent;
if (parent is PrefixedIdentifier) {
return identical(parent.identifier, node);
}
if (parent is PropertyAccess) {
return identical(parent.propertyName, node);
}
if (parent is MethodInvocation) {
MethodInvocation invocation = parent;
return invocation.realTarget != null && identical(invocation.methodName, node);
}
return false;
}
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;
}
@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) {
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 NameElementImpl(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 NameElementImpl(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 (_isQualified(node)) {
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;
}
/**
* 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);
}
}
/**
* @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] 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 [ImportElement] that declares given prefix and imports library with element used
* with given prefix node.
*/
void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) {
IndexContributor_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 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 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 [SimpleIdentifier] looks like a qualified property access
* or method invocation.
*/
void _recordQualifiedMemberReference(SimpleIdentifier node, Element element, Element nameElement, Location location) {
if (_isQualified(node)) {
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));
}
}
}
}
class IndexContributor_AngularHtmlIndexContributor extends IndexContributor {
final AngularHtmlIndexContributor AngularHtmlIndexContributor_this;
IndexContributor_AngularHtmlIndexContributor(IndexStore arg0, this.AngularHtmlIndexContributor_this) : super(arg0);
@override
Element peekElement() => AngularHtmlIndexContributor_this._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);
}
}
/**
* Information about [ImportElement] and place where it is referenced using
* [PrefixElement].
*/
class IndexContributor_ImportElementInfo {
ImportElement _element;
int _periodEnd = 0;
}
/**
* Instances of the [IndexHtmlUnitOperation] implement an operation that adds data to the
* index based on the resolved [HtmlUnit].
*/
class IndexHtmlUnitOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
/**
* The context in which [HtmlUnit] was resolved.
*/
final AnalysisContext _context;
/**
* The [HtmlUnit] being indexed.
*/
final ht.HtmlUnit unit;
/**
* The element of the [HtmlUnit] being indexed.
*/
HtmlElement _htmlElement;
/**
* The source being indexed.
*/
Source _source;
/**
* Initialize a newly created operation that will index the specified [HtmlUnit].
*
* @param indexStore the index store against which this operation is being run
* @param context the context in which [HtmlUnit] was resolved
* @param unit the fully resolved [HtmlUnit]
*/
IndexHtmlUnitOperation(this._indexStore, this._context, this.unit) {
this._htmlElement = unit.element;
this._source = _htmlElement.source;
}
/**
* @return the [Source] to be indexed.
*/
Source get source => _source;
@override
bool get isQuery => false;
@override
void performOperation() {
try {
bool mayIndex = _indexStore.aboutToIndexHtml(_context, _htmlElement);
if (!mayIndex) {
return;
}
AngularHtmlIndexContributor contributor = new AngularHtmlIndexContributor(_indexStore);
unit.accept(contributor);
} catch (exception) {
AnalysisEngine.instance.logger.logError2("Could not index ${unit.element.location}", exception);
}
}
@override
bool removeWhenSourceRemoved(Source source) => this._source == source;
@override
String toString() => "IndexHtmlUnitOperation(${_source.fullName})";
}
/**
* The interface [IndexOperation] defines the behavior of objects used to perform operations
* on an index.
*/
abstract class IndexOperation {
/**
* Return `true` if this operation returns information from the index.
*
* @return `true` if this operation returns information from the index
*/
bool get isQuery;
/**
* Perform the operation implemented by this operation.
*/
void performOperation();
/**
* Return `true` if this operation should be removed from the operation queue when the
* given resource has been removed.
*
* @param source the [Source] that has been removed
* @return `true` if this operation should be removed from the operation queue as a
* result of removing the resource
*/
bool removeWhenSourceRemoved(Source source);
}
/**
* Container of information computed by the index - relationships between elements.
*/
abstract class IndexStore {
/**
* Notifies the index store that we are going to index the unit with the given element.
*
* If the unit is a part of a library, then all its locations are removed. If it is a defining
* compilation unit of a library, then index store also checks if some previously indexed parts of
* the library are not parts of the library anymore, and clears their information.
*
* @param the [AnalysisContext] in which unit being indexed
* @param unitElement the element of the unit being indexed
* @return `true` the given [AnalysisContext] is active, or `false` if it was
* removed before, so no any unit may be indexed with it
*/
bool aboutToIndexDart(AnalysisContext context, CompilationUnitElement unitElement);
/**
* Notifies the index store that we are going to index the given [HtmlElement].
*
* @param the [AnalysisContext] in which unit being indexed
* @param htmlElement the [HtmlElement] being indexed
* @return `true` the given [AnalysisContext] is active, or `false` if it was
* removed before, so no any unit may be indexed with it
*/
bool aboutToIndexHtml(AnalysisContext context, HtmlElement htmlElement);
/**
* Removes all of the information.
*/
void clear();
/**
* Return the locations of the elements that have the given relationship with the given element.
* For example, if the element represents a method and the relationship is the is-referenced-by
* relationship, then the returned locations will be all of the places where the method is
* invoked.
*
* @param element the the element that has the relationship with the locations to be returned
* @param relationship the [Relationship] between the given element and the locations to be
* returned
* @return the locations that have the given relationship with the given element
*/
List<Location> getRelationships(Element element, Relationship relationship);
/**
* Answer index statistics.
*/
String get statistics;
/**
* Record that the given element and location have the given relationship. For example, if the
* relationship is the is-referenced-by relationship, then the element would be the element being
* referenced and the location would be the point at which it is referenced. Each element can have
* the same relationship with multiple locations. In other words, if the following code were
* executed
*
* <pre>
* recordRelationship(element, isReferencedBy, location1);
* recordRelationship(element, isReferencedBy, location2);
* </pre>
*
* then both relationships would be maintained in the index and the result of executing
*
* <pre>
* getRelationship(element, isReferencedBy);
* </pre>
*
* would be an array containing both <code>location1</code> and <code>location2</code>.
*
* @param element the element that is related to the location
* @param relationship the [Relationship] between the element and the location
* @param location the [Location] where relationship happens
*/
void recordRelationship(Element element, Relationship relationship, Location location);
/**
* Remove from the index all of the information associated with [AnalysisContext].
*
* This method should be invoked when a context is disposed.
*
* @param the [AnalysisContext] being removed
*/
void removeContext(AnalysisContext context);
/**
* Remove from the index all of the information associated with elements or locations in the given
* source. This includes relationships between an element in the given source and any other
* locations, relationships between any other elements and a location within the given source.
*
* This method should be invoked when a source is no longer part of the code base.
*
* @param the [AnalysisContext] in which [Source] being removed
* @param source the source being removed
*/
void removeSource(AnalysisContext context, Source source);
/**
* Remove from the index all of the information associated with elements or locations in the given
* sources. This includes relationships between an element in the given sources and any other
* locations, relationships between any other elements and a location within the given sources.
*
* This method should be invoked when multiple sources are no longer part of the code base.
*
* @param the [AnalysisContext] in which [Source]s being removed
* @param container the [SourceContainer] holding the sources being removed
*/
void removeSources(AnalysisContext context, SourceContainer container);
}
/**
* Instances of the [IndexUnitOperation] implement an operation that adds data to the index
* based on the resolved [CompilationUnit].
*/
class IndexUnitOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
/**
* The context in which compilation unit was resolved.
*/
final AnalysisContext _context;
/**
* The compilation unit being indexed.
*/
final CompilationUnit unit;
/**
* The element of the compilation unit being indexed.
*/
CompilationUnitElement _unitElement;
/**
* The source being indexed.
*/
Source _source;
/**
* Initialize a newly created operation that will index the specified unit.
*
* @param indexStore the index store against which this operation is being run
* @param context the context in which compilation unit was resolved
* @param unit the fully resolved AST structure
*/
IndexUnitOperation(this._indexStore, this._context, this.unit) {
this._unitElement = unit.element;
this._source = _unitElement.source;
}
/**
* @return the [Source] to be indexed.
*/
Source get source => _source;
@override
bool get isQuery => false;
@override
void performOperation() {
try {
bool mayIndex = _indexStore.aboutToIndexDart(_context, _unitElement);
if (!mayIndex) {
return;
}
unit.accept(new IndexContributor(_indexStore));
unit.accept(new AngularDartIndexContributor(_indexStore));
} catch (exception) {
AnalysisEngine.instance.logger.logError2("Could not index ${unit.element.location}", exception);
}
}
@override
bool removeWhenSourceRemoved(Source source) => this._source == source;
@override
String toString() => "IndexUnitOperation(${_source.fullName})";
}
/**
* Instances of the class <code>Location</code> represent a location related to an element. The
* location is expressed as an offset and length, but the offset is relative to the resource
* containing the element rather than the start of the element within that resource.
*/
class Location {
/**
* An empty array of locations.
*/
static List<Location> EMPTY_ARRAY = new List<Location>(0);
/**
* The element containing this location.
*/
final Element element;
/**
* The offset of this location within the resource containing the element.
*/
final int offset;
/**
* The length of this location.
*/
final int length;
/**
* Internal field used to hold a key that is referenced at this location.
*/
Object internalKey;
/**
* Initialize a newly create location to be relative to the given element at the given offset with
* the given length.
*
* @param element the [Element] containing this location
* @param offset the offset of this location within the resource containing the element
* @param length the length of this location
*/
Location(this.element, this.offset, this.length) {
if (element == null) {
throw new IllegalArgumentException("element location cannot be null");
}
}
/**
* Returns a clone of this [Location].
*/
Location newClone() => new Location(element, offset, length);
@override
String toString() => "[${offset} - ${(offset + length)}) in ${element}";
}
/**
* [Location] with attached data.
*/
class LocationWithData<D> extends Location {
final D data;
LocationWithData.con1(Location location, this.data) : super(location.element, location.offset, location.length);
LocationWithData.con2(Element element, int offset, int length, this.data) : super(element, offset, length);
@override
Location newClone() => new LocationWithData<D>.con2(element, offset, length, data);
}
/**
* [IndexStore] which keeps all information in memory, but can write it to stream and read
* later.
*/
abstract class MemoryIndexStore implements IndexStore {
}
/**
* [IndexStore] which keeps full index in memory.
*/
class MemoryIndexStoreImpl implements MemoryIndexStore {
/**
* When logging is on, [AnalysisEngine] actually creates
* [InstrumentedAnalysisContextImpl], which wraps [AnalysisContextImpl] used to create
* actual [Element]s. So, in index we have to unwrap [InstrumentedAnalysisContextImpl]
* when perform any operation.
*/
static AnalysisContext unwrapContext(AnalysisContext context) {
if (context is InstrumentedAnalysisContextImpl) {
context = (context as InstrumentedAnalysisContextImpl).basis;
}
return context;
}
/**
* @return the [Source] of the enclosing [LibraryElement], may be `null`.
*/
static Source _getLibrarySourceOrNull(Element element) {
LibraryElement library = element.library;
if (library == null) {
return null;
}
if (library.isAngularHtml) {
return null;
}
return library.source;
}
/**
* This map is used to canonicalize equal keys.
*/
Map<MemoryIndexStoreImpl_ElementRelationKey, MemoryIndexStoreImpl_ElementRelationKey> _canonicalKeys = {};
/**
* The mapping of [ElementRelationKey] to the [Location]s, one-to-many.
*/
Map<MemoryIndexStoreImpl_ElementRelationKey, Set<Location>> _keyToLocations = {};
/**
* The mapping of [Source] to the [ElementRelationKey]s. It is used in
* [removeSource] to identify keys to remove from
* [keyToLocations].
*/
Map<AnalysisContext, Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>>> _contextToSourceToKeys = {};
/**
* The mapping of [Source] to the [Location]s existing in it. It is used in
* [clearSource0] to identify locations to remove from
* [keyToLocations].
*/
Map<AnalysisContext, Map<MemoryIndexStoreImpl_Source2, List<Location>>> _contextToSourceToLocations = {};
/**
* The mapping of library [Source] to the [Source]s of part units.
*/
Map<AnalysisContext, Map<Source, Set<Source>>> _contextToLibraryToUnits = {};
/**
* The mapping of unit [Source] to the [Source]s of libraries it is used in.
*/
Map<AnalysisContext, Map<Source, Set<Source>>> _contextToUnitToLibraries = {};
int _sourceCount = 0;
int _keyCount = 0;
int _locationCount = 0;
@override
bool aboutToIndexDart(AnalysisContext context, CompilationUnitElement unitElement) {
context = unwrapContext(context);
// may be already disposed in other thread
if (context.isDisposed) {
return false;
}
// validate unit
if (unitElement == null) {
return false;
}
LibraryElement libraryElement = unitElement.library;
if (libraryElement == null) {
return false;
}
CompilationUnitElement definingUnitElement = libraryElement.definingCompilationUnit;
if (definingUnitElement == null) {
return false;
}
// prepare sources
Source library = definingUnitElement.source;
Source unit = unitElement.source;
// special handling for the defining library unit
if (unit == library) {
// prepare new parts
Set<Source> newParts = new Set();
for (CompilationUnitElement part in libraryElement.parts) {
newParts.add(part.source);
}
// prepare old parts
Map<Source, Set<Source>> libraryToUnits = _contextToLibraryToUnits[context];
if (libraryToUnits == null) {
libraryToUnits = {};
_contextToLibraryToUnits[context] = libraryToUnits;
}
Set<Source> oldParts = libraryToUnits[library];
// check if some parts are not in the library now
if (oldParts != null) {
Set<Source> noParts = oldParts.difference(newParts);
for (Source noPart in noParts) {
_removeLocations(context, library, noPart);
}
}
// remember new parts
libraryToUnits[library] = newParts;
}
// remember libraries in which unit is used
_recordUnitInLibrary(context, library, unit);
// remove locations
_removeLocations(context, library, unit);
// remove keys
{
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[context];
if (sourceToKeys != null) {
MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(library, unit);
bool hadSource = sourceToKeys.remove(source2) != null;
if (hadSource) {
_sourceCount--;
}
}
}
// OK, we can index
return true;
}
@override
bool aboutToIndexHtml(AnalysisContext context, HtmlElement htmlElement) {
context = unwrapContext(context);
// may be already disposed in other thread
if (context.isDisposed) {
return false;
}
// remove locations
Source source = htmlElement.source;
_removeLocations(context, null, source);
// remove keys
{
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[context];
if (sourceToKeys != null) {
MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(null, source);
bool hadSource = sourceToKeys.remove(source2) != null;
if (hadSource) {
_sourceCount--;
}
}
}
// remember libraries in which unit is used
_recordUnitInLibrary(context, null, source);
// OK, we can index
return true;
}
@override
void clear() {
_canonicalKeys.clear();
_keyToLocations.clear();
_contextToSourceToKeys.clear();
_contextToSourceToLocations.clear();
_contextToLibraryToUnits.clear();
_contextToUnitToLibraries.clear();
}
@override
List<Location> getRelationships(Element element, Relationship relationship) {
MemoryIndexStoreImpl_ElementRelationKey key = new MemoryIndexStoreImpl_ElementRelationKey(element, relationship);
Set<Location> locations = _keyToLocations[key];
if (locations != null) {
return new List.from(locations);
}
return Location.EMPTY_ARRAY;
}
@override
String get statistics => "${_locationCount} relationships in ${_keyCount} keys in ${_sourceCount} sources";
int internalGetKeyCount() => _keyToLocations.length;
int internalGetLocationCount() {
int count = 0;
for (Set<Location> locations in _keyToLocations.values) {
count += locations.length;
}
return count;
}
int internalGetLocationCountForContext(AnalysisContext context) {
context = unwrapContext(context);
int count = 0;
for (Set<Location> locations in _keyToLocations.values) {
for (Location location in locations) {
if (identical(location.element.context, context)) {
count++;
}
}
}
return count;
}
int internalGetSourceKeyCount(AnalysisContext context) {
int count = 0;
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[context];
if (sourceToKeys != null) {
for (Set<MemoryIndexStoreImpl_ElementRelationKey> keys in sourceToKeys.values) {
count += keys.length;
}
}
return count;
}
@override
void recordRelationship(Element element, Relationship relationship, Location location) {
if (element == null || location == null) {
return;
}
location = location.newClone();
// at the index level we don't care about Member(s)
if (element is Member) {
element = (element as Member).baseElement;
}
// System.out.println(element + " " + relationship + " " + location);
// prepare information
AnalysisContext elementContext = element.context;
AnalysisContext locationContext = location.element.context;
Source elementSource = element.source;
Source locationSource = location.element.source;
Source elementLibrarySource = _getLibrarySourceOrNull(element);
Source locationLibrarySource = _getLibrarySourceOrNull(location.element);
// sanity check
if (locationContext == null) {
return;
}
if (locationSource == null) {
return;
}
if (elementContext == null && element is! NameElementImpl && element is! UniverseElementImpl) {
return;
}
if (elementSource == null && element is! NameElementImpl && element is! UniverseElementImpl) {
return;
}
// may be already disposed in other thread
if (elementContext != null && elementContext.isDisposed) {
return;
}
if (locationContext.isDisposed) {
return;
}
// record: key -> location(s)
MemoryIndexStoreImpl_ElementRelationKey key = _getCanonicalKey(element, relationship);
{
Set<Location> locations = _keyToLocations.remove(key);
if (locations == null) {
locations = _createLocationIdentitySet();
} else {
_keyCount--;
}
_keyToLocations[key] = locations;
_keyCount++;
locations.add(location);
_locationCount++;
}
// record: location -> key
location.internalKey = key;
// prepare source pairs
MemoryIndexStoreImpl_Source2 elementSource2 = new MemoryIndexStoreImpl_Source2(elementLibrarySource, elementSource);
MemoryIndexStoreImpl_Source2 locationSource2 = new MemoryIndexStoreImpl_Source2(locationLibrarySource, locationSource);
// record: element source -> keys
{
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[elementContext];
if (sourceToKeys == null) {
sourceToKeys = {};
_contextToSourceToKeys[elementContext] = sourceToKeys;
}
Set<MemoryIndexStoreImpl_ElementRelationKey> keys = sourceToKeys[elementSource2];
if (keys == null) {
keys = new Set();
sourceToKeys[elementSource2] = keys;
_sourceCount++;
}
keys.remove(key);
keys.add(key);
}
// record: location source -> locations
{
Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _contextToSourceToLocations[locationContext];
if (sourceToLocations == null) {
sourceToLocations = {};
_contextToSourceToLocations[locationContext] = sourceToLocations;
}
List<Location> locations = sourceToLocations[locationSource2];
if (locations == null) {
locations = [];
sourceToLocations[locationSource2] = locations;
}
locations.add(location);
}
}
@override
void removeContext(AnalysisContext context) {
context = unwrapContext(context);
if (context == null) {
return;
}
// remove sources
removeSources(context, null);
// remove context
_contextToSourceToKeys.remove(context);
_contextToSourceToLocations.remove(context);
_contextToLibraryToUnits.remove(context);
_contextToUnitToLibraries.remove(context);
}
@override
void removeSource(AnalysisContext context, Source unit) {
context = unwrapContext(context);
if (context == null) {
return;
}
// remove locations defined in source
Map<Source, Set<Source>> unitToLibraries = _contextToUnitToLibraries[context];
if (unitToLibraries != null) {
Set<Source> libraries = unitToLibraries.remove(unit);
if (libraries != null) {
for (Source library in libraries) {
MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(library, unit);
// remove locations defined in source
_removeLocations(context, library, unit);
// remove keys for elements defined in source
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[context];
if (sourceToKeys != null) {
Set<MemoryIndexStoreImpl_ElementRelationKey> keys = sourceToKeys.remove(source2);
if (keys != null) {
for (MemoryIndexStoreImpl_ElementRelationKey key in keys) {
_canonicalKeys.remove(key);
Set<Location> locations = _keyToLocations.remove(key);
if (locations != null) {
_keyCount--;
_locationCount -= locations.length;
}
}
_sourceCount--;
}
}
}
}
}
}
@override
void removeSources(AnalysisContext context, SourceContainer container) {
context = unwrapContext(context);
if (context == null) {
return;
}
// remove sources #1
Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKey>> sourceToKeys = _contextToSourceToKeys[context];
if (sourceToKeys != null) {
List<MemoryIndexStoreImpl_Source2> sources = [];
for (MemoryIndexStoreImpl_Source2 source2 in sources) {
Source source = source2._unitSource;
if (container == null || container.contains(source)) {
removeSource(context, source);
}
}
}
// remove sources #2
Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _contextToSourceToLocations[context];
if (sourceToLocations != null) {
List<MemoryIndexStoreImpl_Source2> sources = [];
for (MemoryIndexStoreImpl_Source2 source2 in sources) {
Source source = source2._unitSource;
if (container == null || container.contains(source)) {
removeSource(context, source);
}
}
}
}
/**
* Creates new [Set] that uses object identity instead of equals.
*/
Set<Location> _createLocationIdentitySet() => new Set<Location>.identity();
/**
* @return the canonical [ElementRelationKey] for given [Element] and
* [Relationship], i.e. unique instance for this combination.
*/
MemoryIndexStoreImpl_ElementRelationKey _getCanonicalKey(Element element, Relationship relationship) {
MemoryIndexStoreImpl_ElementRelationKey key = new MemoryIndexStoreImpl_ElementRelationKey(element, relationship);
MemoryIndexStoreImpl_ElementRelationKey canonicalKey = _canonicalKeys[key];
if (canonicalKey == null) {
canonicalKey = key;
_canonicalKeys[key] = canonicalKey;
}
return canonicalKey;
}
void _recordUnitInLibrary(AnalysisContext context, Source library, Source unit) {
Map<Source, Set<Source>> unitToLibraries = _contextToUnitToLibraries[context];
if (unitToLibraries == null) {
unitToLibraries = {};
_contextToUnitToLibraries[context] = unitToLibraries;
}
Set<Source> libraries = unitToLibraries[unit];
if (libraries == null) {
libraries = new Set();
unitToLibraries[unit] = libraries;
}
libraries.add(library);
}
/**
* Removes locations recorded in the given library/unit pair.
*/
void _removeLocations(AnalysisContext context, Source library, Source unit) {
MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(library, unit);
Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _contextToSourceToLocations[context];
if (sourceToLocations != null) {
List<Location> sourceLocations = sourceToLocations.remove(source2);
if (sourceLocations != null) {
for (Location location in sourceLocations) {
MemoryIndexStoreImpl_ElementRelationKey key = location.internalKey as MemoryIndexStoreImpl_ElementRelationKey;
Set<Location> relLocations = _keyToLocations[key];
if (relLocations != null) {
relLocations.remove(location);
_locationCount--;
// no locations with this key
if (relLocations.isEmpty) {
_canonicalKeys.remove(key);
_keyToLocations.remove(key);
_keyCount--;
}
}
}
}
}
}
}
class MemoryIndexStoreImpl_ElementRelationKey {
final Element _element;
final Relationship _relationship;
MemoryIndexStoreImpl_ElementRelationKey(this._element, this._relationship);
@override
bool operator ==(Object obj) {
MemoryIndexStoreImpl_ElementRelationKey other = obj as MemoryIndexStoreImpl_ElementRelationKey;
Element otherElement = other._element;
return identical(other._relationship, _relationship) && otherElement.nameOffset == _element.nameOffset && otherElement.kind == _element.kind && otherElement.displayName == _element.displayName && otherElement.source == _element.source;
}
@override
int get hashCode => JavaArrays.makeHashCode([
_element.source,
_element.nameOffset,
_element.kind,
_element.displayName,
_relationship]);
@override
String toString() => "${_element} ${_relationship}";
}
class MemoryIndexStoreImpl_Source2 {
final Source _librarySource;
final Source _unitSource;
MemoryIndexStoreImpl_Source2(this._librarySource, this._unitSource);
@override
bool operator ==(Object obj) {
if (identical(obj, this)) {
return true;
}
if (obj is! MemoryIndexStoreImpl_Source2) {
return false;
}
MemoryIndexStoreImpl_Source2 other = obj as MemoryIndexStoreImpl_Source2;
return other._librarySource == _librarySource && other._unitSource == _unitSource;
}
@override
int get hashCode => JavaArrays.makeHashCode([_librarySource, _unitSource]);
@override
String toString() => "${_librarySource} ${_unitSource}";
}
/**
* Special [Element] which is used to index references to the name without specifying concrete
* kind of this name - field, method or something else.
*/
class NameElementImpl extends ElementImpl {
NameElementImpl(String name) : super("name:${name}", -1);
@override
accept(ElementVisitor visitor) => null;
@override
ElementKind get kind => ElementKind.NAME;
}
/**
* The enumeration <code>ProcessorState</code> represents the possible states of an operation
* processor.
*/
class ProcessorState extends Enum<ProcessorState> {
/**
* The processor is ready to be run (has not been run before).
*/
static const ProcessorState READY = const ProcessorState('READY', 0);
/**
* The processor is currently performing operations.
*/
static const ProcessorState RUNNING = const ProcessorState('RUNNING', 1);
/**
* The processor is currently performing operations but has been asked to stop.
*/
static const ProcessorState STOP_REQESTED = const ProcessorState('STOP_REQESTED', 2);
/**
* The processor has stopped performing operations and cannot be used again.
*/
static const ProcessorState STOPPED = const ProcessorState('STOPPED', 3);
static const List<ProcessorState> values = const [READY, RUNNING, STOP_REQESTED, STOPPED];
const ProcessorState(String name, int ordinal) : super(name, ordinal);
}
/**
* Relationship between an element and a location. Relationships are identified by a globally unique
* identifier.
*/
class Relationship {
/**
* The unique identifier for this relationship.
*/
final String _uniqueId;
/**
* A table mapping relationship identifiers to relationships.
*/
static Map<String, Relationship> _RelationshipMap = {};
/**
* Return the relationship with the given unique identifier.
*
* @param uniqueId the unique identifier for the relationship
* @return the relationship with the given unique identifier
*/
static Relationship getRelationship(String uniqueId) {
Relationship relationship = _RelationshipMap[uniqueId];
if (relationship == null) {
relationship = new Relationship(uniqueId);
_RelationshipMap[uniqueId] = relationship;
}
return relationship;
}
/**
* @return all registered [Relationship]s.
*/
static Iterable<Relationship> values() => _RelationshipMap.values;
/**
* Initialize a newly created relationship to have the given unique identifier.
*
* @param uniqueId the unique identifier for this relationship
*/
Relationship(this._uniqueId);
/**
* Return the unique identifier for this relationship.
*
* @return the unique identifier for this relationship
*/
String get identifier => _uniqueId;
@override
String toString() => _uniqueId;
}
/**
* The interface <code>RelationshipCallback</code> defines the behavior of objects that are invoked
* with the results of a query about a given relationship.
*/
abstract class RelationshipCallback {
/**
* This method is invoked when the locations that have a specified relationship with a specified
* element are available. For example, if the element is a field and the relationship is the
* is-referenced-by relationship, then this method will be invoked with each location at which the
* field is referenced.
*
* @param element the [Element] that has the relationship with the locations
* @param relationship the relationship between the given element and the locations
* @param locations the locations that were found
*/
void hasRelationships(Element element, Relationship relationship, List<Location> locations);
}
/**
* Instances of the [RemoveContextOperation] implement an operation that removes from the
* index any data based on the specified [AnalysisContext].
*/
class RemoveContextOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
/**
* The context being removed.
*/
final AnalysisContext context;
/**
* Initialize a newly created operation that will remove the specified resource.
*
* @param indexStore the index store against which this operation is being run
* @param context the [AnalysisContext] to remove
*/
RemoveContextOperation(this._indexStore, this.context);
@override
bool get isQuery => false;
@override
void performOperation() {
_indexStore.removeContext(context);
}
@override
bool removeWhenSourceRemoved(Source source) => false;
@override
String toString() => "RemoveContext(${context})";
}
/**
* Instances of the [RemoveSourceOperation] implement an operation that removes from the index
* any data based on the content of a specified source.
*/
class RemoveSourceOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
/**
* The context in which source being removed.
*/
final AnalysisContext _context;
/**
* The source being removed.
*/
final Source source;
/**
* Initialize a newly created operation that will remove the specified resource.
*
* @param indexStore the index store against which this operation is being run
* @param context the [AnalysisContext] to remove source in
* @param source the [Source] to remove from index
*/
RemoveSourceOperation(this._indexStore, this._context, this.source);
@override
bool get isQuery => false;
@override
void performOperation() {
_indexStore.removeSource(_context, source);
}
@override
bool removeWhenSourceRemoved(Source source) => false;
@override
String toString() => "RemoveSource(${source.fullName})";
}
/**
* Instances of the [RemoveSourcesOperation] implement an operation that removes from the
* index any data based on the content of source belonging to a [SourceContainer].
*/
class RemoveSourcesOperation implements IndexOperation {
/**
* The index store against which this operation is being run.
*/
final IndexStore _indexStore;
/**
* The context to remove container.
*/
final AnalysisContext _context;
/**
* The source container to remove.
*/
final SourceContainer container;
/**
* Initialize a newly created operation that will remove the specified resource.
*
* @param indexStore the index store against which this operation is being run
* @param context the [AnalysisContext] to remove container in
* @param container the [SourceContainer] to remove from index
*/
RemoveSourcesOperation(this._indexStore, this._context, this.container);
@override
bool get isQuery => false;
@override
void performOperation() {
_indexStore.removeSources(_context, container);
}
@override
bool removeWhenSourceRemoved(Source source) => false;
@override
String toString() => "RemoveSources(${container})";
}
/**
* The interface `UniverseElement` defines element to use when we want to request "defines"
* relations without specifying exact library.
*/
abstract class UniverseElement implements Element {
static final UniverseElement INSTANCE = UniverseElementImpl.INSTANCE;
}
/**
* Implementation of [UniverseElement].
*/
class UniverseElementImpl extends ElementImpl implements UniverseElement {
static UniverseElementImpl INSTANCE = new UniverseElementImpl();
UniverseElementImpl() : super("--universe--", -1);
@override
accept(ElementVisitor visitor) => null;
@override
ElementKind get kind => ElementKind.UNIVERSE;
}