blob: 0079e470070535dc8fccacb430a3231bb2cb0bd0 [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.
library analyzer.src.dart.element.builder;
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
/**
* Instances of the class `ApiElementBuilder` traverse an AST structure and
* build elements outside of function bodies and initializers.
*/
class ApiElementBuilder extends _BaseElementBuilder {
/**
* A table mapping field names to field elements for the fields defined in the current class, or
* `null` if we are not in the scope of a class.
*/
HashMap<String, FieldElement> _fieldMap;
/**
* Initialize a newly created element builder to build the elements for a
* compilation unit. The [initialHolder] is the element holder to which the
* children of the visited compilation unit node will be added.
*/
ApiElementBuilder(ElementHolder initialHolder,
CompilationUnitElementImpl compilationUnitElement)
: super(initialHolder, compilationUnitElement);
@override
Object visitAnnotation(Annotation node) {
// Although it isn't valid to do so because closures are not constant
// expressions, it's possible for one of the arguments to the constructor to
// contain a closure. Wrapping the processing of the annotation this way
// prevents these closures from being added to the list of functions in the
// annotated declaration.
ElementHolder holder = new ElementHolder();
ElementHolder previousHolder = _currentHolder;
_currentHolder = holder;
try {
super.visitAnnotation(node);
} finally {
_currentHolder = previousHolder;
}
return null;
}
@override
Object visitBlockFunctionBody(BlockFunctionBody node) {
return null;
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
ElementHolder holder = new ElementHolder();
//
// Process field declarations before constructors and methods so that field
// formal parameters can be correctly resolved to their fields.
//
ElementHolder previousHolder = _currentHolder;
_currentHolder = holder;
try {
List<ClassMember> nonFields = new List<ClassMember>();
node.visitChildren(
new _ElementBuilder_visitClassDeclaration(this, nonFields));
_buildFieldMap(holder.fieldsWithoutFlushing);
int count = nonFields.length;
for (int i = 0; i < count; i++) {
nonFields[i].accept(this);
}
} finally {
_currentHolder = previousHolder;
}
SimpleIdentifier className = node.name;
ClassElementImpl element = new ClassElementImpl.forNode(className);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
element.typeParameters = holder.typeParameters;
setElementDocumentationComment(element, node);
element.abstract = node.isAbstract;
element.accessors = holder.accessors;
List<ConstructorElement> constructors = holder.constructors;
if (constructors.isEmpty) {
constructors = _createDefaultConstructors(element);
}
element.constructors = constructors;
element.fields = holder.fields;
element.methods = holder.methods;
_currentHolder.addType(element);
className.staticElement = element;
_fieldMap = null;
holder.validate();
return null;
}
@override
Object visitClassTypeAlias(ClassTypeAlias node) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
SimpleIdentifier className = node.name;
ClassElementImpl element = new ClassElementImpl.forNode(className);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
element.abstract = node.abstractKeyword != null;
element.mixinApplication = true;
element.typeParameters = holder.typeParameters;
setElementDocumentationComment(element, node);
_currentHolder.addType(element);
className.staticElement = element;
holder.validate();
return null;
}
@override
Object visitCompilationUnit(CompilationUnit node) {
if (_unitElement is ElementImpl) {
_setCodeRange(_unitElement, node);
}
return super.visitCompilationUnit(node);
}
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
FunctionBody body = node.body;
SimpleIdentifier constructorName = node.name;
ConstructorElementImpl element =
new ConstructorElementImpl.forNode(constructorName);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(element, node);
if (node.externalKeyword != null) {
element.external = true;
}
if (node.factoryKeyword != null) {
element.factory = true;
}
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.isConst = node.constKeyword != null;
element.isCycleFree = element.isConst;
if (body.isAsynchronous) {
element.asynchronous = true;
}
if (body.isGenerator) {
element.generator = true;
}
_currentHolder.addConstructor(element);
node.element = element;
if (constructorName == null) {
Identifier returnType = node.returnType;
if (returnType != null) {
element.nameOffset = returnType.offset;
element.nameEnd = returnType.end;
}
} else {
constructorName.staticElement = element;
element.periodOffset = node.period.offset;
element.nameEnd = constructorName.end;
}
holder.validate();
return null;
}
@override
Object visitEnumDeclaration(EnumDeclaration node) {
SimpleIdentifier enumName = node.name;
EnumElementImpl enumElement = new EnumElementImpl.forNode(enumName);
_setCodeRange(enumElement, node);
enumElement.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(enumElement, node);
InterfaceTypeImpl enumType = enumElement.type;
//
// Build the elements for the constants. These are minimal elements; the
// rest of the constant elements (and elements for other fields) must be
// built later after we can access the type provider.
//
List<FieldElement> fields = new List<FieldElement>();
NodeList<EnumConstantDeclaration> constants = node.constants;
for (EnumConstantDeclaration constant in constants) {
SimpleIdentifier constantName = constant.name;
FieldElementImpl constantField =
new ConstFieldElementImpl.forNode(constantName);
constantField.isStatic = true;
constantField.isConst = true;
constantField.type = enumType;
setElementDocumentationComment(constantField, constant);
fields.add(constantField);
new PropertyAccessorElementImpl_ImplicitGetter(constantField);
constantName.staticElement = constantField;
}
enumElement.fields = fields;
_currentHolder.addEnum(enumElement);
enumName.staticElement = enumElement;
return super.visitEnumDeclaration(node);
}
@override
Object visitExportDirective(ExportDirective node) {
List<ElementAnnotation> annotations =
_createElementAnnotations(node.metadata);
_unitElement.setAnnotations(node.offset, annotations);
return super.visitExportDirective(node);
}
@override
Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
return null;
}
@override
Object visitFieldFormalParameter(FieldFormalParameter node) {
if (node.parent is! DefaultFormalParameter) {
SimpleIdentifier parameterName = node.identifier;
FieldFormalParameterElementImpl parameter =
new FieldFormalParameterElementImpl.forNode(parameterName);
_setCodeRange(parameter, node);
_setFieldParameterField(node, parameter);
parameter.isConst = node.isConst;
parameter.isExplicitlyCovariant = node.covariantKeyword != null;
parameter.isFinal = node.isFinal;
parameter.parameterKind = node.kind;
_currentHolder.addParameter(parameter);
parameterName.staticElement = parameter;
}
//
// The children of this parameter include any parameters defined on the type
// of this parameter.
//
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
ParameterElementImpl element = node.element;
element.metadata = _createElementAnnotations(node.metadata);
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
holder.validate();
return null;
}
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
FunctionExpression expression = node.functionExpression;
if (expression != null) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
FunctionBody body = expression.body;
Token property = node.propertyKeyword;
if (property == null) {
SimpleIdentifier functionName = node.name;
FunctionElementImpl element =
new FunctionElementImpl.forNode(functionName);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(element, node);
if (node.externalKeyword != null || body is NativeFunctionBody) {
element.external = true;
}
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
if (body.isAsynchronous) {
element.asynchronous = true;
}
if (body.isGenerator) {
element.generator = true;
}
if (node.returnType == null) {
element.hasImplicitReturnType = true;
}
_currentHolder.addFunction(element);
expression.element = element;
functionName.staticElement = element;
} else {
SimpleIdentifier propertyNameNode = node.name;
if (propertyNameNode == null) {
// TODO(brianwilkerson) Report this internal error.
return null;
}
String propertyName = propertyNameNode.name;
TopLevelVariableElementImpl variable = _currentHolder
.getTopLevelVariable(propertyName) as TopLevelVariableElementImpl;
if (variable == null) {
variable = new TopLevelVariableElementImpl(node.name.name, -1);
variable.isFinal = true;
variable.isSynthetic = true;
_currentHolder.addTopLevelVariable(variable);
}
if (node.isGetter) {
PropertyAccessorElementImpl getter =
new PropertyAccessorElementImpl.forNode(propertyNameNode);
_setCodeRange(getter, node);
getter.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(getter, node);
if (node.externalKeyword != null || body is NativeFunctionBody) {
getter.external = true;
}
getter.functions = holder.functions;
getter.labels = holder.labels;
getter.localVariables = holder.localVariables;
if (body.isAsynchronous) {
getter.asynchronous = true;
}
if (body.isGenerator) {
getter.generator = true;
}
getter.variable = variable;
getter.getter = true;
getter.isStatic = true;
variable.getter = getter;
if (node.returnType == null) {
getter.hasImplicitReturnType = true;
}
_currentHolder.addAccessor(getter);
expression.element = getter;
propertyNameNode.staticElement = getter;
} else {
PropertyAccessorElementImpl setter =
new PropertyAccessorElementImpl.forNode(propertyNameNode);
_setCodeRange(setter, node);
setter.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(setter, node);
if (node.externalKeyword != null || body is NativeFunctionBody) {
setter.external = true;
}
setter.functions = holder.functions;
setter.labels = holder.labels;
setter.localVariables = holder.localVariables;
setter.parameters = holder.parameters;
if (body.isAsynchronous) {
setter.asynchronous = true;
}
if (body.isGenerator) {
setter.generator = true;
}
setter.variable = variable;
setter.setter = true;
setter.isStatic = true;
if (node.returnType == null) {
setter.hasImplicitReturnType = true;
}
variable.setter = setter;
variable.isFinal = false;
_currentHolder.addAccessor(setter);
expression.element = setter;
propertyNameNode.staticElement = setter;
}
}
holder.validate();
}
return null;
}
@override
Object visitFunctionExpression(FunctionExpression node) {
if (node.parent is FunctionDeclaration) {
// visitFunctionDeclaration has already created the element for the
// declaration. We just need to visit children.
return super.visitFunctionExpression(node);
}
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
FunctionBody body = node.body;
FunctionElementImpl element =
new FunctionElementImpl.forOffset(node.beginToken.offset);
_setCodeRange(element, node);
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
if (body.isAsynchronous) {
element.asynchronous = true;
}
if (body.isGenerator) {
element.generator = true;
}
element.type = new FunctionTypeImpl(element);
element.hasImplicitReturnType = true;
_currentHolder.addFunction(element);
node.element = element;
holder.validate();
return null;
}
@override
Object visitFunctionTypeAlias(FunctionTypeAlias node) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
SimpleIdentifier aliasName = node.name;
List<ParameterElement> parameters = holder.parameters;
List<TypeParameterElement> typeParameters = holder.typeParameters;
FunctionTypeAliasElementImpl element =
new FunctionTypeAliasElementImpl.forNode(aliasName);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(element, node);
element.parameters = parameters;
element.typeParameters = typeParameters;
_createTypeParameterTypes(typeParameters);
element.type = new FunctionTypeImpl.forTypedef(element);
_currentHolder.addTypeAlias(element);
aliasName.staticElement = element;
holder.validate();
return null;
}
@override
Object visitGenericTypeAlias(GenericTypeAlias node) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
SimpleIdentifier aliasName = node.name;
List<TypeParameterElement> typeParameters = holder.typeParameters;
GenericTypeAliasElementImpl element =
new GenericTypeAliasElementImpl.forNode(aliasName);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(element, node);
element.typeParameters = typeParameters;
_createTypeParameterTypes(typeParameters);
element.type = new FunctionTypeImpl.forTypedef(element);
element.function = node.functionType?.type?.element;
_currentHolder.addTypeAlias(element);
aliasName.staticElement = element;
holder.validate();
return null;
}
@override
Object visitImportDirective(ImportDirective node) {
List<ElementAnnotation> annotations =
_createElementAnnotations(node.metadata);
_unitElement.setAnnotations(node.offset, annotations);
return super.visitImportDirective(node);
}
@override
Object visitLibraryDirective(LibraryDirective node) {
List<ElementAnnotation> annotations =
_createElementAnnotations(node.metadata);
_unitElement.setAnnotations(node.offset, annotations);
return super.visitLibraryDirective(node);
}
@override
Object visitMethodDeclaration(MethodDeclaration node) {
try {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
bool isStatic = node.isStatic;
Token property = node.propertyKeyword;
FunctionBody body = node.body;
if (property == null) {
SimpleIdentifier methodName = node.name;
String nameOfMethod = methodName.name;
if (nameOfMethod == TokenType.MINUS.lexeme &&
node.parameters.parameters.length == 0) {
nameOfMethod = "unary-";
}
MethodElementImpl element =
new MethodElementImpl(nameOfMethod, methodName.offset);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(element, node);
element.abstract = node.isAbstract;
if (node.externalKeyword != null || body is NativeFunctionBody) {
element.external = true;
}
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.isStatic = isStatic;
element.typeParameters = holder.typeParameters;
if (body.isAsynchronous) {
element.asynchronous = true;
}
if (body.isGenerator) {
element.generator = true;
}
if (node.returnType == null) {
element.hasImplicitReturnType = true;
}
_currentHolder.addMethod(element);
methodName.staticElement = element;
} else {
SimpleIdentifier propertyNameNode = node.name;
String propertyName = propertyNameNode.name;
FieldElementImpl field = _currentHolder.getField(propertyName,
synthetic: true) as FieldElementImpl;
if (field == null) {
field = new FieldElementImpl(node.name.name, -1);
field.isFinal = true;
field.isStatic = isStatic;
field.isSynthetic = true;
_currentHolder.addField(field);
}
if (node.isGetter) {
PropertyAccessorElementImpl getter =
new PropertyAccessorElementImpl.forNode(propertyNameNode);
_setCodeRange(getter, node);
getter.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(getter, node);
if (node.externalKeyword != null || body is NativeFunctionBody) {
getter.external = true;
}
getter.functions = holder.functions;
getter.labels = holder.labels;
getter.localVariables = holder.localVariables;
if (body.isAsynchronous) {
getter.asynchronous = true;
}
if (body.isGenerator) {
getter.generator = true;
}
getter.variable = field;
getter.abstract = node.isAbstract;
getter.getter = true;
getter.isStatic = isStatic;
field.getter = getter;
if (node.returnType == null) {
getter.hasImplicitReturnType = true;
}
_currentHolder.addAccessor(getter);
propertyNameNode.staticElement = getter;
} else {
PropertyAccessorElementImpl setter =
new PropertyAccessorElementImpl.forNode(propertyNameNode);
_setCodeRange(setter, node);
setter.metadata = _createElementAnnotations(node.metadata);
setElementDocumentationComment(setter, node);
if (node.externalKeyword != null || body is NativeFunctionBody) {
setter.external = true;
}
setter.functions = holder.functions;
setter.labels = holder.labels;
setter.localVariables = holder.localVariables;
setter.parameters = holder.parameters;
if (body.isAsynchronous) {
setter.asynchronous = true;
}
if (body.isGenerator) {
setter.generator = true;
}
setter.variable = field;
setter.abstract = node.isAbstract;
setter.setter = true;
setter.isStatic = isStatic;
if (node.returnType == null) {
setter.hasImplicitReturnType = true;
}
field.setter = setter;
field.isFinal = false;
_currentHolder.addAccessor(setter);
propertyNameNode.staticElement = setter;
}
}
holder.validate();
} catch (exception, stackTrace) {
if (node.name.staticElement == null) {
ClassDeclaration classNode =
node.getAncestor((node) => node is ClassDeclaration);
StringBuffer buffer = new StringBuffer();
buffer.write("The element for the method ");
buffer.write(node.name);
buffer.write(" in ");
buffer.write(classNode.name);
buffer.write(" was not set while trying to build the element model.");
AnalysisEngine.instance.logger.logError(
buffer.toString(), new CaughtException(exception, stackTrace));
} else {
String message =
"Exception caught in ElementBuilder.visitMethodDeclaration()";
AnalysisEngine.instance.logger
.logError(message, new CaughtException(exception, stackTrace));
}
} finally {
if (node.name.staticElement == null) {
ClassDeclaration classNode =
node.getAncestor((node) => node is ClassDeclaration);
StringBuffer buffer = new StringBuffer();
buffer.write("The element for the method ");
buffer.write(node.name);
buffer.write(" in ");
buffer.write(classNode.name);
buffer.write(" was not set while trying to resolve types.");
AnalysisEngine.instance.logger.logError(
buffer.toString(),
new CaughtException(
new AnalysisException(buffer.toString()), null));
}
}
return null;
}
@override
Object visitPartDirective(PartDirective node) {
List<ElementAnnotation> annotations =
_createElementAnnotations(node.metadata);
_unitElement.setAnnotations(node.offset, annotations);
return super.visitPartDirective(node);
}
@override
Object visitVariableDeclaration(VariableDeclaration node) {
bool isConst = node.isConst;
bool isFinal = node.isFinal;
Expression initializerNode = node.initializer;
bool hasInitializer = initializerNode != null;
VariableDeclarationList varList = node.parent;
FieldDeclaration fieldNode =
varList.parent is FieldDeclaration ? varList.parent : null;
VariableElementImpl element;
if (fieldNode != null) {
SimpleIdentifier fieldName = node.name;
FieldElementImpl field;
if ((isConst || isFinal && !fieldNode.isStatic) && hasInitializer) {
field = new ConstFieldElementImpl.forNode(fieldName);
} else {
field = new FieldElementImpl.forNode(fieldName);
}
element = field;
field.isCovariant = fieldNode.covariantKeyword != null;
field.isStatic = fieldNode.isStatic;
_setCodeRange(element, node);
setElementDocumentationComment(element, fieldNode);
field.hasImplicitType = varList.type == null;
_currentHolder.addField(field);
fieldName.staticElement = field;
} else {
SimpleIdentifier variableName = node.name;
TopLevelVariableElementImpl variable;
if (isConst && hasInitializer) {
variable = new ConstTopLevelVariableElementImpl.forNode(variableName);
} else {
variable = new TopLevelVariableElementImpl.forNode(variableName);
}
element = variable;
_setCodeRange(element, node);
if (varList.parent is TopLevelVariableDeclaration) {
setElementDocumentationComment(element, varList.parent);
}
variable.hasImplicitType = varList.type == null;
_currentHolder.addTopLevelVariable(variable);
variableName.staticElement = element;
}
element.isConst = isConst;
element.isFinal = isFinal;
if (element is PropertyInducingElementImpl) {
PropertyAccessorElementImpl_ImplicitGetter getter =
new PropertyAccessorElementImpl_ImplicitGetter(element);
_currentHolder.addAccessor(getter);
if (!isConst && !isFinal) {
PropertyAccessorElementImpl_ImplicitSetter setter =
new PropertyAccessorElementImpl_ImplicitSetter(element);
if (fieldNode != null) {
(setter.parameters[0] as ParameterElementImpl).isExplicitlyCovariant =
fieldNode.covariantKeyword != null;
}
_currentHolder.addAccessor(setter);
}
}
return null;
}
@override
Object visitVariableDeclarationList(VariableDeclarationList node) {
super.visitVariableDeclarationList(node);
AstNode parent = node.parent;
List<ElementAnnotation> elementAnnotations;
if (parent is FieldDeclaration) {
elementAnnotations = _createElementAnnotations(parent.metadata);
} else if (parent is TopLevelVariableDeclaration) {
elementAnnotations = _createElementAnnotations(parent.metadata);
} else {
// Local variable declaration
elementAnnotations = _createElementAnnotations(node.metadata);
}
_setVariableDeclarationListAnnotations(node, elementAnnotations);
return null;
}
/**
* Build the table mapping field names to field elements for the [fields]
* defined in the current class.
*/
void _buildFieldMap(List<FieldElement> fields) {
_fieldMap = new HashMap<String, FieldElement>();
int count = fields.length;
for (int i = 0; i < count; i++) {
FieldElement field = fields[i];
_fieldMap[field.name] ??= field;
}
}
/**
* Creates the [ConstructorElement]s array with the single default constructor element.
*
* @param interfaceType the interface type for which to create a default constructor
* @return the [ConstructorElement]s array with the single default constructor element
*/
List<ConstructorElement> _createDefaultConstructors(
ClassElementImpl definingClass) {
ConstructorElementImpl constructor =
new ConstructorElementImpl.forNode(null);
constructor.isSynthetic = true;
constructor.enclosingElement = definingClass;
return <ConstructorElement>[constructor];
}
/**
* Create the types associated with the given type parameters, setting the type of each type
* parameter, and return an array of types corresponding to the given parameters.
*
* @param typeParameters the type parameters for which types are to be created
* @return an array of types corresponding to the given parameters
*/
List<DartType> _createTypeParameterTypes(
List<TypeParameterElement> typeParameters) {
int typeParameterCount = typeParameters.length;
List<DartType> typeArguments = new List<DartType>(typeParameterCount);
for (int i = 0; i < typeParameterCount; i++) {
TypeParameterElementImpl typeParameter =
typeParameters[i] as TypeParameterElementImpl;
TypeParameterTypeImpl typeParameterType =
new TypeParameterTypeImpl(typeParameter);
typeParameter.type = typeParameterType;
typeArguments[i] = typeParameterType;
}
return typeArguments;
}
@override
void _setFieldParameterField(
FormalParameter node, FieldFormalParameterElementImpl element) {
if (node.parent?.parent is ConstructorDeclaration) {
FieldElement field = _fieldMap == null ? null : _fieldMap[element.name];
if (field != null) {
element.field = field;
}
}
}
}
/**
* A `CompilationUnitBuilder` builds an element model for a single compilation
* unit.
*/
class CompilationUnitBuilder {
/**
* Build the compilation unit element for the given [source] based on the
* compilation [unit] associated with the source. Throw an AnalysisException
* if the element could not be built. [librarySource] is the source for the
* containing library.
*/
CompilationUnitElementImpl buildCompilationUnit(
Source source, CompilationUnit unit, Source librarySource) {
return PerformanceStatistics.resolve.makeCurrentWhile(() {
if (unit == null) {
return null;
}
ElementHolder holder = new ElementHolder();
CompilationUnitElementImpl element =
new CompilationUnitElementImpl(source.shortName);
ElementBuilder builder = new ElementBuilder(holder, element);
unit.accept(builder);
element.accessors = holder.accessors;
element.enums = holder.enums;
element.functions = holder.functions;
element.source = source;
element.librarySource = librarySource;
element.typeAliases = holder.typeAliases;
element.types = holder.types;
element.topLevelVariables = holder.topLevelVariables;
unit.element = element;
holder.validate();
return element;
});
}
}
/**
* Instances of the class `DirectiveElementBuilder` build elements for top
* level library directives.
*/
class DirectiveElementBuilder extends SimpleAstVisitor<Object> {
/**
* The analysis context within which directive elements are being built.
*/
final AnalysisContext context;
/**
* The library element for which directive elements are being built.
*/
final LibraryElementImpl libraryElement;
/**
* Map from sources referenced by this library to their modification times.
*/
final Map<Source, int> sourceModificationTimeMap;
/**
* Map from sources imported by this library to their corresponding library
* elements.
*/
final Map<Source, LibraryElement> importLibraryMap;
/**
* Map from sources imported by this library to their corresponding source
* kinds.
*/
final Map<Source, SourceKind> importSourceKindMap;
/**
* Map from sources exported by this library to their corresponding library
* elements.
*/
final Map<Source, LibraryElement> exportLibraryMap;
/**
* Map from sources exported by this library to their corresponding source
* kinds.
*/
final Map<Source, SourceKind> exportSourceKindMap;
/**
* The [ImportElement]s created so far.
*/
final List<ImportElement> imports = <ImportElement>[];
/**
* The [ExportElement]s created so far.
*/
final List<ExportElement> exports = <ExportElement>[];
/**
* The errors found while building directive elements.
*/
final List<AnalysisError> errors = <AnalysisError>[];
/**
* Map from prefix names to their corresponding elements.
*/
final HashMap<String, PrefixElementImpl> nameToPrefixMap =
new HashMap<String, PrefixElementImpl>();
/**
* Indicates whether an explicit import of `dart:core` has been found.
*/
bool explicitlyImportsCore = false;
DirectiveElementBuilder(
this.context,
this.libraryElement,
this.sourceModificationTimeMap,
this.importLibraryMap,
this.importSourceKindMap,
this.exportLibraryMap,
this.exportSourceKindMap);
@override
Object visitCompilationUnit(CompilationUnit node) {
//
// Resolve directives.
//
for (Directive directive in node.directives) {
directive.accept(this);
}
//
// Ensure "dart:core" import.
//
Source librarySource = libraryElement.source;
Source coreLibrarySource = context.sourceFactory.forUri(DartSdk.DART_CORE);
if (!explicitlyImportsCore && coreLibrarySource != librarySource) {
ImportElementImpl importElement = new ImportElementImpl(-1);
importElement.importedLibrary = importLibraryMap[coreLibrarySource];
importElement.isSynthetic = true;
imports.add(importElement);
}
//
// Populate the library element.
//
libraryElement.imports = imports;
libraryElement.exports = exports;
return null;
}
@override
Object visitExportDirective(ExportDirective node) {
// Remove previous element. (It will remain null if the target is missing.)
node.element = null;
Source exportedSource = node.selectedSource;
int exportedTime = sourceModificationTimeMap[exportedSource] ?? -1;
// The exported source will be null if the URI in the export
// directive was invalid.
LibraryElement exportedLibrary = exportLibraryMap[exportedSource];
if (exportedLibrary != null) {
ExportElementImpl exportElement = new ExportElementImpl(node.offset);
exportElement.metadata = _getElementAnnotations(node.metadata);
StringLiteral uriLiteral = node.uri;
if (uriLiteral != null) {
exportElement.uriOffset = uriLiteral.offset;
exportElement.uriEnd = uriLiteral.end;
}
exportElement.uri = node.selectedUriContent;
exportElement.combinators = _buildCombinators(node);
exportElement.exportedLibrary = exportedLibrary;
setElementDocumentationComment(exportElement, node);
node.element = exportElement;
exports.add(exportElement);
if (exportedTime >= 0 &&
exportSourceKindMap[exportedSource] != SourceKind.LIBRARY) {
int offset = node.offset;
int length = node.length;
if (uriLiteral != null) {
offset = uriLiteral.offset;
length = uriLiteral.length;
}
errors.add(new AnalysisError(
libraryElement.source,
offset,
length,
CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
[uriLiteral.toSource()]));
}
}
return null;
}
@override
Object visitImportDirective(ImportDirective node) {
// Remove previous element. (It will remain null if the target is missing.)
node.element = null;
Source importedSource = node.selectedSource;
int importedTime = sourceModificationTimeMap[importedSource] ?? -1;
// The imported source will be null if the URI in the import
// directive was invalid.
LibraryElement importedLibrary = importLibraryMap[importedSource];
if (importedLibrary != null) {
if (importedLibrary.isDartCore) {
explicitlyImportsCore = true;
}
ImportElementImpl importElement = new ImportElementImpl(node.offset);
importElement.metadata = _getElementAnnotations(node.metadata);
StringLiteral uriLiteral = node.uri;
if (uriLiteral != null) {
importElement.uriOffset = uriLiteral.offset;
importElement.uriEnd = uriLiteral.end;
}
importElement.uri = node.selectedUriContent;
importElement.deferred = node.deferredKeyword != null;
importElement.combinators = _buildCombinators(node);
importElement.importedLibrary = importedLibrary;
setElementDocumentationComment(importElement, node);
SimpleIdentifier prefixNode = node.prefix;
if (prefixNode != null) {
importElement.prefixOffset = prefixNode.offset;
String prefixName = prefixNode.name;
PrefixElementImpl prefix = nameToPrefixMap[prefixName];
if (prefix == null) {
prefix = new PrefixElementImpl.forNode(prefixNode);
nameToPrefixMap[prefixName] = prefix;
}
importElement.prefix = prefix;
prefixNode.staticElement = prefix;
}
node.element = importElement;
imports.add(importElement);
if (importedTime >= 0 &&
importSourceKindMap[importedSource] != SourceKind.LIBRARY) {
int offset = node.offset;
int length = node.length;
if (uriLiteral != null) {
offset = uriLiteral.offset;
length = uriLiteral.length;
}
ErrorCode errorCode = importElement.isDeferred
? StaticWarningCode.IMPORT_OF_NON_LIBRARY
: CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY;
errors.add(new AnalysisError(libraryElement.source, offset, length,
errorCode, [uriLiteral.toSource()]));
}
}
return null;
}
@override
Object visitLibraryDirective(LibraryDirective node) {
(node.element as LibraryElementImpl)?.metadata =
_getElementAnnotations(node.metadata);
return null;
}
@override
Object visitPartDirective(PartDirective node) {
(node.element as CompilationUnitElementImpl)?.metadata =
_getElementAnnotations(node.metadata);
return null;
}
/**
* Gather a list of the [ElementAnnotation]s referred to by the [Annotation]s
* in [metadata].
*/
List<ElementAnnotation> _getElementAnnotations(
NodeList<Annotation> metadata) {
if (metadata.isEmpty) {
return ElementAnnotation.EMPTY_LIST;
}
return metadata.map((Annotation a) => a.elementAnnotation).toList();
}
/**
* Build the element model representing the combinators declared by
* the given [directive].
*/
static List<NamespaceCombinator> _buildCombinators(
NamespaceDirective directive) {
_NamespaceCombinatorBuilder namespaceCombinatorBuilder =
new _NamespaceCombinatorBuilder();
for (Combinator combinator in directive.combinators) {
combinator.accept(namespaceCombinatorBuilder);
}
return namespaceCombinatorBuilder.combinators;
}
}
/**
* Instances of the class `ElementBuilder` traverse an AST structure and build the element
* model representing the AST structure.
*/
class ElementBuilder extends ApiElementBuilder {
/**
* Initialize a newly created element builder to build the elements for a
* compilation unit. The [initialHolder] is the element holder to which the
* children of the visited compilation unit node will be added.
*/
ElementBuilder(ElementHolder initialHolder,
CompilationUnitElement compilationUnitElement)
: super(initialHolder, compilationUnitElement);
@override
Object visitBlockFunctionBody(BlockFunctionBody node) {
_buildLocal(node);
return null;
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
super.visitDefaultFormalParameter(node);
buildParameterInitializer(
node.element as ParameterElementImpl, node.defaultValue);
return null;
}
@override
Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
_buildLocal(node);
return null;
}
@override
Object visitVariableDeclaration(VariableDeclaration node) {
super.visitVariableDeclaration(node);
VariableElementImpl element = node.element as VariableElementImpl;
buildVariableInitializer(element, node.initializer);
return null;
}
void _buildLocal(AstNode node) {
node.accept(new LocalElementBuilder(_currentHolder, _unitElement));
}
}
/**
* Traverse a [FunctionBody] and build elements for AST structures.
*/
class LocalElementBuilder extends _BaseElementBuilder {
/**
* Initialize a newly created element builder to build the elements for a
* compilation unit. The [initialHolder] is the element holder to which the
* children of the visited compilation unit node will be added.
*/
LocalElementBuilder(ElementHolder initialHolder,
CompilationUnitElementImpl compilationUnitElement)
: super(initialHolder, compilationUnitElement);
/**
* Builds the variable elements associated with [node] and stores them in
* the element holder.
*/
void buildCatchVariableElements(CatchClause node) {
SimpleIdentifier exceptionParameter = node.exceptionParameter;
if (exceptionParameter != null) {
// exception
LocalVariableElementImpl exception =
new LocalVariableElementImpl.forNode(exceptionParameter);
if (node.exceptionType == null) {
exception.hasImplicitType = true;
}
exception.setVisibleRange(node.offset, node.length);
_currentHolder.addLocalVariable(exception);
exceptionParameter.staticElement = exception;
// stack trace
SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
if (stackTraceParameter != null) {
LocalVariableElementImpl stackTrace =
new LocalVariableElementImpl.forNode(stackTraceParameter);
_setCodeRange(stackTrace, stackTraceParameter);
stackTrace.setVisibleRange(node.offset, node.length);
_currentHolder.addLocalVariable(stackTrace);
stackTraceParameter.staticElement = stackTrace;
}
}
}
/**
* Builds the label elements associated with [labels] and stores them in the
* element holder.
*/
void buildLabelElements(
NodeList<Label> labels, bool onSwitchStatement, bool onSwitchMember) {
for (Label label in labels) {
SimpleIdentifier labelName = label.label;
LabelElementImpl element = new LabelElementImpl.forNode(
labelName, onSwitchStatement, onSwitchMember);
labelName.staticElement = element;
_currentHolder.addLabel(element);
}
}
@override
Object visitCatchClause(CatchClause node) {
buildCatchVariableElements(node);
return super.visitCatchClause(node);
}
@override
Object visitDeclaredIdentifier(DeclaredIdentifier node) {
SimpleIdentifier variableName = node.identifier;
LocalVariableElementImpl element =
new LocalVariableElementImpl.forNode(variableName);
_setCodeRange(element, node);
element.metadata = _createElementAnnotations(node.metadata);
ForEachStatement statement = node.parent as ForEachStatement;
element.setVisibleRange(statement.offset, statement.length);
element.isConst = node.isConst;
element.isFinal = node.isFinal;
if (node.type == null) {
element.hasImplicitType = true;
}
_currentHolder.addLocalVariable(element);
variableName.staticElement = element;
return null;
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
super.visitDefaultFormalParameter(node);
buildParameterInitializer(
node.element as ParameterElementImpl, node.defaultValue);
return null;
}
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
FunctionExpression expression = node.functionExpression;
if (expression == null) {
return null;
}
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
FunctionElementImpl element = new FunctionElementImpl.forNode(node.name);
_setCodeRange(element, node);
setElementDocumentationComment(element, node);
element.metadata = _createElementAnnotations(node.metadata);
FunctionBody body = expression.body;
if (node.externalKeyword != null || body is NativeFunctionBody) {
element.external = true;
}
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
if (body.isAsynchronous) {
element.asynchronous = body.isAsynchronous;
}
if (body.isGenerator) {
element.generator = true;
}
{
Block enclosingBlock = node.getAncestor((node) => node is Block);
if (enclosingBlock != null) {
element.setVisibleRange(enclosingBlock.offset, enclosingBlock.length);
}
}
if (node.returnType == null) {
element.hasImplicitReturnType = true;
}
_currentHolder.addFunction(element);
expression.element = element;
node.name.staticElement = element;
holder.validate();
return null;
}
@override
Object visitFunctionExpression(FunctionExpression node) {
if (node.parent is FunctionDeclaration) {
// visitFunctionDeclaration has already created the element for the
// declaration. We just need to visit children.
return super.visitFunctionExpression(node);
}
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
FunctionElementImpl element =
new FunctionElementImpl.forOffset(node.beginToken.offset);
_setCodeRange(element, node);
element.functions = holder.functions;
element.labels = holder.labels;
element.localVariables = holder.localVariables;
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
FunctionBody body = node.body;
if (body.isAsynchronous) {
element.asynchronous = true;
}
if (body.isGenerator) {
element.generator = true;
}
{
Block enclosingBlock = node.getAncestor((node) => node is Block);
if (enclosingBlock != null) {
element.setVisibleRange(enclosingBlock.offset, enclosingBlock.length);
}
}
element.type = new FunctionTypeImpl(element);
element.hasImplicitReturnType = true;
_currentHolder.addFunction(element);
node.element = element;
holder.validate();
return null;
}
@override
Object visitLabeledStatement(LabeledStatement node) {
bool onSwitchStatement = node.statement is SwitchStatement;
buildLabelElements(node.labels, onSwitchStatement, false);
return super.visitLabeledStatement(node);
}
@override
Object visitSwitchCase(SwitchCase node) {
buildLabelElements(node.labels, false, true);
return super.visitSwitchCase(node);
}
@override
Object visitSwitchDefault(SwitchDefault node) {
buildLabelElements(node.labels, false, true);
return super.visitSwitchDefault(node);
}
@override
Object visitVariableDeclaration(VariableDeclaration node) {
bool isConst = node.isConst;
bool isFinal = node.isFinal;
Expression initializerNode = node.initializer;
VariableDeclarationList varList = node.parent;
SimpleIdentifier variableName = node.name;
LocalVariableElementImpl element;
if (isConst && initializerNode != null) {
element = new ConstLocalVariableElementImpl.forNode(variableName);
} else {
element = new LocalVariableElementImpl.forNode(variableName);
}
_setCodeRange(element, node);
_setVariableVisibleRange(element, node);
element.hasImplicitType = varList.type == null;
_currentHolder.addLocalVariable(element);
variableName.staticElement = element;
element.isConst = isConst;
element.isFinal = isFinal;
buildVariableInitializer(element, initializerNode);
return null;
}
@override
Object visitVariableDeclarationList(VariableDeclarationList node) {
super.visitVariableDeclarationList(node);
List<ElementAnnotation> elementAnnotations =
_createElementAnnotations(node.metadata);
_setVariableDeclarationListAnnotations(node, elementAnnotations);
return null;
}
void _setVariableVisibleRange(
LocalVariableElementImpl element, VariableDeclaration node) {
AstNode scopeNode;
AstNode parent2 = node.parent.parent;
if (parent2 is ForStatement) {
scopeNode = parent2;
} else {
scopeNode = node.getAncestor((node) => node is Block);
}
element.setVisibleRange(scopeNode.offset, scopeNode.length);
}
}
/**
* Base class for API and local element builders.
*/
abstract class _BaseElementBuilder extends RecursiveAstVisitor<Object> {
/**
* The compilation unit element into which the elements being built will be
* stored.
*/
final CompilationUnitElementImpl _unitElement;
/**
* The element holder associated with the element that is currently being built.
*/
ElementHolder _currentHolder;
_BaseElementBuilder(this._currentHolder, this._unitElement);
/**
* If the [defaultValue] is not `null`, build the [FunctionElementImpl]
* that corresponds it, and set it as the initializer for the [parameter].
*/
void buildParameterInitializer(
ParameterElementImpl parameter, Expression defaultValue) {
if (defaultValue != null) {
ElementHolder holder = new ElementHolder();
_visit(holder, defaultValue);
FunctionElementImpl initializer =
new FunctionElementImpl.forOffset(defaultValue.beginToken.offset);
initializer.hasImplicitReturnType = true;
initializer.functions = holder.functions;
initializer.labels = holder.labels;
initializer.localVariables = holder.localVariables;
initializer.parameters = holder.parameters;
initializer.isSynthetic = true;
initializer.type = new FunctionTypeImpl(initializer);
parameter.initializer = initializer;
parameter.defaultValueCode = defaultValue.toSource();
holder.validate();
}
}
/**
* If the [initializer] is not `null`, build the [FunctionElementImpl] that
* corresponds it, and set it as the initializer for the [variable].
*/
void buildVariableInitializer(
VariableElementImpl variable, Expression initializer) {
if (initializer != null) {
ElementHolder holder = new ElementHolder();
_visit(holder, initializer);
FunctionElementImpl initializerElement =
new FunctionElementImpl.forOffset(initializer.beginToken.offset);
initializerElement.hasImplicitReturnType = true;
initializerElement.functions = holder.functions;
initializerElement.labels = holder.labels;
initializerElement.localVariables = holder.localVariables;
initializerElement.isSynthetic = true;
initializerElement.type = new FunctionTypeImpl(initializerElement);
variable.initializer = initializerElement;
holder.validate();
}
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
NormalFormalParameter normalParameter = node.parameter;
SimpleIdentifier parameterName = normalParameter.identifier;
ParameterElementImpl parameter;
if (normalParameter is FieldFormalParameter) {
DefaultFieldFormalParameterElementImpl fieldParameter =
new DefaultFieldFormalParameterElementImpl.forNode(parameterName);
_setFieldParameterField(node, fieldParameter);
parameter = fieldParameter;
} else {
parameter = new DefaultParameterElementImpl.forNode(parameterName);
}
_setCodeRange(parameter, node);
parameter.isConst = node.isConst;
parameter.isExplicitlyCovariant = node.parameter.covariantKeyword != null;
parameter.isFinal = node.isFinal;
parameter.parameterKind = node.kind;
// visible range
_setParameterVisibleRange(node, parameter);
if (normalParameter is SimpleFormalParameter &&
normalParameter.type == null) {
parameter.hasImplicitType = true;
}
_currentHolder.addParameter(parameter);
if (normalParameter is SimpleFormalParameterImpl) {
normalParameter.element = parameter;
}
parameterName?.staticElement = parameter;
normalParameter.accept(this);
return null;
}
@override
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
if (node.parent is! DefaultFormalParameter) {
SimpleIdentifier parameterName = node.identifier;
ParameterElementImpl parameter =
new ParameterElementImpl.forNode(parameterName);
_setCodeRange(parameter, node);
parameter.isConst = node.isConst;
parameter.isExplicitlyCovariant = node.covariantKeyword != null;
parameter.isFinal = node.isFinal;
parameter.parameterKind = node.kind;
_setParameterVisibleRange(node, parameter);
_currentHolder.addParameter(parameter);
parameterName.staticElement = parameter;
}
//
// The children of this parameter include any parameters defined on the type
//of this parameter.
//
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
ParameterElementImpl element = node.element;
element.metadata = _createElementAnnotations(node.metadata);
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
holder.validate();
return null;
}
@override
Object visitGenericFunctionType(GenericFunctionType node) {
ElementHolder holder = new ElementHolder();
_visitChildren(holder, node);
GenericFunctionTypeElementImpl element =
new GenericFunctionTypeElementImpl.forOffset(node.beginToken.offset);
_setCodeRange(element, node);
element.parameters = holder.parameters;
element.typeParameters = holder.typeParameters;
FunctionType type = new FunctionTypeImpl(element);
element.type = type;
(node as GenericFunctionTypeImpl).type = type;
holder.validate();
return null;
}
@override
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
ParameterElementImpl parameter;
if (node.parent is! DefaultFormalParameter) {
SimpleIdentifier parameterName = node.identifier;
parameter = new ParameterElementImpl.forNode(parameterName);
_setCodeRange(parameter, node);
parameter.isConst = node.isConst;
parameter.isExplicitlyCovariant = node.covariantKeyword != null;
parameter.isFinal = node.isFinal;
parameter.parameterKind = node.kind;
_setParameterVisibleRange(node, parameter);
if (node.type == null) {
parameter.hasImplicitType = true;
}
_currentHolder.addParameter(parameter);
(node as SimpleFormalParameterImpl).element = parameter;
parameterName?.staticElement = parameter;
}
super.visitSimpleFormalParameter(node);
parameter ??= node.element;
parameter?.metadata = _createElementAnnotations(node.metadata);
return null;
}
@override
Object visitTypeParameter(TypeParameter node) {
SimpleIdentifier parameterName = node.name;
TypeParameterElementImpl typeParameter =
new TypeParameterElementImpl.forNode(parameterName);
_setCodeRange(typeParameter, node);
typeParameter.metadata = _createElementAnnotations(node.metadata);
TypeParameterTypeImpl typeParameterType =
new TypeParameterTypeImpl(typeParameter);
typeParameter.type = typeParameterType;
_currentHolder.addTypeParameter(typeParameter);
parameterName.staticElement = typeParameter;
return super.visitTypeParameter(node);
}
/**
* For each [Annotation] found in [annotations], create a new
* [ElementAnnotation] object and set the [Annotation] to point to it.
*/
List<ElementAnnotation> _createElementAnnotations(
NodeList<Annotation> annotations) {
if (annotations.isEmpty) {
return ElementAnnotation.EMPTY_LIST;
}
return annotations.map((Annotation a) {
ElementAnnotationImpl elementAnnotation =
new ElementAnnotationImpl(_unitElement);
a.elementAnnotation = elementAnnotation;
return elementAnnotation;
}).toList();
}
/**
* Return the body of the function that contains the given [parameter], or
* `null` if no function body could be found.
*/
FunctionBody _getFunctionBody(FormalParameter parameter) {
AstNode parent = parameter?.parent?.parent;
if (parent is ConstructorDeclaration) {
return parent.body;
} else if (parent is FunctionExpression) {
return parent.body;
} else if (parent is MethodDeclaration) {
return parent.body;
}
return null;
}
void _setCodeRange(ElementImpl element, AstNode node) {
element.setCodeRange(node.offset, node.length);
}
void _setFieldParameterField(
FormalParameter node, FieldFormalParameterElementImpl element) {}
/**
* Sets the visible source range for formal parameter.
*/
void _setParameterVisibleRange(
FormalParameter node, ParameterElementImpl element) {
FunctionBody body = _getFunctionBody(node);
if (body is BlockFunctionBody || body is ExpressionFunctionBody) {
element.setVisibleRange(body.offset, body.length);
}
}
void _setVariableDeclarationListAnnotations(VariableDeclarationList node,
List<ElementAnnotation> elementAnnotations) {
for (VariableDeclaration variableDeclaration in node.variables) {
ElementImpl element = variableDeclaration.element as ElementImpl;
_setCodeRange(element, node.parent);
element.metadata = elementAnnotations;
}
}
/**
* Make the given holder be the current holder while visiting the given node.
*
* @param holder the holder that will gather elements that are built while visiting the children
* @param node the node to be visited
*/
void _visit(ElementHolder holder, AstNode node) {
if (node != null) {
ElementHolder previousHolder = _currentHolder;
_currentHolder = holder;
try {
node.accept(this);
} finally {
_currentHolder = previousHolder;
}
}
}
/**
* Make the given holder be the current holder while visiting the children of the given node.
*
* @param holder the holder that will gather elements that are built while visiting the children
* @param node the node whose children are to be visited
*/
void _visitChildren(ElementHolder holder, AstNode node) {
if (node != null) {
ElementHolder previousHolder = _currentHolder;
_currentHolder = holder;
try {
node.visitChildren(this);
} finally {
_currentHolder = previousHolder;
}
}
}
}
class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> {
final ApiElementBuilder builder;
List<ClassMember> nonFields;
_ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super();
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
nonFields.add(node);
return null;
}
@override
Object visitMethodDeclaration(MethodDeclaration node) {
nonFields.add(node);
return null;
}
@override
Object visitNode(AstNode node) => node.accept(builder);
}
/**
* Instances of the class [_NamespaceCombinatorBuilder] can be used to visit
* [Combinator] AST nodes and generate [NamespaceCombinator] elements.
*/
class _NamespaceCombinatorBuilder extends SimpleAstVisitor<Object> {
/**
* Elements generated so far.
*/
final List<NamespaceCombinator> combinators = <NamespaceCombinator>[];
@override
Object visitHideCombinator(HideCombinator node) {
HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
hide.hiddenNames = _getIdentifiers(node.hiddenNames);
combinators.add(hide);
return null;
}
@override
Object visitShowCombinator(ShowCombinator node) {
ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
show.offset = node.offset;
show.end = node.end;
show.shownNames = _getIdentifiers(node.shownNames);
combinators.add(show);
return null;
}
/**
* Return the lexical identifiers associated with the given [identifiers].
*/
static List<String> _getIdentifiers(NodeList<SimpleIdentifier> identifiers) {
return identifiers.map((identifier) => identifier.name).toList();
}
}