blob: e349274d6bd4bda43b4dd402bfccb7657d2958b5 [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.
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:meta/meta.dart';
/**
* A constructor element defined in a parameterized type where the values of the
* type parameters are known.
*/
class ConstructorMember extends ExecutableMember implements ConstructorElement {
/**
* Initialize a newly created element to represent a constructor, based on
* the [declaration], and applied [substitution].
*/
ConstructorMember(
ConstructorElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
ConstructorElement get baseElement => declaration;
@override
ConstructorElement get declaration => super.declaration as ConstructorElement;
@override
ClassElement get enclosingElement => declaration.enclosingElement;
@override
bool get isConst => declaration.isConst;
@override
bool get isConstantEvaluated => declaration.isConstantEvaluated;
@override
bool get isDefaultConstructor => declaration.isDefaultConstructor;
@override
bool get isFactory => declaration.isFactory;
@override
int get nameEnd => declaration.nameEnd;
@override
int get periodOffset => declaration.periodOffset;
@override
ConstructorElement get redirectedConstructor {
var element = this.declaration.redirectedConstructor;
if (element == null) {
return null;
}
ConstructorElement declaration;
MapSubstitution substitution;
if (element is ConstructorMember) {
declaration = element._declaration;
var map = <TypeParameterElement, DartType>{};
var elementMap = element._substitution.map;
for (var typeParameter in elementMap.keys) {
var type = elementMap[typeParameter];
map[typeParameter] = _substitution.substituteType(type);
}
substitution = Substitution.fromMap(map);
} else {
declaration = element;
substitution = _substitution;
}
return ConstructorMember(declaration, substitution);
}
@override
T accept<T>(ElementVisitor<T> visitor) =>
visitor.visitConstructorElement(this);
@override
String toString() {
ConstructorElement declaration = this.declaration;
List<ParameterElement> parameters = this.parameters;
FunctionType type = this.type;
StringBuffer buffer = StringBuffer();
if (type != null) {
buffer.write(type.returnType);
buffer.write(' ');
}
buffer.write(declaration.enclosingElement.displayName);
String name = displayName;
if (name != null && name.isNotEmpty) {
buffer.write('.');
buffer.write(name);
}
buffer.write('(');
int parameterCount = parameters.length;
for (int i = 0; i < parameterCount; i++) {
if (i > 0) {
buffer.write(', ');
}
buffer.write(parameters[i]);
}
buffer.write(')');
return buffer.toString();
}
/**
* If the given [constructor]'s type is different when any type parameters
* from the defining type's declaration are replaced with the actual type
* arguments from the [definingType], create a constructor member representing
* the given constructor. Return the member that was created, or the original
* constructor if no member was created.
*/
static ConstructorElement from(
ConstructorElement constructor, InterfaceType definingType) {
if (constructor == null || definingType.typeArguments.isEmpty) {
return constructor;
}
FunctionType baseType = constructor.type;
if (baseType == null) {
// TODO(brianwilkerson) We need to understand when this can happen.
return constructor;
}
return ConstructorMember(
constructor,
Substitution.fromInterfaceType(definingType),
);
}
}
/**
* An executable element defined in a parameterized type where the values of the
* type parameters are known.
*/
abstract class ExecutableMember extends Member implements ExecutableElement {
FunctionType _type;
/**
* Initialize a newly created element to represent a callable element (like a
* method or function or property), based on the [declaration], and applied
* [substitution].
*/
ExecutableMember(
ExecutableElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
ExecutableElement get baseElement => declaration;
@override
ExecutableElement get declaration => super.declaration as ExecutableElement;
@override
bool get hasImplicitReturnType => declaration.hasImplicitReturnType;
@override
bool get isAbstract => declaration.isAbstract;
@override
bool get isAsynchronous => declaration.isAsynchronous;
@override
bool get isExternal => declaration.isExternal;
@override
bool get isGenerator => declaration.isGenerator;
@override
bool get isOperator => declaration.isOperator;
@override
bool get isSimplyBounded => declaration.isSimplyBounded;
@override
bool get isStatic => declaration.isStatic;
@override
bool get isSynchronous => declaration.isSynchronous;
@override
List<ParameterElement> get parameters {
return declaration.parameters.map((p) {
if (p is FieldFormalParameterElement) {
return FieldFormalParameterMember(p, _substitution);
}
return ParameterMember(p, _substitution);
}).toList();
}
@override
DartType get returnType => type.returnType;
@override
FunctionType get type {
if (_type != null) return _type;
return _type = _substitution.substituteType(declaration.type);
}
@override
List<TypeParameterElement> get typeParameters {
return TypeParameterMember.from(
declaration.typeParameters,
_substitution,
);
}
@override
void visitChildren(ElementVisitor visitor) {
// TODO(brianwilkerson) We need to finish implementing the accessors used
// below so that we can safely invoke them.
super.visitChildren(visitor);
safelyVisitChildren(parameters, visitor);
}
static ExecutableElement from2(
ExecutableElement element,
MapSubstitution substitution,
) {
var combined = substitution;
if (element is ExecutableMember) {
ExecutableMember member = element;
element = member.declaration;
var map = <TypeParameterElement, DartType>{};
map.addAll(member._substitution.map);
map.addAll(substitution.map);
combined = Substitution.fromMap(map);
}
if (combined.map.isEmpty) {
return element;
}
if (element is ConstructorElement) {
return ConstructorMember(element, combined);
} else if (element is MethodElement) {
return MethodMember(element, combined);
} else if (element is PropertyAccessorElement) {
return PropertyAccessorMember(element, combined);
} else {
throw UnimplementedError('(${element.runtimeType}) $element');
}
}
}
/**
* A parameter element defined in a parameterized type where the values of the
* type parameters are known.
*/
class FieldFormalParameterMember extends ParameterMember
implements FieldFormalParameterElement {
/**
* Initialize a newly created element to represent a field formal parameter,
* based on the [declaration], with applied [substitution].
*/
FieldFormalParameterMember(
FieldFormalParameterElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@override
FieldElement get field {
var field = (declaration as FieldFormalParameterElement).field;
if (field == null) {
return null;
}
return FieldMember(field, _substitution);
}
@override
bool get isCovariant => declaration.isCovariant;
@override
T accept<T>(ElementVisitor<T> visitor) =>
visitor.visitFieldFormalParameterElement(this);
}
/**
* A field element defined in a parameterized type where the values of the type
* parameters are known.
*/
class FieldMember extends VariableMember implements FieldElement {
/**
* Initialize a newly created element to represent a field, based on the
* [declaration], with applied [substitution].
*/
FieldMember(
FieldElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
FieldElement get baseElement => declaration;
@override
FieldElement get declaration => super.declaration as FieldElement;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
PropertyAccessorElement get getter {
var baseGetter = declaration.getter;
if (baseGetter == null) {
return null;
}
return PropertyAccessorMember(baseGetter, _substitution);
}
@override
bool get isCovariant => declaration.isCovariant;
@override
bool get isEnumConstant => declaration.isEnumConstant;
@override
PropertyAccessorElement get setter {
var baseSetter = declaration.setter;
if (baseSetter == null) {
return null;
}
return PropertyAccessorMember(baseSetter, _substitution);
}
@override
T accept<T>(ElementVisitor<T> visitor) => visitor.visitFieldElement(this);
@override
String toString() => '$type $displayName';
/**
* If the given [field]'s type is different when any type parameters from the
* defining type's declaration are replaced with the actual type arguments
* from the [definingType], create a field member representing the given
* field. Return the member that was created, or the base field if no member
* was created.
*/
static FieldElement from(FieldElement field, InterfaceType definingType) {
if (field == null || definingType.typeArguments.isEmpty) {
return field;
}
return FieldMember(
field,
Substitution.fromInterfaceType(definingType),
);
}
static FieldElement from2(
FieldElement element,
MapSubstitution substitution,
) {
if (substitution.map.isEmpty) {
return element;
}
return FieldMember(element, substitution);
}
}
/**
* An element defined in a parameterized type where the values of the type
* parameters are known.
*/
abstract class Member implements Element {
/**
* The element on which the parameterized element was created.
*/
final Element _declaration;
/**
* The substitution for type parameters referenced in the base element.
*/
final MapSubstitution _substitution;
/**
* Initialize a newly created element to represent a member, based on the
* [declaration], and applied [_substitution].
*/
Member(this._declaration, this._substitution) {
if (_declaration is Member) {
throw StateError('Members must be created from a declarations.');
}
}
/**
* Return the element on which the parameterized element was created.
*/
@Deprecated('Use Element.declaration instead')
Element get baseElement => _declaration;
@override
AnalysisContext get context => _declaration.context;
@override
Element get declaration => _declaration;
@override
String get displayName => _declaration.displayName;
@override
String get documentationComment => _declaration.documentationComment;
@override
bool get hasAlwaysThrows => _declaration.hasAlwaysThrows;
@override
bool get hasDeprecated => _declaration.hasDeprecated;
@override
bool get hasFactory => _declaration.hasFactory;
@override
bool get hasIsTest => _declaration.hasIsTest;
@override
bool get hasIsTestGroup => _declaration.hasIsTestGroup;
@override
bool get hasJS => _declaration.hasJS;
@override
bool get hasLiteral => _declaration.hasLiteral;
@override
bool get hasMustCallSuper => _declaration.hasMustCallSuper;
@override
bool get hasNonVirtual => _declaration.hasNonVirtual;
@override
bool get hasOptionalTypeArgs => _declaration.hasOptionalTypeArgs;
@override
bool get hasOverride => _declaration.hasOverride;
@override
bool get hasProtected => _declaration.hasProtected;
@override
bool get hasRequired => _declaration.hasRequired;
@override
bool get hasSealed => _declaration.hasSealed;
@override
bool get hasVisibleForTemplate => _declaration.hasVisibleForTemplate;
@override
bool get hasVisibleForTesting => _declaration.hasVisibleForTesting;
@override
int get id => _declaration.id;
@override
bool get isPrivate => _declaration.isPrivate;
@override
bool get isPublic => _declaration.isPublic;
@override
bool get isSynthetic => _declaration.isSynthetic;
@override
ElementKind get kind => _declaration.kind;
@override
LibraryElement get library => _declaration.library;
@override
Source get librarySource => _declaration.librarySource;
@override
ElementLocation get location => _declaration.location;
@override
List<ElementAnnotation> get metadata => _declaration.metadata;
@override
String get name => _declaration.name;
@override
int get nameLength => _declaration.nameLength;
@override
int get nameOffset => _declaration.nameOffset;
@override
AnalysisSession get session => _declaration.session;
@override
Source get source => _declaration.source;
/**
* The substitution for type parameters referenced in the base element.
*/
MapSubstitution get substitution => _substitution;
@override
E getAncestor<E extends Element>(Predicate<Element> predicate) =>
declaration.getAncestor(predicate);
@override
String getExtendedDisplayName(String shortName) =>
_declaration.getExtendedDisplayName(shortName);
@override
bool isAccessibleIn(LibraryElement library) =>
_declaration.isAccessibleIn(library);
/**
* Use the given [visitor] to visit all of the [children].
*/
void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
// TODO(brianwilkerson) Make this private
if (children != null) {
for (Element child in children) {
child.accept(visitor);
}
}
}
@override
void visitChildren(ElementVisitor visitor) {
// There are no children to visit
}
}
/**
* A method element defined in a parameterized type where the values of the type
* parameters are known.
*/
class MethodMember extends ExecutableMember implements MethodElement {
/**
* Initialize a newly created element to represent a method, based on the
* [declaration], with applied [substitution].
*/
MethodMember(
MethodElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
MethodElement get baseElement => declaration;
@override
MethodElement get declaration => super.declaration as MethodElement;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
T accept<T>(ElementVisitor<T> visitor) => visitor.visitMethodElement(this);
@override
String toString() {
MethodElement declaration = this.declaration;
List<ParameterElement> parameters = this.parameters;
FunctionType type = this.type;
StringBuffer buffer = StringBuffer();
if (type != null) {
buffer.write(type.returnType);
buffer.write(' ');
}
buffer.write(declaration.enclosingElement.displayName);
buffer.write('.');
buffer.write(declaration.displayName);
int typeParameterCount = typeParameters.length;
if (typeParameterCount > 0) {
buffer.write('<');
for (int i = 0; i < typeParameterCount; i++) {
if (i > 0) {
buffer.write(', ');
}
// TODO(scheglov) consider always using TypeParameterMember
var typeParameter = typeParameters[i];
if (typeParameter is TypeParameterElementImpl) {
typeParameter.appendTo(buffer);
} else
(typeParameter as TypeParameterMember).appendTo(buffer);
}
buffer.write('>');
}
buffer.write('(');
String closing;
ParameterKind kind = ParameterKind.REQUIRED;
int parameterCount = parameters.length;
for (int i = 0; i < parameterCount; i++) {
if (i > 0) {
buffer.write(', ');
}
ParameterElement parameter = parameters[i];
// ignore: deprecated_member_use_from_same_package
ParameterKind parameterKind = parameter.parameterKind;
if (parameterKind != kind) {
if (closing != null) {
buffer.write(closing);
}
if (parameter.isOptionalPositional) {
buffer.write('[');
closing = ']';
} else if (parameter.isNamed) {
buffer.write('{');
closing = '}';
} else {
closing = null;
}
}
kind = parameterKind;
parameter.appendToWithoutDelimiters(buffer);
}
if (closing != null) {
buffer.write(closing);
}
buffer.write(')');
return buffer.toString();
}
/**
* If the given [method]'s type is different when any type parameters from the
* defining type's declaration are replaced with the actual type arguments
* from the [definingType], create a method member representing the given
* method. Return the member that was created, or the base method if no member
* was created.
*/
static MethodElement from(MethodElement method, InterfaceType definingType) {
if (method == null || definingType.typeArguments.isEmpty) {
return method;
}
return MethodMember(
method,
Substitution.fromInterfaceType(definingType),
);
}
static MethodElement from2(
MethodElement element,
MapSubstitution substitution,
) {
if (substitution.map.isEmpty) {
return element;
}
return MethodMember(element, substitution);
}
}
/**
* A parameter element defined in a parameterized type where the values of the
* type parameters are known.
*/
class ParameterMember extends VariableMember
with ParameterElementMixin
implements ParameterElement {
/**
* Initialize a newly created element to represent a parameter, based on the
* [declaration], with applied [substitution].
*/
ParameterMember(
ParameterElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
ParameterElement get baseElement => declaration;
@override
ParameterElement get declaration => super.declaration as ParameterElement;
@override
String get defaultValueCode => declaration.defaultValueCode;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
int get hashCode => declaration.hashCode;
@override
bool get isCovariant => declaration.isCovariant;
@override
bool get isInitializingFormal => declaration.isInitializingFormal;
@deprecated
@override
ParameterKind get parameterKind => declaration.parameterKind;
@override
List<ParameterElement> get parameters {
DartType type = this.type;
if (type is FunctionType) {
return type.parameters;
}
return const <ParameterElement>[];
}
@override
List<TypeParameterElement> get typeParameters {
return TypeParameterMember.from(
declaration.typeParameters,
_substitution,
);
}
@override
T accept<T>(ElementVisitor<T> visitor) => visitor.visitParameterElement(this);
@override
E getAncestor<E extends Element>(Predicate<Element> predicate) {
Element element = declaration.getAncestor(predicate);
if (element is ExecutableElement) {
return ExecutableMember.from2(element, _substitution) as E;
}
return element as E;
}
@override
String toString() {
ParameterElement declaration = this.declaration;
String left = "";
String right = "";
while (true) {
if (declaration.isNamed) {
left = "{";
right = "}";
} else if (declaration.isOptionalPositional) {
left = "[";
right = "]";
}
break;
}
return '$left$type ${declaration.displayName}$right';
}
@override
void visitChildren(ElementVisitor visitor) {
super.visitChildren(visitor);
safelyVisitChildren(parameters, visitor);
}
}
/**
* A property accessor element defined in a parameterized type where the values
* of the type parameters are known.
*/
class PropertyAccessorMember extends ExecutableMember
implements PropertyAccessorElement {
/**
* Initialize a newly created element to represent a property, based on the
* [declaration], with applied [substitution].
*/
PropertyAccessorMember(
PropertyAccessorElement declaration,
MapSubstitution substitution,
) : super(declaration, substitution);
@deprecated
@override
PropertyAccessorElement get baseElement => declaration;
@override
PropertyAccessorElement get correspondingGetter {
return PropertyAccessorMember(
declaration.correspondingGetter,
_substitution,
);
}
@override
PropertyAccessorElement get correspondingSetter {
return PropertyAccessorMember(
declaration.correspondingSetter,
_substitution,
);
}
@override
PropertyAccessorElement get declaration =>
super.declaration as PropertyAccessorElement;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
bool get isGetter => declaration.isGetter;
@override
bool get isSetter => declaration.isSetter;
@override
PropertyInducingElement get variable {
PropertyInducingElement variable = declaration.variable;
if (variable is FieldElement) {
return FieldMember(variable, _substitution);
}
return variable;
}
@override
T accept<T>(ElementVisitor<T> visitor) =>
visitor.visitPropertyAccessorElement(this);
@override
String toString() {
PropertyAccessorElement declaration = this.declaration;
List<ParameterElement> parameters = this.parameters;
FunctionType type = this.type;
StringBuffer builder = StringBuffer();
if (type != null) {
builder.write(type.returnType);
builder.write(' ');
}
if (isGetter) {
builder.write('get ');
} else {
builder.write('set ');
}
builder.write(declaration.enclosingElement.displayName);
builder.write('.');
builder.write(declaration.displayName);
builder.write('(');
int parameterCount = parameters.length;
for (int i = 0; i < parameterCount; i++) {
if (i > 0) {
builder.write(', ');
}
builder.write(parameters[i]);
}
builder.write(')');
return builder.toString();
}
/**
* If the given [accessor]'s type is different when any type parameters from
* the defining type's declaration are replaced with the actual type
* arguments from the [definingType], create an accessor member representing
* the given accessor. Return the member that was created, or the base
* accessor if no member was created.
*/
static PropertyAccessorElement from(
PropertyAccessorElement accessor, InterfaceType definingType) {
if (accessor == null || definingType.typeArguments.isEmpty) {
return accessor;
}
return PropertyAccessorMember(
accessor,
Substitution.fromInterfaceType(definingType),
);
}
}
/**
* A type parameter defined inside of another parameterized type, where the
* values of the enclosing type parameters are known.
*
* For example:
*
* class C<T> {
* S m<S extends T>(S s);
* }
*
* If we have `C<num>.m` and we ask for the type parameter "S", we should get
* `<S extends num>` instead of `<S extends T>`. This is how the parameter
* and return types work, see: [FunctionType.parameters],
* [FunctionType.returnType], and [ParameterMember].
*/
class TypeParameterMember extends Member implements TypeParameterElement {
DartType _bound;
DartType _type;
TypeParameterMember(TypeParameterElement declaration,
MapSubstitution substitution, this._bound)
: super(declaration, substitution) {
_type = TypeParameterTypeImpl(this);
}
@deprecated
@override
TypeParameterElement get baseElement => declaration;
@override
DartType get bound => _bound;
@override
TypeParameterElement get declaration =>
super.declaration as TypeParameterElement;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
int get hashCode => declaration.hashCode;
@override
TypeParameterType get type => _type;
@override
bool operator ==(Object other) {
return declaration == other;
}
@override
T accept<T>(ElementVisitor<T> visitor) =>
visitor.visitTypeParameterElement(this);
void appendTo(StringBuffer buffer) {
buffer.write(displayName);
if (bound != null) {
buffer.write(" extends ");
buffer.write(bound);
}
}
@override
TypeParameterType instantiate({
@required NullabilitySuffix nullabilitySuffix,
}) {
return TypeParameterTypeImpl(this, nullabilitySuffix: nullabilitySuffix);
}
@override
String toString() {
var buffer = StringBuffer();
appendTo(buffer);
return buffer.toString();
}
static List<TypeParameterElement> from(
List<TypeParameterElement> elements,
MapSubstitution substitution,
) {
if (substitution.map.isEmpty) {
return elements;
}
// Create type formals with specialized bounds.
// For example `<U extends T>` where T comes from an outer scope.
var newElements = List<TypeParameterElement>(elements.length);
var newTypes = List<TypeParameterType>(elements.length);
for (int i = 0; i < newElements.length; i++) {
var element = elements[i];
var bound = element.bound;
if (bound != null) {
bound = substitution.substituteType(bound);
element = TypeParameterMember(element, substitution, bound);
}
newElements[i] = element;
newTypes[i] = newElements[i].instantiate(
nullabilitySuffix: NullabilitySuffix.none,
);
}
// Update bounds to reference new TypeParameterMember(s).
var substitution2 = Substitution.fromPairs(elements, newTypes);
for (var newElement in newElements) {
if (newElement is TypeParameterMember) {
newElement._bound = substitution2.substituteType(newElement.bound);
}
}
return newElements;
}
}
/**
* A variable element defined in a parameterized type where the values of the
* type parameters are known.
*/
abstract class VariableMember extends Member implements VariableElement {
DartType _type;
/**
* Initialize a newly created element to represent a variable, based on the
* [declaration], with applied [substitution].
*/
VariableMember(VariableElement declaration, MapSubstitution substitution)
: super(declaration, substitution);
@deprecated
@override
VariableElement get baseElement => declaration;
@override
DartObject get constantValue => declaration.constantValue;
@override
VariableElement get declaration => super.declaration as VariableElement;
@override
bool get hasImplicitType => declaration.hasImplicitType;
@override
FunctionElement get initializer {
//
// Elements within this element should have type parameters substituted,
// just like this element.
//
throw UnsupportedError('initializer');
// return getBaseElement().getInitializer();
}
@override
bool get isConst => declaration.isConst;
@override
bool get isConstantEvaluated => declaration.isConstantEvaluated;
@override
bool get isFinal => declaration.isFinal;
@override
bool get isLate => declaration.isLate;
@override
bool get isStatic => declaration.isStatic;
@override
DartType get type {
if (_type != null) return _type;
return _type = _substitution.substituteType(declaration.type);
}
@override
DartObject computeConstantValue() => declaration.computeConstantValue();
@override
void visitChildren(ElementVisitor visitor) {
// TODO(brianwilkerson) We need to finish implementing the accessors used
// below so that we can safely invoke them.
super.visitChildren(visitor);
declaration.initializer?.accept(visitor);
}
}