| // 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/ast/ast.dart'; |
| import 'package:analyzer/dart/constant/value.dart'; |
| import 'package:analyzer/dart/element/element.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'; |
| |
| /** |
| * 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 [baseElement], and applied [substitution]. |
| */ |
| ConstructorMember( |
| ConstructorElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| ConstructorElement get baseElement => super.baseElement as ConstructorElement; |
| |
| @override |
| ClassElement get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| bool get isConst => baseElement.isConst; |
| |
| @override |
| bool get isConstantEvaluated => baseElement.isConstantEvaluated; |
| |
| @override |
| bool get isDefaultConstructor => baseElement.isDefaultConstructor; |
| |
| @override |
| bool get isFactory => baseElement.isFactory; |
| |
| @override |
| int get nameEnd => baseElement.nameEnd; |
| |
| @override |
| int get periodOffset => baseElement.periodOffset; |
| |
| @override |
| ConstructorElement get redirectedConstructor { |
| var definingType = _substitution.substituteType(enclosingElement.type); |
| return from(baseElement.redirectedConstructor, definingType); |
| } |
| |
| @override |
| T accept<T>(ElementVisitor<T> visitor) => |
| visitor.visitConstructorElement(this); |
| |
| @deprecated |
| @override |
| ConstructorDeclaration computeNode() => baseElement.computeNode(); |
| |
| @override |
| String toString() { |
| ConstructorElement baseElement = this.baseElement; |
| List<ParameterElement> parameters = this.parameters; |
| FunctionType type = this.type; |
| |
| StringBuffer buffer = StringBuffer(); |
| if (type != null) { |
| buffer.write(type.returnType); |
| buffer.write(' '); |
| } |
| buffer.write(baseElement.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 [baseElement], and applied |
| * [substitution]. |
| */ |
| ExecutableMember( |
| ExecutableElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| ExecutableElement get baseElement => super.baseElement as ExecutableElement; |
| |
| @override |
| bool get hasImplicitReturnType => baseElement.hasImplicitReturnType; |
| |
| @override |
| bool get isAbstract => baseElement.isAbstract; |
| |
| @override |
| bool get isAsynchronous => baseElement.isAsynchronous; |
| |
| @override |
| bool get isExternal => baseElement.isExternal; |
| |
| @override |
| bool get isGenerator => baseElement.isGenerator; |
| |
| @override |
| bool get isOperator => baseElement.isOperator; |
| |
| @override |
| bool get isSimplyBounded => baseElement.isSimplyBounded; |
| |
| @override |
| bool get isStatic => baseElement.isStatic; |
| |
| @override |
| bool get isSynchronous => baseElement.isSynchronous; |
| |
| @override |
| List<ParameterElement> get parameters { |
| return baseElement.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(baseElement.type); |
| } |
| |
| @override |
| List<TypeParameterElement> get typeParameters { |
| return TypeParameterMember.from2( |
| baseElement.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.baseElement; |
| 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 [baseElement], with applied [substitution]. |
| */ |
| FieldFormalParameterMember( |
| FieldFormalParameterElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| FieldElement get field { |
| var field = (baseElement as FieldFormalParameterElement).field; |
| return FieldMember(field, _substitution); |
| } |
| |
| @override |
| bool get isCovariant => baseElement.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 |
| * [baseElement], with applied [substitution]. |
| */ |
| FieldMember( |
| FieldElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| FieldElement get baseElement => super.baseElement as FieldElement; |
| |
| @override |
| Element get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| PropertyAccessorElement get getter { |
| var baseGetter = baseElement.getter; |
| if (baseGetter == null) { |
| return null; |
| } |
| return PropertyAccessorMember(baseGetter, _substitution); |
| } |
| |
| @override |
| bool get isCovariant => baseElement.isCovariant; |
| |
| @override |
| bool get isEnumConstant => baseElement.isEnumConstant; |
| |
| @override |
| bool get isLate => baseElement.isLate; |
| |
| @deprecated |
| @override |
| bool get isVirtual => baseElement.isVirtual; |
| |
| @deprecated |
| @override |
| DartType get propagatedType => null; |
| |
| @override |
| PropertyAccessorElement get setter { |
| var baseSetter = baseElement.setter; |
| if (baseSetter == null) { |
| return null; |
| } |
| return PropertyAccessorMember(baseSetter, _substitution); |
| } |
| |
| @override |
| T accept<T>(ElementVisitor<T> visitor) => visitor.visitFieldElement(this); |
| |
| @deprecated |
| @override |
| VariableDeclaration computeNode() => baseElement.computeNode(); |
| |
| @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), |
| ); |
| } |
| } |
| |
| /** |
| * 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 _baseElement; |
| |
| /** |
| * 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 |
| * [baseElement], and applied [_substitution]. |
| */ |
| Member(this._baseElement, this._substitution); |
| |
| /** |
| * Return the element on which the parameterized element was created. |
| */ |
| Element get baseElement => _baseElement; |
| |
| @override |
| AnalysisContext get context => _baseElement.context; |
| |
| @override |
| String get displayName => _baseElement.displayName; |
| |
| @override |
| String get documentationComment => _baseElement.documentationComment; |
| |
| @override |
| bool get hasAlwaysThrows => _baseElement.hasAlwaysThrows; |
| |
| @override |
| bool get hasDeprecated => _baseElement.hasDeprecated; |
| |
| @override |
| bool get hasFactory => _baseElement.hasFactory; |
| |
| @override |
| bool get hasIsTest => _baseElement.hasIsTest; |
| |
| @override |
| bool get hasIsTestGroup => _baseElement.hasIsTestGroup; |
| |
| @override |
| bool get hasJS => _baseElement.hasJS; |
| |
| @override |
| bool get hasLiteral => _baseElement.hasLiteral; |
| |
| @override |
| bool get hasMustCallSuper => _baseElement.hasMustCallSuper; |
| |
| @override |
| bool get hasOptionalTypeArgs => _baseElement.hasOptionalTypeArgs; |
| |
| @override |
| bool get hasOverride => _baseElement.hasOverride; |
| |
| @override |
| bool get hasProtected => _baseElement.hasProtected; |
| |
| @override |
| bool get hasRequired => _baseElement.hasRequired; |
| |
| @override |
| bool get hasSealed => _baseElement.hasSealed; |
| |
| @override |
| bool get hasVisibleForTemplate => _baseElement.hasVisibleForTemplate; |
| |
| @override |
| bool get hasVisibleForTesting => _baseElement.hasVisibleForTesting; |
| |
| @override |
| int get id => _baseElement.id; |
| |
| @override |
| bool get isAlwaysThrows => _baseElement.hasAlwaysThrows; |
| |
| @override |
| bool get isDeprecated => _baseElement.hasDeprecated; |
| |
| @override |
| bool get isFactory => _baseElement.hasFactory; |
| |
| @override |
| bool get isJS => _baseElement.hasJS; |
| |
| @override |
| bool get isOverride => _baseElement.hasOverride; |
| |
| @override |
| bool get isPrivate => _baseElement.isPrivate; |
| |
| @override |
| bool get isProtected => _baseElement.hasProtected; |
| |
| @override |
| bool get isPublic => _baseElement.isPublic; |
| |
| @override |
| bool get isRequired => _baseElement.hasRequired; |
| |
| @override |
| bool get isSynthetic => _baseElement.isSynthetic; |
| |
| @override |
| bool get isVisibleForTesting => _baseElement.hasVisibleForTesting; |
| |
| @override |
| ElementKind get kind => _baseElement.kind; |
| |
| @override |
| LibraryElement get library => _baseElement.library; |
| |
| @override |
| Source get librarySource => _baseElement.librarySource; |
| |
| @override |
| ElementLocation get location => _baseElement.location; |
| |
| @override |
| List<ElementAnnotation> get metadata => _baseElement.metadata; |
| |
| @override |
| String get name => _baseElement.name; |
| |
| @override |
| int get nameLength => _baseElement.nameLength; |
| |
| @override |
| int get nameOffset => _baseElement.nameOffset; |
| |
| @override |
| AnalysisSession get session => _baseElement.session; |
| |
| @override |
| Source get source => _baseElement.source; |
| |
| /** |
| * The substitution for type parameters referenced in the base element. |
| */ |
| MapSubstitution get substitution => _substitution; |
| |
| @deprecated |
| @override |
| CompilationUnit get unit => _baseElement.unit; |
| |
| @override |
| String computeDocumentationComment() => documentationComment; |
| |
| @deprecated |
| @override |
| AstNode computeNode() => _baseElement.computeNode(); |
| |
| @override |
| E getAncestor<E extends Element>(Predicate<Element> predicate) => |
| baseElement.getAncestor(predicate); |
| |
| @override |
| String getExtendedDisplayName(String shortName) => |
| _baseElement.getExtendedDisplayName(shortName); |
| |
| @override |
| bool isAccessibleIn(LibraryElement library) => |
| _baseElement.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 |
| * [baseElement], with applied [substitution]. |
| */ |
| MethodMember( |
| MethodElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| MethodElement get baseElement => super.baseElement as MethodElement; |
| |
| @override |
| Element get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| T accept<T>(ElementVisitor<T> visitor) => visitor.visitMethodElement(this); |
| |
| @deprecated |
| @override |
| MethodDeclaration computeNode() => baseElement.computeNode(); |
| |
| @override |
| String toString() { |
| MethodElement baseElement = this.baseElement; |
| List<ParameterElement> parameters = this.parameters; |
| FunctionType type = this.type; |
| |
| StringBuffer buffer = StringBuffer(); |
| if (type != null) { |
| buffer.write(type.returnType); |
| buffer.write(' '); |
| } |
| buffer.write(baseElement.enclosingElement.displayName); |
| buffer.write('.'); |
| buffer.write(baseElement.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 = null; |
| 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), |
| ); |
| } |
| } |
| |
| /** |
| * 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 |
| * [baseElement], with applied [substitution]. If [type] is passed it will |
| * represent the already substituted type. |
| */ |
| ParameterMember( |
| ParameterElement baseElement, |
| MapSubstitution substitution, [ |
| DartType type, |
| ]) : super._(baseElement, substitution, type); |
| |
| @override |
| ParameterElement get baseElement => super.baseElement as ParameterElement; |
| |
| @override |
| String get defaultValueCode => baseElement.defaultValueCode; |
| |
| @override |
| Element get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| int get hashCode => baseElement.hashCode; |
| |
| @override |
| bool get isCovariant => baseElement.isCovariant; |
| |
| @override |
| bool get isInitializingFormal => baseElement.isInitializingFormal; |
| |
| @deprecated |
| @override |
| ParameterKind get parameterKind => baseElement.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.from2( |
| baseElement.typeParameters, |
| _substitution, |
| ); |
| } |
| |
| @override |
| SourceRange get visibleRange => baseElement.visibleRange; |
| |
| @override |
| T accept<T>(ElementVisitor<T> visitor) => visitor.visitParameterElement(this); |
| |
| @deprecated |
| @override |
| FormalParameter computeNode() => baseElement.computeNode(); |
| |
| @override |
| E getAncestor<E extends Element>(Predicate<Element> predicate) { |
| Element element = baseElement.getAncestor(predicate); |
| if (element is ExecutableElement) { |
| return ExecutableMember.from2(element, _substitution) as E; |
| } |
| return element as E; |
| } |
| |
| @override |
| String toString() { |
| ParameterElement baseElement = this.baseElement; |
| String left = ""; |
| String right = ""; |
| while (true) { |
| if (baseElement.isNamed) { |
| left = "{"; |
| right = "}"; |
| } else if (baseElement.isOptionalPositional) { |
| left = "["; |
| right = "]"; |
| } |
| break; |
| } |
| return '$left$type ${baseElement.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 |
| * [baseElement], with applied [substitution]. |
| */ |
| PropertyAccessorMember( |
| PropertyAccessorElement baseElement, |
| MapSubstitution substitution, |
| ) : super(baseElement, substitution); |
| |
| @override |
| PropertyAccessorElement get baseElement => |
| super.baseElement as PropertyAccessorElement; |
| |
| @override |
| PropertyAccessorElement get correspondingGetter { |
| return PropertyAccessorMember( |
| baseElement.correspondingGetter, |
| _substitution, |
| ); |
| } |
| |
| @override |
| PropertyAccessorElement get correspondingSetter { |
| return PropertyAccessorMember( |
| baseElement.correspondingSetter, |
| _substitution, |
| ); |
| } |
| |
| @override |
| Element get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| bool get isGetter => baseElement.isGetter; |
| |
| @override |
| bool get isSetter => baseElement.isSetter; |
| |
| @override |
| PropertyInducingElement get variable { |
| PropertyInducingElement variable = baseElement.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 baseElement = this.baseElement; |
| 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(baseElement.enclosingElement.displayName); |
| builder.write('.'); |
| builder.write(baseElement.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 baseElement, |
| MapSubstitution substitution, this._bound) |
| : super(baseElement, substitution) { |
| _type = TypeParameterTypeImpl(this); |
| } |
| |
| @override |
| TypeParameterElement get baseElement => |
| super.baseElement as TypeParameterElement; |
| |
| @override |
| DartType get bound => _bound; |
| |
| @override |
| Element get enclosingElement => baseElement.enclosingElement; |
| |
| @override |
| int get hashCode => baseElement.hashCode; |
| |
| @override |
| TypeParameterType get type => _type; |
| |
| @override |
| bool operator ==(Object other) => |
| other is TypeParameterMember && baseElement == other.baseElement; |
| |
| @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 |
| String toString() { |
| var buffer = StringBuffer(); |
| appendTo(buffer); |
| return buffer.toString(); |
| } |
| |
| /** |
| * If the given [parameter]'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 parameter member representing |
| * the given parameter. Return the member that was created, or the base |
| * parameter if no member was created. |
| */ |
| static List<TypeParameterElement> from( |
| List<TypeParameterElement> formals, FunctionType definingType) { |
| var substitution = Substitution.fromPairs( |
| definingType.typeParameters, |
| definingType.typeArguments, |
| ); |
| return from2(formals, substitution); |
| } |
| |
| static List<TypeParameterElement> from2( |
| List<TypeParameterElement> formals, |
| MapSubstitution substitution, |
| ) { |
| // Create type formals with specialized bounds. |
| // For example `<U extends T>` where T comes from an outer scope. |
| var newElements = formals.toList(growable: false); |
| var newTypes = List<TypeParameterType>(formals.length); |
| for (int i = 0; i < newElements.length; i++) { |
| var formal = newElements[i]; |
| DartType bound = formal?.bound; |
| if (bound != null) { |
| bound = substitution.substituteType(bound); |
| var member = TypeParameterMember(formal, substitution, bound); |
| newElements[i] = member; |
| } |
| newTypes[i] = newElements[i].type; |
| } |
| |
| // Recursive bounds are allowed too, so make sure these are updated |
| // to refer to any new TypeParameterMember we just made, rather than |
| // the original type parameter |
| var substitution2 = Substitution.fromPairs(formals, newTypes); |
| for (var formal in newElements) { |
| if (formal is TypeParameterMember) { |
| // TODO(jmesserly): this is required so substituting for the |
| // type formal will work. Investigate if there's a better solution. |
| formal._bound = substitution2.substituteType(formal.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 |
| * [baseElement], with applied [substitution]. |
| */ |
| VariableMember( |
| VariableElement baseElement, |
| MapSubstitution substitution, [ |
| DartType type, |
| ]) : _type = type, |
| super(baseElement, substitution); |
| |
| // TODO(jmesserly): this is temporary to allow the ParameterMember subclass. |
| // Apparently mixins don't work with optional params. |
| VariableMember._(VariableElement baseElement, MapSubstitution substitution, |
| [DartType type]) |
| : this(baseElement, substitution, type); |
| |
| @override |
| VariableElement get baseElement => super.baseElement as VariableElement; |
| |
| @override |
| DartObject get constantValue => baseElement.constantValue; |
| |
| @override |
| bool get hasImplicitType => baseElement.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 => baseElement.isConst; |
| |
| @override |
| bool get isConstantEvaluated => baseElement.isConstantEvaluated; |
| |
| @override |
| bool get isFinal => baseElement.isFinal; |
| |
| @override |
| @deprecated |
| bool get isPotentiallyMutatedInClosure => |
| baseElement.isPotentiallyMutatedInClosure; |
| |
| @override |
| @deprecated |
| bool get isPotentiallyMutatedInScope => |
| baseElement.isPotentiallyMutatedInScope; |
| |
| @override |
| bool get isStatic => baseElement.isStatic; |
| |
| @override |
| DartType get type { |
| if (_type != null) return _type; |
| |
| return _type = _substitution.substituteType(baseElement.type); |
| } |
| |
| @override |
| DartObject computeConstantValue() => baseElement.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); |
| baseElement.initializer?.accept(visitor); |
| } |
| } |