blob: 29bd5d9bee4db9be0a2f2bc25d8b0980df2b8e0b [file] [log] [blame] [edit]
// Copyright (c) 2024, 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.
part of '../../ast.dart';
// ------------------------------------------------------------------------
// EXPRESSIONS
// ------------------------------------------------------------------------
sealed class Expression extends TreeNode {
/// Returns the static type of the expression.
///
/// This calls `StaticTypeContext.getExpressionType` which calls
/// [getStaticTypeInternal] to compute the type of not already cached in
/// [context].
DartType getStaticType(StaticTypeContext context) {
return context.getExpressionType(this);
}
/// Computes the static type of this expression.
///
/// This is called by `StaticTypeContext.getExpressionType` if the static
/// type of this expression is not already cached in [context].
DartType getStaticTypeInternal(StaticTypeContext context);
/// Returns the static type of the expression as an instantiation of
/// [superclass].
///
/// Shouldn't be used on code compiled in legacy mode, as this method assumes
/// the IR is strongly typed.
///
/// This method furthermore assumes that the type of the expression actually
/// is a subtype of (some instantiation of) the given [superclass].
/// If this is not the case, either an exception is thrown or the raw type of
/// [superclass] is returned.
InterfaceType getStaticTypeAsInstanceOf(
Class superclass, StaticTypeContext context) {
// This method assumes the program is correctly typed, so if the superclass
// is not generic, we can just return its raw type without computing the
// type of this expression. It also ensures that all types are considered
// subtypes of Object (not just interface types), and function types are
// considered subtypes of Function.
if (superclass.typeParameters.isEmpty) {
return context.typeEnvironment.coreTypes
.rawType(superclass, context.nonNullable);
}
DartType type = getStaticType(context).nonTypeParameterBound;
if (type is NullType) {
return context.typeEnvironment.coreTypes
.bottomInterfaceType(superclass, context.nullable);
} else if (type is NeverType) {
return context.typeEnvironment.coreTypes
.bottomInterfaceType(superclass, type.nullability);
}
if (type is TypeDeclarationType) {
List<DartType>? upcastTypeArguments = context.typeEnvironment
.getTypeArgumentsAsInstanceOf(type, superclass);
if (upcastTypeArguments != null) {
return new InterfaceType(
superclass, type.nullability, upcastTypeArguments);
}
}
// The static type of this expression is not a subtype of [superclass]. The
// means that the static type of this expression is not the same as when
// the parent [PropertyGet] or [MethodInvocation] was created.
//
// For instance when cloning generic mixin methods, the substitution can
// render some of the code paths as dead code:
//
// mixin M<T> {
// int method(T t) => t is String ? t.length : 0;
// }
// class C with M<int> {}
//
// The mixin transformation will clone the `M.method` method into the
// unnamed mixin application for `Object&M<int>` as this:
//
// int method(int t) => t is String ? t.length : 0;
//
// Now `t.length`, which was originally an access to `String.length` on a
// receiver of type `T & String`, is an access to `String.length` on `int`.
// When computing the static type of `t.length` we will try to compute the
// type of `int` as an instance of `String`, and we do not find it to be
// an instance of `String`.
//
// To resolve this case we compute the type of `t.length` to be the type
// as if accessed on an unknown subtype `String`.
return context.typeEnvironment.coreTypes
.rawType(superclass, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg);
int get precedence => astToText.Precedence.of(this);
@override
String toText(AstTextStrategy strategy) {
AstPrinter printer = new AstPrinter(strategy);
printer.writeExpression(this);
return printer.getText();
}
}
/// Abstract subclass of [Expression] that can be used to add [Expression]
/// subclasses from outside `package:kernel`.
abstract class AuxiliaryExpression extends Expression {
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitAuxiliaryExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAuxiliaryExpression(this, arg);
}
/// An expression containing compile-time errors.
///
/// Should throw a runtime error when evaluated.
///
/// The [fileOffset] of an [InvalidExpression] indicates the location in the
/// tree where the expression occurs, rather than the location of the error.
class InvalidExpression extends Expression {
// TODO(johnniwinther): Avoid using `null` as the empty string.
String? message;
/// The expression containing the error.
Expression? expression;
InvalidExpression(this.message, [this.expression]) {
expression?.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
const NeverType.nonNullable();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInvalidExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInvalidExpression(this, arg);
@override
void visitChildren(Visitor v) {
expression?.accept(v);
}
@override
void transformChildren(Transformer v) {
if (expression != null) {
expression = v.transform(expression!);
expression?.parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (expression != null) {
expression = v.transformOrRemoveExpression(expression!);
expression?.parent = this;
}
}
@override
String toString() {
return "InvalidExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('<invalid:');
printer.write(message ?? '');
if (expression != null) {
printer.write(', ');
printer.writeExpression(expression!);
}
printer.write('>');
}
}
/// Read a local variable, a local function, or a function parameter.
class VariableGet extends Expression {
VariableDeclaration variable;
DartType? promotedType; // Null if not promoted.
VariableGet(this.variable, [this.promotedType]);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return promotedType ?? variable.type;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitVariableGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitVariableGet(this, arg);
@override
void visitChildren(Visitor v) {
promotedType?.accept(v);
}
@override
void transformChildren(Transformer v) {
if (promotedType != null) {
promotedType = v.visitDartType(promotedType!);
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (promotedType != null) {
DartType newPromotedType = v.visitDartType(promotedType!, dummyDartType);
if (identical(newPromotedType, dummyDartType)) {
promotedType = null;
} else {
promotedType = newPromotedType;
}
}
}
@override
String toString() {
return "VariableGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(printer.getVariableName(variable));
if (promotedType != null) {
printer.write('{');
printer.writeType(promotedType!);
printer.write('}');
}
}
}
/// Assign a local variable or function parameter.
///
/// Evaluates to the value of [value].
class VariableSet extends Expression {
VariableDeclaration variable;
Expression value;
VariableSet(this.variable, this.value) {
value.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitVariableSet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitVariableSet(this, arg);
@override
void visitChildren(Visitor v) {
value.accept(v);
}
@override
void transformChildren(Transformer v) {
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "VariableSet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(printer.getVariableName(variable));
printer.write(' = ');
printer.writeExpression(value);
}
}
class RecordIndexGet extends Expression {
Expression receiver;
RecordType receiverType;
final int index;
RecordIndexGet(this.receiver, this.receiverType, this.index)
: assert(0 <= index && index < receiverType.positional.length) {
receiver.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
assert(index < receiverType.positional.length);
return receiverType.positional[index];
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitRecordIndexGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitRecordIndexGet(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
receiverType.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver)..parent = this;
receiverType = v.visitDartType(receiverType) as RecordType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver)..parent = this;
receiverType =
v.visitDartType(receiverType, cannotRemoveSentinel) as RecordType;
}
@override
String toString() {
return "RecordIndexGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver);
printer.write(".\$${index + 1}");
}
}
class RecordNameGet extends Expression {
Expression receiver;
RecordType receiverType;
final String name;
RecordNameGet(this.receiver, this.receiverType, this.name)
: assert(receiverType.named
.singleWhere((element) => element.name == name)
.name ==
name) {
receiver.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
DartType? result;
for (NamedType namedType in receiverType.named) {
if (namedType.name == name) {
result = namedType.type;
break;
}
}
assert(result != null);
return result!;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitRecordNameGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitRecordNameGet(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
receiverType.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver)..parent = this;
receiverType = v.visitDartType(receiverType) as RecordType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver)..parent = this;
receiverType =
v.visitDartType(receiverType, cannotRemoveSentinel) as RecordType;
}
@override
String toString() {
return "RecordNameGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver);
printer.write(".${name}");
}
}
enum DynamicAccessKind {
/// An access on a receiver of type dynamic.
///
/// An access of this kind always results in a value of static type dynamic.
///
/// Valid accesses to Object members on receivers of type dynamic are encoded
/// as an [InstanceInvocation] of kind [InstanceAccessKind.Object].
Dynamic,
/// An access on a receiver of type Never.
///
/// An access of this kind always results in a value of static type Never.
///
/// Valid accesses to Object members on receivers of type Never are also
/// encoded as [DynamicInvocation] of kind [DynamicAccessKind.Never] and _not_
/// as an [InstanceInvocation] of kind [InstanceAccessKind.Object].
Never,
/// An access on a receiver of an invalid type.
///
/// An access of this kind always results in a value of an invalid static
/// type.
Invalid,
/// An access of an unresolved target.
///
/// An access of this kind always results in a value of an invalid static
/// type.
Unresolved,
}
class DynamicGet extends Expression {
final DynamicAccessKind kind;
Expression receiver;
Name name;
DynamicGet(this.kind, this.receiver, this.name) {
receiver.parent = this;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitDynamicGet(this, arg);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
switch (kind) {
case DynamicAccessKind.Dynamic:
return const DynamicType();
case DynamicAccessKind.Never:
return const NeverType.nonNullable();
case DynamicAccessKind.Invalid:
case DynamicAccessKind.Unresolved:
return const InvalidType();
}
}
@override
void visitChildren(Visitor v) {
receiver.accept(v);
name.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
}
@override
String toString() {
return "DynamicGet($kind,${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeName(name);
}
}
/// A property read of an instance getter or field with a statically known
/// interface target.
class InstanceGet extends Expression {
final InstanceAccessKind kind;
Expression receiver;
// TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead?
Name name;
/// The static type of result of the property read.
///
/// This includes substituted type parameters from the static receiver type.
///
/// For instance
///
/// class A<T> {
/// T get t;
/// }
/// m(A<String> a) {
/// a.t; // The result type is `String`.
/// }
///
DartType resultType;
Reference interfaceTargetReference;
InstanceGet(InstanceAccessKind kind, Expression receiver, Name name,
{required Member interfaceTarget, required DartType resultType})
: this.byReference(kind, receiver, name,
interfaceTargetReference:
getNonNullableMemberReferenceGetter(interfaceTarget),
resultType: resultType);
InstanceGet.byReference(this.kind, this.receiver, this.name,
{required this.interfaceTargetReference, required this.resultType}) {
receiver.parent = this;
}
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) => resultType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceGet(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
interfaceTarget.acceptReference(v);
name.accept(v);
resultType.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
resultType = v.visitDartType(resultType);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
resultType = v.visitDartType(resultType, cannotRemoveSentinel);
}
@override
String toString() {
return "InstanceGet($kind,${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
}
}
/// A tear-off of the 'call' method on an expression whose static type is
/// a function type or the type 'Function'.
class FunctionTearOff extends Expression {
Expression receiver;
FunctionTearOff(this.receiver) {
receiver.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
receiver.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitFunctionTearOff(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
}
@override
String toString() {
return "FunctionTearOff(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeName(Name.callName);
}
}
/// A tear-off of an instance method with a statically known interface target.
class InstanceTearOff extends Expression {
final InstanceAccessKind kind;
Expression receiver;
// TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead?
Name name;
/// The static type of result of the tear-off.
///
/// This includes substituted type parameters from the static receiver type.
///
/// For instance
///
/// class A<T, S> {
/// T method<U>(S s, U u) { ... }
/// }
/// m(A<String, int> a) {
/// a.method; // The result type is `String Function<U>(int, U)`.
/// }
///
DartType resultType;
Reference interfaceTargetReference;
InstanceTearOff(InstanceAccessKind kind, Expression receiver, Name name,
{required Procedure interfaceTarget, required DartType resultType})
: this.byReference(kind, receiver, name,
interfaceTargetReference:
getNonNullableMemberReferenceGetter(interfaceTarget),
resultType: resultType);
InstanceTearOff.byReference(this.kind, this.receiver, this.name,
{required this.interfaceTargetReference, required this.resultType}) {
receiver.parent = this;
}
Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
void set interfaceTarget(Procedure procedure) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(procedure);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) => resultType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceTearOff(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
interfaceTarget.acceptReference(v);
name.accept(v);
resultType.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
resultType = v.visitDartType(resultType);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
resultType = v.visitDartType(resultType, cannotRemoveSentinel);
}
@override
String toString() {
return "InstanceTearOff($kind, ${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
}
}
class DynamicSet extends Expression {
final DynamicAccessKind kind;
Expression receiver;
Name name;
Expression value;
DynamicSet(this.kind, this.receiver, this.name, this.value) {
receiver.parent = this;
value.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicSet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitDynamicSet(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
name.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "DynamicSet($kind,${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeName(name);
printer.write(' = ');
printer.writeExpression(value);
}
}
/// An property write of an instance setter or field with a statically known
/// interface target.
class InstanceSet extends Expression {
final InstanceAccessKind kind;
Expression receiver;
// TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead?
Name name;
Expression value;
Reference interfaceTargetReference;
InstanceSet(
InstanceAccessKind kind, Expression receiver, Name name, Expression value,
{required Member interfaceTarget})
: this.byReference(kind, receiver, name, value,
interfaceTargetReference:
getNonNullableMemberReferenceSetter(interfaceTarget));
InstanceSet.byReference(this.kind, this.receiver, this.name, this.value,
{required this.interfaceTargetReference}) {
receiver.parent = this;
value.parent = this;
}
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceSetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceSet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceSet(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
interfaceTarget.acceptReference(v);
name.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "InstanceSet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.write(' = ');
printer.writeExpression(value);
}
}
/// Expression of form `super.foo` occurring in a mixin declaration.
///
/// In this setting, the target is looked up on the types in the mixin 'on'
/// clause and are therefore not necessary the runtime targets of the read. An
/// [AbstractSuperPropertyGet] must be converted into a [SuperPropertyGet] to
/// statically bind the target.
///
/// For instance
///
/// abstract class Interface {
/// get getter;
/// }
/// mixin Mixin on Interface {
/// get getter {
/// // This is an [AbstractSuperPropertyGet] with interface target
/// // `Interface.getter`.
/// return super.getter;
/// }
/// }
/// class Super implements Interface {
/// // This is the target when `Mixin` is applied to `Class`.
/// get getter => 42;
/// }
/// class Class extends Super with Mixin {}
///
/// This may invoke a getter, read a field, or tear off a method.
class AbstractSuperPropertyGet extends Expression {
Name name;
Reference interfaceTargetReference;
AbstractSuperPropertyGet(Name name, Member interfaceTarget)
: this.byReference(
name, getNonNullableMemberReferenceGetter(interfaceTarget));
AbstractSuperPropertyGet.byReference(
this.name, this.interfaceTargetReference);
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
Class declaringClass = interfaceTarget.enclosingClass!;
if (declaringClass.typeParameters.isEmpty) {
return interfaceTarget.getterType;
}
List<DartType>? receiverArguments = context.typeEnvironment
.getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass);
return Substitution.fromPairs(
declaringClass.typeParameters, receiverArguments!)
.substituteType(interfaceTarget.getterType);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertyGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAbstractSuperPropertyGet(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "AbstractSuperPropertyGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.{abstract}');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
}
}
/// Expression of form `super.field`.
///
/// This may invoke a getter, read a field, or tear off a method.
class SuperPropertyGet extends Expression {
Name name;
Reference interfaceTargetReference;
SuperPropertyGet(Name name, Member interfaceTarget)
: this.byReference(
name, getNonNullableMemberReferenceGetter(interfaceTarget));
SuperPropertyGet.byReference(this.name, this.interfaceTargetReference);
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
Class declaringClass = interfaceTarget.enclosingClass!;
if (declaringClass.typeParameters.isEmpty) {
return interfaceTarget.getterType;
}
List<DartType>? receiverArguments = context.typeEnvironment
.getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass);
return Substitution.fromPairs(
declaringClass.typeParameters, receiverArguments!)
.substituteType(interfaceTarget.getterType);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertyGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSuperPropertyGet(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "SuperPropertyGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
}
}
/// Expression of form `super.foo = x` occurring in a mixin declaration.
///
/// In this setting, the target is looked up on the types in the mixin 'on'
/// clause and are therefore not necessary the runtime targets of the
/// assignment. An [AbstractSuperPropertySet] must be converted into a
/// [SuperPropertySet] to statically bind the target.
///
/// For instance
///
/// abstract class Interface {
/// void set setter(value);
/// }
/// mixin Mixin on Interface {
/// void set setter(value) {
/// // This is an [AbstractSuperPropertySet] with interface target
/// // `Interface.setter`.
/// super.setter = value;
/// }
/// }
/// class Super implements Interface {
/// // This is the target when `Mixin` is applied to `Class`.
/// void set setter(value) {}
/// }
/// class Class extends Super with Mixin {}
///
/// This may invoke a setter or assign a field.
class AbstractSuperPropertySet extends Expression {
Name name;
Expression value;
Reference interfaceTargetReference;
AbstractSuperPropertySet(Name name, Expression value, Member interfaceTarget)
: this.byReference(
name, value, getNonNullableMemberReferenceSetter(interfaceTarget));
AbstractSuperPropertySet.byReference(
this.name, this.value, this.interfaceTargetReference) {
value.parent = this;
}
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceSetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertySet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAbstractSuperPropertySet(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "AbstractSuperPropertySet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.{abstract}');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.write(' = ');
printer.writeExpression(value);
}
}
/// Expression of form `super.field = value`.
///
/// This may invoke a setter or assign a field.
///
/// Evaluates to the value of [value].
class SuperPropertySet extends Expression {
Name name;
Expression value;
Reference interfaceTargetReference;
SuperPropertySet(Name name, Expression value, Member interfaceTarget)
: this.byReference(
name, value, getNonNullableMemberReferenceSetter(interfaceTarget));
SuperPropertySet.byReference(
this.name, this.value, this.interfaceTargetReference) {
value.parent = this;
}
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member member) {
interfaceTargetReference = getNonNullableMemberReferenceSetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertySet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSuperPropertySet(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "SuperPropertySet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.write(' = ');
printer.writeExpression(value);
}
}
/// Read a static field, call a static getter, or tear off a static method.
class StaticGet extends Expression {
/// A static field, getter, or method (for tear-off).
Reference targetReference;
StaticGet(Member target)
: assert(target is Field || (target is Procedure && target.isGetter)),
this.targetReference = getNonNullableMemberReferenceGetter(target);
StaticGet.byReference(this.targetReference);
Member get target => targetReference.asMember;
void set target(Member target) {
targetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
target.getterType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStaticGet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStaticGet(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "StaticGet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
}
}
/// Tear-off of a static method.
class StaticTearOff extends Expression {
Reference targetReference;
StaticTearOff(Procedure target)
: assert(target.isStatic, "Unexpected static tear off target: $target"),
assert(target.kind == ProcedureKind.Method,
"Unexpected static tear off target: $target"),
this.targetReference = getNonNullableMemberReferenceGetter(target);
StaticTearOff.byReference(this.targetReference);
Procedure get target => targetReference.asProcedure;
void set target(Procedure target) {
targetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
target.getterType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStaticTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStaticTearOff(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "StaticTearOff(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
}
}
/// Assign a static field or call a static setter.
///
/// Evaluates to the value of [value].
class StaticSet extends Expression {
/// A mutable static field or a static setter.
Reference targetReference;
Expression value;
StaticSet(Member target, Expression value)
: this.byReference(getNonNullableMemberReferenceSetter(target), value);
StaticSet.byReference(this.targetReference, this.value) {
value.parent = this;
}
Member get target => targetReference.asMember;
void set target(Member target) {
targetReference = getNonNullableMemberReferenceSetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStaticSet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStaticSet(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "StaticSet(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
printer.write(' = ');
printer.writeExpression(value);
}
}
/// The arguments to a function call, divided into type arguments,
/// positional arguments, and named arguments.
class Arguments extends TreeNode {
final List<DartType> types;
final List<Expression> positional;
List<NamedExpression> named;
Arguments(this.positional,
{List<DartType>? types, List<NamedExpression>? named})
: this.types = types ?? <DartType>[],
this.named = named ?? <NamedExpression>[] {
setParents(this.positional, this);
setParents(this.named, this);
}
Arguments.empty()
: types = <DartType>[],
positional = <Expression>[],
named = <NamedExpression>[];
factory Arguments.forwarded(FunctionNode function, Library library) {
return new Arguments(
function.positionalParameters
.map<Expression>((p) => new VariableGet(p))
.toList(),
named: function.namedParameters
.map((p) => new NamedExpression(p.name!, new VariableGet(p)))
.toList(),
types: function.typeParameters
.map<DartType>(
(p) => new TypeParameterType.withDefaultNullability(p))
.toList());
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitArguments(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitArguments(this, arg);
@override
void visitChildren(Visitor v) {
visitList(types, v);
visitList(positional, v);
visitList(named, v);
}
@override
void transformChildren(Transformer v) {
v.transformDartTypeList(types);
v.transformList(positional, this);
v.transformList(named, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformDartTypeList(types);
v.transformExpressionList(positional, this);
v.transformNamedExpressionList(named, this);
}
@override
String toString() {
return "Arguments(${toStringInternal()})";
}
@override
String toText(AstTextStrategy strategy) {
AstPrinter printer = new AstPrinter(strategy);
printer.writeArguments(this);
return printer.getText();
}
@override
void toTextInternal(AstPrinter printer, {bool includeTypeArguments = true}) {
if (includeTypeArguments) {
printer.writeTypeArguments(types);
}
printer.write('(');
for (int index = 0; index < positional.length; index++) {
if (index > 0) {
printer.write(', ');
}
printer.writeExpression(positional[index]);
}
if (named.isNotEmpty) {
if (positional.isNotEmpty) {
printer.write(', ');
}
for (int index = 0; index < named.length; index++) {
if (index > 0) {
printer.write(', ');
}
printer.writeNamedExpression(named[index]);
}
}
printer.write(')');
}
}
/// A named argument, `name: value`.
class NamedExpression extends TreeNode {
String name;
Expression value;
NamedExpression(this.name, this.value) {
value.parent = this;
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitNamedExpression(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitNamedExpression(this, arg);
@override
void visitChildren(Visitor v) {
value.accept(v);
}
@override
void transformChildren(Transformer v) {
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "NamedExpression(${toStringInternal()})";
}
@override
String toText(AstTextStrategy strategy) {
AstPrinter printer = new AstPrinter(strategy);
toTextInternal(printer);
return printer.getText();
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(name);
printer.write(': ');
printer.writeExpression(value);
}
}
/// Common super class for [DirectMethodInvocation], [MethodInvocation],
/// [SuperMethodInvocation], [StaticInvocation], and [ConstructorInvocation].
abstract class InvocationExpression extends Expression {
Arguments get arguments;
/// Name of the invoked method.
Name get name;
}
abstract class InstanceInvocationExpression extends InvocationExpression {
Expression get receiver;
}
class DynamicInvocation extends InstanceInvocationExpression {
// Must match serialized bit positions.
static const int FlagImplicitCall = 1 << 0;
final DynamicAccessKind kind;
@override
Expression receiver;
@override
Name name;
@override
Arguments arguments;
int flags = 0;
DynamicInvocation(this.kind, this.receiver, this.name, this.arguments) {
receiver.parent = this;
arguments.parent = this;
}
/// If `true` this is an implicit call to 'call'. For instance
///
/// method(dynamic d) {
/// d(); // Implicit call.
/// d.call(); // Explicit call.
///
bool get isImplicitCall => flags & FlagImplicitCall != 0;
void set isImplicitCall(bool value) {
flags = value ? (flags | FlagImplicitCall) : (flags & ~FlagImplicitCall);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
switch (kind) {
case DynamicAccessKind.Dynamic:
return const DynamicType();
case DynamicAccessKind.Never:
return const NeverType.nonNullable();
case DynamicAccessKind.Invalid:
case DynamicAccessKind.Unresolved:
return const InvalidType();
}
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitDynamicInvocation(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
name.accept(v);
arguments.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
String toString() {
return "DynamicInvocation($kind,${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
if (!isImplicitCall) {
printer.write('.');
printer.writeName(name);
}
printer.writeArguments(arguments);
}
}
/// Access kind used by [InstanceInvocation], [InstanceGet], [InstanceSet],
/// and [InstanceTearOff].
enum InstanceAccessKind {
/// An access to a member on a static receiver type which is an interface
/// type.
///
/// In null safe libraries the static receiver type is non-nullable.
///
/// For instance:
///
/// class C { void method() {} }
/// main() => new C().method();
///
Instance,
/// An access to a member defined on Object on a static receiver type that
/// is either a non-interface type or a nullable type.
///
/// For instance:
///
/// test1(String? s) => s.toString();
/// test1(dynamic s) => s.hashCode;
///
Object,
/// An access to a method on a static receiver type which is an interface
/// type which is inapplicable, that is, whose arguments don't match the
/// required parameter structure.
///
/// This is an error case which is only used on expression nested within
/// [InvalidExpression]s.
///
/// For instance:
///
/// class C { void method() {} }
/// main() => new C().method(0); // Too many arguments.
///
Inapplicable,
/// An access to a non-Object member on a static receiver type which is a
/// nullable interface type.
///
/// This is an error case which is only used on expression nested within
/// [InvalidExpression]s.
///
/// For instance:
///
/// class C { void method() {} }
/// test(C? c) => c.method(0); // 'c' is nullable.
///
Nullable,
}
/// An invocation of an instance method with a statically known interface
/// target.
class InstanceInvocation extends InstanceInvocationExpression {
// Must match serialized bit positions.
static const int FlagInvariant = 1 << 0;
static const int FlagBoundsSafe = 1 << 1;
final InstanceAccessKind kind;
@override
Expression receiver;
// TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead?
@override
Name name;
@override
Arguments arguments;
int flags = 0;
/// The static type of the invocation.
///
/// This includes substituted type parameters from the static receiver type
/// and generic type arguments.
///
/// For instance
///
/// class A<T> {
/// Map<T, S> map<S>(S s) { ... }
/// }
/// m(A<String> a) {
/// a.map(0); // The function type is `Map<String, int> Function(int)`.
/// }
///
FunctionType functionType;
Reference interfaceTargetReference;
InstanceInvocation(InstanceAccessKind kind, Expression receiver, Name name,
Arguments arguments,
{required Procedure interfaceTarget, required FunctionType functionType})
: this.byReference(kind, receiver, name, arguments,
interfaceTargetReference:
getNonNullableMemberReferenceGetter(interfaceTarget),
functionType: functionType);
InstanceInvocation.byReference(
this.kind, this.receiver, this.name, this.arguments,
{required this.interfaceTargetReference, required this.functionType})
: assert(functionType.typeParameters.isEmpty) {
receiver.parent = this;
arguments.parent = this;
}
Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
void set interfaceTarget(Procedure target) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
}
/// If `true`, this call is known to be safe wrt. parameter covariance checks.
///
/// This is for instance the case in code patterns like this
///
/// List<int> list = <int>[];
/// list.add(0);
///
/// where the `list` variable is known to hold a value of the same type as
/// the static type. In contrast the would not be the case in code patterns
/// like this
///
/// List<num> list = <double>[];
/// list.add(0); // Runtime error `int` is not a subtype of `double`.
///
bool get isInvariant => flags & FlagInvariant != 0;
void set isInvariant(bool value) {
flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant);
}
/// If `true`, this call is known to be safe wrt. parameter covariance checks.
///
/// This is for instance the case in code patterns like this
///
/// List list = new List.filled(2, 0);
/// list[1] = 42;
///
/// where the `list` is known to have a sufficient length for the update
/// in `list[1] = 42`.
bool get isBoundsSafe => flags & FlagBoundsSafe != 0;
void set isBoundsSafe(bool value) {
flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
functionType.returnType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceInvocation(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
functionType.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
functionType = v.visitDartType(functionType) as FunctionType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
functionType =
v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType;
}
@override
String toString() {
return "InstanceInvocation($kind, ${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.writeArguments(arguments);
}
}
/// An invocation of an instance getter or field with a statically known
/// interface target.
///
/// This is used only for web backend in order to support invocation of
/// native properties as functions. This node will be removed when this
/// invocation style is no longer supported.
class InstanceGetterInvocation extends InstanceInvocationExpression {
// Must match serialized bit positions.
static const int FlagInvariant = 1 << 0;
static const int FlagBoundsSafe = 1 << 1;
final InstanceAccessKind kind;
@override
Expression receiver;
@override
Name name;
@override
Arguments arguments;
int flags = 0;
/// The static type of the invocation, or `dynamic` is of the type is unknown.
///
/// This includes substituted type parameters from the static receiver type
/// and generic type arguments.
///
/// For instance
///
/// class A<T> {
/// Map<T, S> Function<S>(S) get map => ...
/// dynamic get dyn => ...
/// }
/// m(A<String> a) {
/// a.map(0); // The function type is `Map<String, int> Function(int)`.
/// a.dyn(0); // The function type is `null`.
/// }
///
FunctionType? functionType;
Reference interfaceTargetReference;
InstanceGetterInvocation(InstanceAccessKind kind, Expression receiver,
Name name, Arguments arguments,
{required Member interfaceTarget, required FunctionType? functionType})
: this.byReference(kind, receiver, name, arguments,
interfaceTargetReference:
getNonNullableMemberReferenceGetter(interfaceTarget),
functionType: functionType);
InstanceGetterInvocation.byReference(
this.kind, this.receiver, this.name, this.arguments,
{required this.interfaceTargetReference, required this.functionType})
: assert(functionType == null || functionType.typeParameters.isEmpty) {
receiver.parent = this;
arguments.parent = this;
}
Member get interfaceTarget => interfaceTargetReference.asMember;
void set interfaceTarget(Member target) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
}
/// If `true`, this call is known to be safe wrt. parameter covariance checks.
///
/// This is for instance the case in code patterns like this
///
/// List<int> list = <int>[];
/// list.add(0);
///
/// where the `list` variable is known to hold a value of the same type as
/// the static type. In contrast the would not be the case in code patterns
/// like this
///
/// List<num> list = <double>[];
/// list.add(0); // Runtime error `int` is not a subtype of `double`.
///
bool get isInvariant => flags & FlagInvariant != 0;
void set isInvariant(bool value) {
flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant);
}
/// If `true`, this call is known to be safe wrt. parameter covariance checks.
///
/// This is for instance the case in code patterns like this
///
/// List list = new List.filled(2, 0);
/// list[1] = 42;
///
/// where the `list` is known to have a sufficient length for the update
/// in `list[1] = 42`.
bool get isBoundsSafe => flags & FlagBoundsSafe != 0;
void set isBoundsSafe(bool value) {
flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
functionType?.returnType ?? const DynamicType();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGetterInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceGetterInvocation(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
functionType?.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
if (functionType != null) {
functionType = v.visitDartType(functionType!) as FunctionType;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
if (functionType != null) {
functionType =
v.visitDartType(functionType!, cannotRemoveSentinel) as FunctionType;
}
}
@override
String toString() {
return "InstanceGetterInvocation($kind, ${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.write('.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.writeArguments(arguments);
}
}
/// Access kind used by [FunctionInvocation] and [FunctionTearOff].
enum FunctionAccessKind {
/// An access to the 'call' method on an expression of static type `Function`.
///
/// For instance
///
/// method(Function f) => f();
///
Function,
/// An access to the 'call' method on an expression whose static type is a
/// function type.
///
/// For instance
///
/// method(void Function() f) => f();
///
FunctionType,
/// An access to the 'call' method on an expression whose static type is a
/// function type which is inapplicable, that is, whose arguments don't match
/// the required parameter structure.
///
/// This is an error case which is only used on expression nested within
/// [InvalidExpression]s.
///
/// For instance:
///
/// test(void Function() f) => f(0); // Too many arguments.
///
Inapplicable,
/// An access to the 'call' method on an expression whose static type is a
/// nullable function type or `Function?`.
///
/// This is an error case which is only used on expression nested within
/// [InvalidExpression]s.
///
/// For instance:
///
/// test(void Function()? f) => f(); // 'f' is nullable.
///
Nullable,
}
/// An invocation of the 'call' method on an expression whose static type is
/// a function type or the type 'Function'.
class FunctionInvocation extends InstanceInvocationExpression {
final FunctionAccessKind kind;
@override
Expression receiver;
@override
Arguments arguments;
/// The static type of the invocation.
///
/// This is `null` if the static type of the receiver is not a function type
/// or is not bounded by a function type.
///
/// For instance
///
/// m<T extends Function, S extends int Function()>(T t, S s, Function f) {
/// X local<X>(X t) => t;
/// t(); // The function type is `null`.
/// s(); // The function type is `int Function()`.
/// f(); // The function type is `null`.
/// local(0); // The function type is `int Function(int)`.
/// }
///
FunctionType? functionType;
FunctionInvocation(this.kind, this.receiver, this.arguments,
{required this.functionType}) {
receiver.parent = this;
arguments.parent = this;
}
@override
Name get name => Name.callName;
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
functionType?.returnType ?? const DynamicType();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitFunctionInvocation(this, arg);
@override
void visitChildren(Visitor v) {
receiver.accept(v);
name.accept(v);
arguments.accept(v);
functionType?.accept(v);
}
@override
void transformChildren(Transformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
FunctionType? type = functionType;
if (type != null) {
functionType = v.visitDartType(type) as FunctionType;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
receiver = v.transform(receiver);
receiver.parent = this;
arguments = v.transform(arguments);
arguments.parent = this;
FunctionType? type = functionType;
if (type != null) {
functionType =
v.visitDartType(type, cannotRemoveSentinel) as FunctionType;
}
}
@override
String toString() {
return "FunctionInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(receiver,
minimumPrecedence: astToText.Precedence.PRIMARY);
printer.writeArguments(arguments);
}
}
/// An invocation of a local function declaration.
class LocalFunctionInvocation extends InvocationExpression {
/// The variable declaration for the function declaration.
VariableDeclaration variable;
@override
Arguments arguments;
/// The static type of the invocation.
///
/// This might differ from the static type of [variable] for generic
/// functions.
///
/// For instance
///
/// m() {
/// T local<T>(T t) => t;
/// local(0); // The static type is `int Function(int)`.
/// }
///
FunctionType functionType;
LocalFunctionInvocation(this.variable, this.arguments,
{required this.functionType}) {
arguments.parent = this;
}
/// The declaration for the invoked local function.
FunctionDeclaration get localFunction =>
variable.parent as FunctionDeclaration;
@override
Name get name => Name.callName;
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
functionType.returnType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitLocalFunctionInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitLocalFunctionInvocation(this, arg);
@override
void visitChildren(Visitor v) {
arguments.accept(v);
functionType.accept(v);
}
@override
void transformChildren(Transformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
functionType = v.visitDartType(functionType) as FunctionType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
functionType =
v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType;
}
@override
String toString() {
return "LocalFunctionInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(printer.getVariableName(variable));
printer.writeArguments(arguments);
}
}
/// Nullness test of an expression, that is `e == null`.
///
/// This is generated for code like `e1 == e2` where `e1` or `e2` is `null`.
class EqualsNull extends Expression {
/// The expression tested for nullness.
Expression expression;
EqualsNull(this.expression) {
expression.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsNull(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitEqualsNull(this, arg);
@override
void visitChildren(Visitor v) {
expression.accept(v);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
String toString() {
return "EqualsNull(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(expression, minimumPrecedence: precedence);
printer.write(' == null');
}
}
/// A test of equality, that is `e1 == e2`.
///
/// This is generated for code like `e1 == e2` where neither `e1` nor `e2` is
/// `null`.
class EqualsCall extends Expression {
Expression left;
Expression right;
/// The static type of the invocation.
///
/// This might differ from the static type of [Object.==] for covariant
/// parameters.
///
/// For instance
///
/// class C<T> {
/// bool operator(covariant C<T> other) { ... }
/// }
/// // The function type is `bool Function(C<num>)`.
/// method(C<num> a, C<int> b) => a == b;
///
FunctionType functionType;
Reference interfaceTargetReference;
EqualsCall(Expression left, Expression right,
{required FunctionType functionType, required Procedure interfaceTarget})
: this.byReference(left, right,
functionType: functionType,
interfaceTargetReference:
getNonNullableMemberReferenceGetter(interfaceTarget));
EqualsCall.byReference(this.left, this.right,
{required this.functionType, required this.interfaceTargetReference}) {
left.parent = this;
right.parent = this;
}
Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
void set interfaceTarget(Procedure target) {
interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return functionType.returnType;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsCall(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitEqualsCall(this, arg);
@override
void visitChildren(Visitor v) {
left.accept(v);
interfaceTarget.acceptReference(v);
right.accept(v);
functionType.accept(v);
}
@override
void transformChildren(Transformer v) {
left = v.transform(left);
left.parent = this;
right = v.transform(right);
right.parent = this;
functionType = v.visitDartType(functionType) as FunctionType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
left = v.transform(left);
left.parent = this;
right = v.transform(right);
right.parent = this;
functionType =
v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType;
}
@override
String toString() {
return "EqualsCall(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
int minimumPrecedence = precedence;
printer.writeExpression(left, minimumPrecedence: minimumPrecedence);
printer.write(' == ');
printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1);
}
}
/// Expression of form `super.foo(x)` occurring in a mixin declaration.
///
/// In this setting, the target is looked up on the types in the mixin 'on'
/// clause and are therefore not necessary the runtime targets of the
/// invocation. An [AbstractSuperMethodInvocation] must be converted into
/// a [SuperMethodInvocation] to statically bind the target.
///
/// For instance
///
/// abstract class Interface {
/// void method();
/// }
/// mixin Mixin on Interface {
/// void method() {
/// // This is an [AbstractSuperMethodInvocation] with interface target
/// // `Interface.method`.
/// super.method(); // This targets Super.method.
/// }
/// }
/// class Super implements Interface {
/// // This is the target when `Mixin` is applied to `Class`.
/// void method() {}
/// }
/// class Class extends Super with Mixin {}
///
class AbstractSuperMethodInvocation extends InvocationExpression {
@override
Name name;
@override
Arguments arguments;
Reference interfaceTargetReference;
AbstractSuperMethodInvocation(
Name name, Arguments arguments, Procedure interfaceTarget)
: this.byReference(
name,
arguments,
// An invocation doesn't refer to the setter.
getNonNullableMemberReferenceGetter(interfaceTarget));
AbstractSuperMethodInvocation.byReference(
this.name, this.arguments, this.interfaceTargetReference) {
arguments.parent = this;
}
Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
void set interfaceTarget(Procedure target) {
// An invocation doesn't refer to the setter.
interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
Class superclass = interfaceTarget.enclosingClass!;
List<DartType>? receiverTypeArguments = context.typeEnvironment
.getTypeArgumentsAsInstanceOf(context.thisType!, superclass);
DartType returnType = Substitution.fromPairs(
superclass.typeParameters, receiverTypeArguments!)
.substituteType(interfaceTarget.function.returnType);
return Substitution.fromPairs(
interfaceTarget.function.typeParameters, arguments.types)
.substituteType(returnType);
}
@override
R accept<R>(ExpressionVisitor<R> v) =>
v.visitAbstractSuperMethodInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAbstractSuperMethodInvocation(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
}
@override
void transformChildren(Transformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
String toString() {
return "AbstractSuperMethodInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.{abstract}');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.writeArguments(arguments);
}
}
/// Expression of form `super.foo(x)`.
///
/// The provided arguments might not match the parameters of the target.
class SuperMethodInvocation extends InvocationExpression {
@override
Name name;
@override
Arguments arguments;
Reference interfaceTargetReference;
SuperMethodInvocation(
Name name, Arguments arguments, Procedure interfaceTarget)
: this.byReference(
name,
arguments,
// An invocation doesn't refer to the setter.
getNonNullableMemberReferenceGetter(interfaceTarget));
SuperMethodInvocation.byReference(
this.name, this.arguments, this.interfaceTargetReference) {
arguments.parent = this;
}
Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
void set interfaceTarget(Procedure target) {
// An invocation doesn't refer to the setter.
interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
Class superclass = interfaceTarget.enclosingClass!;
List<DartType>? receiverTypeArguments = context.typeEnvironment
.getTypeArgumentsAsInstanceOf(context.thisType!, superclass);
DartType returnType = Substitution.fromPairs(
superclass.typeParameters, receiverTypeArguments!)
.substituteType(interfaceTarget.function.returnType);
return Substitution.fromPairs(
interfaceTarget.function.typeParameters, arguments.types)
.substituteType(returnType);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSuperMethodInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSuperMethodInvocation(this, arg);
@override
void visitChildren(Visitor v) {
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
}
@override
void transformChildren(Transformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
String toString() {
return "SuperMethodInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('super.');
printer.writeInterfaceMemberName(interfaceTargetReference, name);
printer.writeArguments(arguments);
}
}
/// Expression of form `foo(x)`, or `const foo(x)` if the target is an
/// external constant factory.
///
/// The provided arguments might not match the parameters of the target.
class StaticInvocation extends InvocationExpression {
Reference targetReference;
@override
Arguments arguments;
/// True if this is a constant call to an external constant factory.
bool isConst;
@override
Name get name => target.name;
StaticInvocation(Procedure target, Arguments arguments,
{bool isConst = false})
: this.byReference(
// An invocation doesn't refer to the setter.
getNonNullableMemberReferenceGetter(target),
arguments,
isConst: isConst);
StaticInvocation.byReference(this.targetReference, this.arguments,
{this.isConst = false}) {
arguments.parent = this;
}
Procedure get target => targetReference.asProcedure;
void set target(Procedure target) {
// An invocation doesn't refer to the setter.
targetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return Substitution.fromPairs(
target.function.typeParameters, arguments.types)
.substituteType(target.function.returnType);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStaticInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStaticInvocation(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
arguments.accept(v);
}
@override
void transformChildren(Transformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
String toString() {
return "StaticInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
printer.writeArguments(arguments);
}
}
/// Expression of form `new Foo(x)` or `const Foo(x)`.
///
/// The provided arguments might not match the parameters of the target.
//
// DESIGN TODO: Should we pass type arguments in a separate field
// `classTypeArguments`? They are quite different from type arguments to
// generic functions.
class ConstructorInvocation extends InvocationExpression {
Reference targetReference;
@override
Arguments arguments;
bool isConst;
@override
Name get name => target.name;
ConstructorInvocation(Constructor target, Arguments arguments,
{bool isConst = false})
: this.byReference(
// A constructor doesn't refer to the setter.
getNonNullableMemberReferenceGetter(target),
arguments,
isConst: isConst);
ConstructorInvocation.byReference(this.targetReference, this.arguments,
{this.isConst = false}) {
arguments.parent = this;
}
Constructor get target => targetReference.asConstructor;
void set target(Constructor target) {
// A constructor doesn't refer to the setter.
targetReference = getNonNullableMemberReferenceGetter(target);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return arguments.types.isEmpty
? context.typeEnvironment.coreTypes
.rawType(target.enclosingClass, context.nonNullable)
: new InterfaceType(
target.enclosingClass, context.nonNullable, arguments.types);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorInvocation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitConstructorInvocation(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
arguments.accept(v);
}
@override
void transformChildren(Transformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
arguments = v.transform(arguments);
arguments.parent = this;
}
// TODO(cstefantsova): Change the getter into a method that accepts a
// CoreTypes.
InterfaceType get constructedType {
Class enclosingClass = target.enclosingClass;
// TODO(cstefantsova): Get raw type from a CoreTypes object if arguments is
// empty.
return arguments.types.isEmpty
? new InterfaceType(enclosingClass, target.enclosingLibrary.nonNullable,
const <DartType>[])
: new InterfaceType(enclosingClass, target.enclosingLibrary.nonNullable,
arguments.types);
}
@override
String toString() {
return "ConstructorInvocation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (isConst) {
printer.write('const ');
} else {
printer.write('new ');
}
printer.writeClassName(target.enclosingClass.reference);
printer.writeTypeArguments(arguments.types);
if (target.name.text.isNotEmpty) {
printer.write('.');
printer.write(target.name.text);
}
printer.writeArguments(arguments, includeTypeArguments: false);
}
}
/// An explicit type instantiation of a generic function.
class Instantiation extends Expression {
Expression expression;
final List<DartType> typeArguments;
Instantiation(this.expression, this.typeArguments) {
expression.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
DartType type = expression.getStaticType(context);
if (type is FunctionType) {
return FunctionTypeInstantiator.instantiate(type, typeArguments);
}
assert(type is InvalidType || type is NeverType,
"Unexpected operand type $type for $expression");
return type;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstantiation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstantiation(this, arg);
@override
void visitChildren(Visitor v) {
expression.accept(v);
visitList(typeArguments, v);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression);
expression.parent = this;
v.transformDartTypeList(typeArguments);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression);
expression.parent = this;
v.transformDartTypeList(typeArguments);
}
@override
String toString() {
return "Instantiation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(expression);
printer.writeTypeArguments(typeArguments);
}
}
/// Expression of form `!x`.
///
/// The `is!` and `!=` operators are desugared into [Not] nodes with `is` and
/// `==` expressions inside, respectively.
class Not extends Expression {
Expression operand;
Not(this.operand) {
operand.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitNot(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitNot(this, arg);
@override
void visitChildren(Visitor v) {
operand.accept(v);
}
@override
void transformChildren(Transformer v) {
operand = v.transform(operand);
operand.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
operand = v.transform(operand);
operand.parent = this;
}
@override
String toString() {
return "Not(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('!');
printer.writeExpression(operand,
minimumPrecedence: astToText.Precedence.PREFIX);
}
}
enum LogicalExpressionOperator { AND, OR }
String logicalExpressionOperatorToString(LogicalExpressionOperator operator) {
switch (operator) {
case LogicalExpressionOperator.AND:
return "&&";
case LogicalExpressionOperator.OR:
return "||";
}
}
/// Expression of form `x && y` or `x || y`
class LogicalExpression extends Expression {
Expression left;
LogicalExpressionOperator operatorEnum; // AND (&&) or OR (||).
Expression right;
LogicalExpression(this.left, this.operatorEnum, this.right) {
left.parent = this;
right.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitLogicalExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitLogicalExpression(this, arg);
@override
void visitChildren(Visitor v) {
left.accept(v);
right.accept(v);
}
@override
void transformChildren(Transformer v) {
left = v.transform(left);
left.parent = this;
right = v.transform(right);
right.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
left = v.transform(left);
left.parent = this;
right = v.transform(right);
right.parent = this;
}
@override
String toString() {
return "LogicalExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
int minimumPrecedence = precedence;
printer.writeExpression(left, minimumPrecedence: minimumPrecedence);
printer.write(' ${logicalExpressionOperatorToString(operatorEnum)} ');
printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1);
}
}
/// Expression of form `x ? y : z`.
class ConditionalExpression extends Expression {
Expression condition;
Expression then;
Expression otherwise;
/// The static type of the expression.
DartType staticType;
ConditionalExpression(
this.condition, this.then, this.otherwise, this.staticType) {
condition.parent = this;
then.parent = this;
otherwise.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) => staticType;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitConditionalExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitConditionalExpression(this, arg);
@override
void visitChildren(Visitor v) {
condition.accept(v);
then.accept(v);
otherwise.accept(v);
staticType.accept(v);
}
@override
void transformChildren(Transformer v) {
condition = v.transform(condition);
condition.parent = this;
then = v.transform(then);
then.parent = this;
otherwise = v.transform(otherwise);
otherwise.parent = this;
staticType = v.visitDartType(staticType);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
condition = v.transform(condition);
condition.parent = this;
then = v.transform(then);
then.parent = this;
otherwise = v.transform(otherwise);
otherwise.parent = this;
staticType = v.visitDartType(staticType, cannotRemoveSentinel);
}
@override
String toString() {
return "ConditionalExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(condition,
minimumPrecedence: astToText.Precedence.LOGICAL_OR);
printer.write(' ?');
printer.write('{');
printer.writeType(staticType);
printer.write('}');
printer.write(' ');
printer.writeExpression(then);
printer.write(' : ');
printer.writeExpression(otherwise);
}
}
/// Convert expressions to strings and concatenate them. Semantically, calls
/// `toString` on every argument, checks that a string is returned, and returns
/// the concatenation of all the strings.
///
/// If [expressions] is empty then an empty string is returned.
///
/// These arise from string interpolations and adjacent string literals.
class StringConcatenation extends Expression {
final List<Expression> expressions;
StringConcatenation(this.expressions) {
setParents(expressions, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStringConcatenation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStringConcatenation(this, arg);
@override
void visitChildren(Visitor v) {
visitList(expressions, v);
}
@override
void transformChildren(Transformer v) {
v.transformList(expressions, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformExpressionList(expressions, this);
}
@override
String toString() {
return "StringConcatenation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('"');
for (Expression part in expressions) {
if (part is StringLiteral) {
printer.write(escapeString(part.value));
} else {
printer.write(r'${');
printer.writeExpression(part);
printer.write('}');
}
}
printer.write('"');
}
}
/// Concatenate lists into a single list.
///
/// If [lists] is empty then an empty list is returned.
///
/// These arise from spread and control-flow elements in const list literals.
/// They are only present before constant evaluation, or within unevaluated
/// constants in constant expressions.
class ListConcatenation extends Expression {
DartType typeArgument;
final List<Expression> lists;
ListConcatenation(this.lists, {this.typeArgument = const DynamicType()}) {
setParents(lists, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.listType(typeArgument, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitListConcatenation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitListConcatenation(this, arg);
@override
void visitChildren(Visitor v) {
typeArgument.accept(v);
visitList(lists, v);
}
@override
void transformChildren(Transformer v) {
typeArgument = v.visitDartType(typeArgument);
v.transformList(lists, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
v.transformExpressionList(lists, this);
}
@override
String toString() {
return "ListConcatenation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
bool first = true;
for (Expression part in lists) {
if (!first) {
printer.write(' + ');
}
printer.writeExpression(part);
first = false;
}
}
}
/// Concatenate sets into a single set.
///
/// If [sets] is empty then an empty set is returned.
///
/// These arise from spread and control-flow elements in const set literals.
/// They are only present before constant evaluation, or within unevaluated
/// constants in constant expressions.
///
/// Duplicated values in or across the sets will result in a compile-time error
/// during constant evaluation.
class SetConcatenation extends Expression {
DartType typeArgument;
final List<Expression> sets;
SetConcatenation(this.sets, {this.typeArgument = const DynamicType()}) {
setParents(sets, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.setType(typeArgument, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSetConcatenation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSetConcatenation(this, arg);
@override
void visitChildren(Visitor v) {
typeArgument.accept(v);
visitList(sets, v);
}
@override
void transformChildren(Transformer v) {
typeArgument = v.visitDartType(typeArgument);
v.transformList(sets, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
v.transformExpressionList(sets, this);
}
@override
String toString() {
return "SetConcatenation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
bool first = true;
for (Expression part in sets) {
if (!first) {
printer.write(' + ');
}
printer.writeExpression(part);
first = false;
}
}
}
/// Concatenate maps into a single map.
///
/// If [maps] is empty then an empty map is returned.
///
/// These arise from spread and control-flow elements in const map literals.
/// They are only present before constant evaluation, or within unevaluated
/// constants in constant expressions.
///
/// Duplicated keys in or across the maps will result in a compile-time error
/// during constant evaluation.
class MapConcatenation extends Expression {
DartType keyType;
DartType valueType;
final List<Expression> maps;
MapConcatenation(this.maps,
{this.keyType = const DynamicType(),
this.valueType = const DynamicType()}) {
setParents(maps, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment
.mapType(keyType, valueType, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitMapConcatenation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitMapConcatenation(this, arg);
@override
void visitChildren(Visitor v) {
keyType.accept(v);
valueType.accept(v);
visitList(maps, v);
}
@override
void transformChildren(Transformer v) {
keyType = v.visitDartType(keyType);
valueType = v.visitDartType(valueType);
v.transformList(maps, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
keyType = v.visitDartType(keyType, cannotRemoveSentinel);
valueType = v.visitDartType(valueType, cannotRemoveSentinel);
v.transformExpressionList(maps, this);
}
@override
String toString() {
return "MapConcatenation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
bool first = true;
for (Expression part in maps) {
if (!first) {
printer.write(' + ');
}
printer.writeExpression(part);
first = false;
}
}
}
/// Create an instance directly from the field values.
///
/// These expressions arise from const constructor calls when one or more field
/// initializing expressions, field initializers, assert initializers or unused
/// arguments contain unevaluated expressions. They only ever occur within
/// unevaluated constants in constant expressions.
class InstanceCreation extends Expression {
final Reference classReference;
final List<DartType> typeArguments;
final Map<Reference, Expression> fieldValues;
final List<AssertStatement> asserts;
final List<Expression> unusedArguments;
InstanceCreation(this.classReference, this.typeArguments, this.fieldValues,
this.asserts, this.unusedArguments) {
setParents(fieldValues.values.toList(), this);
setParents(asserts, this);
setParents(unusedArguments, this);
}
Class get classNode => classReference.asClass;
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return typeArguments.isEmpty
? context.typeEnvironment.coreTypes
.rawType(classNode, context.nonNullable)
: new InterfaceType(classNode, context.nonNullable, typeArguments);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceCreation(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitInstanceCreation(this, arg);
@override
void visitChildren(Visitor v) {
classReference.asClass.acceptReference(v);
visitList(typeArguments, v);
for (final Reference reference in fieldValues.keys) {
reference.asField.acceptReference(v);
}
for (final Expression value in fieldValues.values) {
value.accept(v);
}
visitList(asserts, v);
visitList(unusedArguments, v);
}
@override
void transformChildren(Transformer v) {
fieldValues.forEach((Reference fieldRef, Expression value) {
Expression transformed = v.transform(value);
if (!identical(value, transformed)) {
fieldValues[fieldRef] = transformed;
transformed.parent = this;
}
});
v.transformList(asserts, this);
v.transformList(unusedArguments, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
fieldValues.forEach((Reference fieldRef, Expression value) {
Expression transformed = v.transform(value);
if (!identical(value, transformed)) {
fieldValues[fieldRef] = transformed;
transformed.parent = this;
}
});
v.transformList(asserts, this, dummyAssertStatement);
v.transformExpressionList(unusedArguments, this);
}
@override
String toString() {
return "InstanceCreation(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeClassName(classReference);
printer.writeTypeArguments(typeArguments);
printer.write('{');
bool first = true;
fieldValues.forEach((Reference fieldRef, Expression value) {
if (!first) {
printer.write(', ');
}
printer.writeName(fieldRef.asField.name);
printer.write(': ');
printer.writeExpression(value);
first = false;
});
for (AssertStatement assert_ in asserts) {
if (!first) {
printer.write(', ');
}
printer.write('assert(');
printer.writeExpression(assert_.condition);
if (assert_.message != null) {
printer.write(', ');
printer.writeExpression(assert_.message!);
}
printer.write(')');
first = false;
}
for (Expression unusedArgument in unusedArguments) {
if (!first) {
printer.write(', ');
}
printer.writeExpression(unusedArgument);
first = false;
}
printer.write('}');
}
}
/// A marker indicating that a subexpression originates in a different source
/// file than the surrounding context.
///
/// These expressions arise from inlining of const variables during constant
/// evaluation. They only ever occur within unevaluated constants in constant
/// expressions.
class FileUriExpression extends Expression implements FileUriNode {
/// The URI of the source file in which the subexpression is located.
/// Can be different from the file containing the [FileUriExpression].
@override
Uri fileUri;
Expression expression;
FileUriExpression(this.expression, this.fileUri) {
expression.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
expression.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitFileUriExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitFileUriExpression(this, arg);
@override
void visitChildren(Visitor v) {
expression.accept(v);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
}
@override
Location? _getLocationInEnclosingFile(int offset) {
return _getLocationInComponent(enclosingComponent, fileUri, offset,
viaForErrorMessage: "File uri expression");
}
@override
String toString() {
return "FileUriExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (printer.includeAuxiliaryProperties) {
printer.write('{');
printer.write(fileUri.toString());
printer.write('}');
}
printer.writeExpression(expression);
}
}
/// Expression of form `x is T`.
class IsExpression extends Expression {
Expression operand;
DartType type;
IsExpression(this.operand, this.type) {
operand.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitIsExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitIsExpression(this, arg);
@override
void visitChildren(Visitor v) {
operand.accept(v);
type.accept(v);
}
@override
void transformChildren(Transformer v) {
operand = v.transform(operand);
operand.parent = this;
type = v.visitDartType(type);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
operand = v.transform(operand);
operand.parent = this;
type = v.visitDartType(type, cannotRemoveSentinel);
}
@override
String toString() {
return "IsExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(operand,
minimumPrecedence: astToText.Precedence.BITWISE_OR);
printer.write(' is ');
printer.writeType(type);
}
}
/// Expression of form `x as T`.
class AsExpression extends Expression {
int flags = 0;
Expression operand;
DartType type;
AsExpression(this.operand, this.type) {
operand.parent = this;
}
// Must match serialized bit positions.
static const int FlagTypeError = 1 << 0;
static const int FlagCovarianceCheck = 1 << 1;
static const int FlagForDynamic = 1 << 2;
static const int FlagUnchecked = 1 << 3;
/// If `true`, this test is an implicit down cast.
///
/// If `true` a TypeError should be thrown. If `false` a CastError should be
/// thrown.
bool get isTypeError => flags & FlagTypeError != 0;
void set isTypeError(bool value) {
flags = value ? (flags | FlagTypeError) : (flags & ~FlagTypeError);
}
/// If `true`, this test is needed to ensure soundness of covariant type
/// variables using in contravariant positions.
///
/// For instance
///
/// class Class<T> {
/// void Function(T) field;
/// Class(this.field);
/// }
/// main() {
/// Class<num> c = new Class<int>((int i) {});
/// void Function<num> field = c.field; // Check needed on `c.field`
/// field(0.5);
/// }
///
/// Here a covariant check `c.field as void Function(num)` is needed because
/// the field could be (and indeed is) not a subtype of the static type of
/// the expression.
bool get isCovarianceCheck => flags & FlagCovarianceCheck != 0;
void set isCovarianceCheck(bool value) {
flags =
value ? (flags | FlagCovarianceCheck) : (flags & ~FlagCovarianceCheck);
}
/// If `true`, this is an implicit down cast from an expression of type
/// `dynamic`.
bool get isForDynamic => flags & FlagForDynamic != 0;
void set isForDynamic(bool value) {
flags = value ? (flags | FlagForDynamic) : (flags & ~FlagForDynamic);
}
/// If `true`, this test is added to show the known static type of the
/// expression and should not be performed at runtime.
///
/// This is the case for instance for access to extension type representation
/// fields on an extension type, where this node shows that the static type
/// changes from the extension type of the declared representation type.
///
/// This is also the case when a field access undergoes type promotion.
bool get isUnchecked => flags & FlagUnchecked != 0;
void set isUnchecked(bool value) {
flags = value ? (flags | FlagUnchecked) : (flags & ~FlagUnchecked);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) => type;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitAsExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAsExpression(this, arg);
@override
void visitChildren(Visitor v) {
operand.accept(v);
type.accept(v);
}
@override
void transformChildren(Transformer v) {
operand = v.transform(operand);
operand.parent = this;
type = v.visitDartType(type);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
operand = v.transform(operand);
operand.parent = this;
type = v.visitDartType(type, cannotRemoveSentinel);
}
@override
String toString() {
return "AsExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(operand,
minimumPrecedence: astToText.Precedence.BITWISE_OR);
printer.write(' as');
if (printer.includeAuxiliaryProperties) {
List<String> flags = <String>[];
if (isTypeError) {
flags.add('TypeError');
}
if (isCovarianceCheck) {
flags.add('CovarianceCheck');
}
if (isForDynamic) {
flags.add('ForDynamic');
}
if (flags.isNotEmpty) {
printer.write('{${flags.join(',')}}');
}
}
printer.write(' ');
printer.writeType(type);
}
}
/// Null check expression of form `x!`.
///
/// This expression was added as part of NNBD and is currently only created when
/// the 'non-nullable' experimental feature is enabled.
class NullCheck extends Expression {
Expression operand;
NullCheck(this.operand) {
operand.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
DartType operandType = operand.getStaticType(context);
return operandType is NullType
? const NeverType.nonNullable()
: operandType.withDeclaredNullability(Nullability.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitNullCheck(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitNullCheck(this, arg);
@override
void visitChildren(Visitor v) {
operand.accept(v);
}
@override
void transformChildren(Transformer v) {
operand = v.transform(operand);
operand.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
operand = v.transform(operand);
operand.parent = this;
}
@override
String toString() {
return "NullCheck(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(operand,
minimumPrecedence: astToText.Precedence.POSTFIX);
printer.write('!');
}
}
/// An integer, double, boolean, string, or null constant.
abstract class BasicLiteral extends Expression {
Object? get value;
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
}
class StringLiteral extends BasicLiteral {
@override
String value;
StringLiteral(this.value);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitStringLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitStringLiteral(this, arg);
@override
String toString() {
return "StringLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('"');
printer.write(escapeString(value));
printer.write('"');
}
}
class IntLiteral extends BasicLiteral {
/// Note that this value holds a uint64 value.
/// E.g. "0x8000000000000000" will be saved as "-9223372036854775808" despite
/// technically (on some platforms, particularly JavaScript) being positive.
/// If the number is meant to be negative it will be wrapped in a "unary-".
@override
int value;
IntLiteral(this.value);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.intRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitIntLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitIntLiteral(this, arg);
@override
String toString() {
return "IntLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('$value');
}
}
class DoubleLiteral extends BasicLiteral {
@override
double value;
DoubleLiteral(this.value);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitDoubleLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitDoubleLiteral(this, arg);
@override
String toString() {
return "DoubleLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('$value');
}
}
class BoolLiteral extends BasicLiteral {
@override
bool value;
BoolLiteral(this.value);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitBoolLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitBoolLiteral(this, arg);
@override
String toString() {
return "BoolLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('$value');
}
}
class NullLiteral extends BasicLiteral {
@override
Object? get value => null;
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) => const NullType();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitNullLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitNullLiteral(this, arg);
@override
String toString() {
return "NullLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('null');
}
}
class SymbolLiteral extends Expression {
String value; // Everything strictly after the '#'.
SymbolLiteral(this.value);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSymbolLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSymbolLiteral(this, arg);
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "SymbolLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('#');
printer.write(value);
}
}
class TypeLiteral extends Expression {
DartType type;
TypeLiteral(this.type);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.typeEnvironment.coreTypes.typeRawType(context.nonNullable);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitTypeLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitTypeLiteral(this, arg);
@override
void visitChildren(Visitor v) {
type.accept(v);
}
@override
void transformChildren(Transformer v) {
type = v.visitDartType(type);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
type = v.visitDartType(type, cannotRemoveSentinel);
}
@override
String toString() {
return "TypeLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeType(type);
}
}
class ThisExpression extends Expression {
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
context.thisType!;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitThisExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitThisExpression(this, arg);
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "ThisExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('this');
}
}
class Rethrow extends Expression {
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
const NeverType.nonNullable();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitRethrow(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitRethrow(this, arg);
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "Rethrow(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('rethrow');
}
}
class Throw extends Expression {
Expression expression;
int flags = 0;
Throw(this.expression) {
expression.parent = this;
}
// Must match serialized bit positions.
static const int FlagForErrorHandling = 1 << 0;
/// If `true`, this `throw` is *not* present in the source code but added
/// to ensure correctness and/or soundness of the generated code.
///
/// This is used for instance in the lowering for handling duplicate writes
/// to a late final field or for pattern assignments that don't match.
bool get forErrorHandling => flags & FlagForErrorHandling != 0;
void set forErrorHandling(bool value) {
flags = value
? (flags | FlagForErrorHandling)
: (flags & ~FlagForErrorHandling);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
const NeverType.nonNullable();
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitThrow(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitThrow(this, arg);
@override
void visitChildren(Visitor v) {
expression.accept(v);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression);
expression.parent = this;
}
@override
String toString() {
return "Throw(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('throw ');
printer.writeExpression(expression);
}
}
class ListLiteral extends Expression {
bool isConst;
DartType typeArgument; // Not null, defaults to DynamicType.
final List<Expression> expressions;
ListLiteral(this.expressions,
{this.typeArgument = const DynamicType(), this.isConst = false}) {
setParents(expressions, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.listType(typeArgument, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitListLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitListLiteral(this, arg);
@override
void visitChildren(Visitor v) {
typeArgument.accept(v);
visitList(expressions, v);
}
@override
void transformChildren(Transformer v) {
typeArgument = v.visitDartType(typeArgument);
v.transformList(expressions, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
v.transformExpressionList(expressions, this);
}
@override
String toString() {
return "ListLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (isConst) {
printer.write('const ');
}
printer.write('<');
printer.writeType(typeArgument);
printer.write('>[');
printer.writeExpressions(expressions);
printer.write(']');
}
}
class SetLiteral extends Expression {
bool isConst;
DartType typeArgument; // Not null, defaults to DynamicType.
final List<Expression> expressions;
SetLiteral(this.expressions,
{this.typeArgument = const DynamicType(), this.isConst = false}) {
setParents(expressions, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.setType(typeArgument, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitSetLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitSetLiteral(this, arg);
@override
void visitChildren(Visitor v) {
typeArgument.accept(v);
visitList(expressions, v);
}
@override
void transformChildren(Transformer v) {
typeArgument = v.visitDartType(typeArgument);
v.transformList(expressions, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
v.transformExpressionList(expressions, this);
}
@override
String toString() {
return "SetLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (isConst) {
printer.write('const ');
}
printer.write('<');
printer.writeType(typeArgument);
printer.write('>{');
printer.writeExpressions(expressions);
printer.write('}');
}
}
class MapLiteral extends Expression {
bool isConst;
DartType keyType; // Not null, defaults to DynamicType.
DartType valueType; // Not null, defaults to DynamicType.
final List<MapLiteralEntry> entries;
MapLiteral(this.entries,
{this.keyType = const DynamicType(),
this.valueType = const DynamicType(),
this.isConst = false}) {
setParents(entries, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment
.mapType(keyType, valueType, context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitMapLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitMapLiteral(this, arg);
@override
void visitChildren(Visitor v) {
keyType.accept(v);
valueType.accept(v);
visitList(entries, v);
}
@override
void transformChildren(Transformer v) {
keyType = v.visitDartType(keyType);
valueType = v.visitDartType(valueType);
v.transformList(entries, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
keyType = v.visitDartType(keyType, cannotRemoveSentinel);
valueType = v.visitDartType(valueType, cannotRemoveSentinel);
v.transformMapEntryList(entries, this);
}
@override
String toString() {
return "MapLiteral(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (isConst) {
printer.write('const ');
}
printer.write('<');
printer.writeType(keyType);
printer.write(', ');
printer.writeType(valueType);
printer.write('>{');
for (int index = 0; index < entries.length; index++) {
if (index > 0) {
printer.write(', ');
}
printer.writeMapEntry(entries[index]);
}
printer.write('}');
}
}
class MapLiteralEntry extends TreeNode {
Expression key;
Expression value;
MapLiteralEntry(this.key, this.value) {
key.parent = this;
value.parent = this;
}
@override
R accept<R>(TreeVisitor<R> v) => v.visitMapLiteralEntry(this);
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
v.visitMapLiteralEntry(this, arg);
@override
void visitChildren(Visitor v) {
key.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
key = v.transform(key);
key.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
key = v.transform(key);
key.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "MapEntry(${toStringInternal()})";
}
@override
String toText(AstTextStrategy strategy) {
AstPrinter printer = new AstPrinter(strategy);
toTextInternal(printer);
return printer.getText();
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(key);
printer.write(': ');
printer.writeExpression(value);
}
}
class RecordLiteral extends Expression {
bool isConst;
final List<Expression> positional;
final List<NamedExpression> named;
RecordType recordType;
RecordLiteral(this.positional, this.named, this.recordType,
{this.isConst = false})
: assert(positional.length == recordType.positional.length &&
named.length == recordType.named.length &&
recordType.named
.map((f) => f.name)
.toSet()
.containsAll(named.map((f) => f.name))),
assert(() {
// Assert that the named fields are sorted.
for (int i = 1; i < named.length; i++) {
if (named[i].name.compareTo(named[i - 1].name) < 0) {
return false;
}
}
return true;
}(),
"Named fields of a RecordLiterals aren't sorted lexicographically: "
"${named.map((f) => f.name).join(", ")}") {
setParents(positional, this);
setParents(named, this);
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return recordType;
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitRecordLiteral(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitRecordLiteral(this, arg);
@override
void visitChildren(Visitor v) {
visitList(positional, v);
visitList(named, v);
recordType.accept(v);
}
@override
void transformChildren(Transformer v) {
v.transformList(positional, this);
v.transformList(named, this);
recordType = v.visitDartType(recordType) as RecordType;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformExpressionList(positional, this);
v.transformNamedExpressionList(named, this);
recordType =
v.visitDartType(recordType, cannotRemoveSentinel) as RecordType;
}
@override
String toString() {
return "RecordType(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
if (isConst) {
printer.write("const ");
}
printer.write("(");
for (int index = 0; index < positional.length; index++) {
if (index > 0) {
printer.write(", ");
}
printer.writeExpression(positional[index]);
}
if (named.isNotEmpty) {
if (positional.isNotEmpty) {
printer.write(", ");
}
for (int index = 0; index < named.length; index++) {
if (index > 0) {
printer.write(", ");
}
printer.writeNamedExpression(named[index]);
}
}
printer.write(")");
}
}
/// Expression of form `await x`.
class AwaitExpression extends Expression {
Expression operand;
/// If non-null, the runtime should check whether the value of [operand] is a
/// subtype of [runtimeCheckType], and if _not_ so, wrap the value in a call
/// to the `Future.value()` constructor.
///
/// For instance
///
/// FutureOr<Object> future1 = Future<Object?>.value();
/// var x = await future1; // Check against `Future<Object>`.
///
/// Object object = Future<Object?>.value();
/// var y = await object; // Check against `Future<Object>`.
///
/// Future<Object?> future2 = Future<Object?>.value();
/// var z = await future2; // No check.
///
/// This runtime checks is necessary to ensure that we don't evaluate the
/// await expression to `null` when the static type of the expression is
/// non-nullable.
///
/// The [runtimeCheckType] is computed as `Future<T>` where `T = flatten(S)`
/// and `S` is the static type of [operand]. To avoid unnecessary runtime
/// checks, the [runtimeCheckType] is not set if the static type of the
/// [operand] is a subtype of `Future<T>`.
///
/// See https://github.com/dart-lang/sdk/issues/49396 for further discussion
/// of which the check is needed.
DartType? runtimeCheckType;
AwaitExpression(this.operand) {
operand.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.flatten(operand.getStaticType(context));
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitAwaitExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitAwaitExpression(this, arg);
@override
void visitChildren(Visitor v) {
operand.accept(v);
}
@override
void transformChildren(Transformer v) {
operand = v.transform(operand);
operand.parent = this;
if (runtimeCheckType != null) {
runtimeCheckType = v.visitDartType(runtimeCheckType!);
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
operand = v.transform(operand);
operand.parent = this;
if (runtimeCheckType != null) {
runtimeCheckType = v.visitDartType(runtimeCheckType!, null);
}
}
@override
String toString() {
return "AwaitExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('await ');
printer.writeExpression(operand);
}
}
/// Common super-interface for [FunctionExpression] and [FunctionDeclaration].
abstract class LocalFunction implements GenericFunction {
@override
FunctionNode get function;
}
/// Expression of form `(x,y) => ...` or `(x,y) { ... }`
///
/// The arrow-body form `=> e` is desugared into `return e;`.
class FunctionExpression extends Expression implements LocalFunction {
@override
FunctionNode function;
FunctionExpression(this.function) {
function.parent = this;
}
@override
List<TypeParameter> get typeParameters => function.typeParameters;
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return function.computeFunctionType(context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitFunctionExpression(this, arg);
@override
void visitChildren(Visitor v) {
function.accept(v);
}
@override
void transformChildren(Transformer v) {
function = v.transform(function);
function.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
function = v.transform(function);
function.parent = this;
}
@override
String toString() {
return "FunctionExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeFunctionNode(function, '');
}
}
class ConstantExpression extends Expression {
Constant constant;
DartType type;
ConstantExpression(this.constant, [this.type = const DynamicType()]);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) => type;
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitConstantExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitConstantExpression(this, arg);
@override
void visitChildren(Visitor v) {
constant.acceptReference(v);
type.accept(v);
}
@override
void transformChildren(Transformer v) {
constant = v.visitConstant(constant);
type = v.visitDartType(type);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
constant = v.visitConstant(constant, cannotRemoveSentinel);
type = v.visitDartType(type, cannotRemoveSentinel);
}
@override
String toString() {
return "ConstantExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeConstant(constant);
}
}
class FileUriConstantExpression extends ConstantExpression
implements FileUriNode {
@override
Uri fileUri;
FileUriConstantExpression(Constant constant,
{DartType type = const DynamicType(), required this.fileUri})
: super(constant, type);
@override
Location? _getLocationInEnclosingFile(int offset) {
return _getLocationInComponent(enclosingComponent, fileUri, offset,
viaForErrorMessage: "File uri constant expression");
}
}
/// Synthetic expression of form `let v = x in y`
class Let extends Expression {
VariableDeclaration variable; // Must have an initializer.
Expression body;
Let(this.variable, this.body) {
variable.parent = this;
body.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
body.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitLet(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitLet(this, arg);
@override
void visitChildren(Visitor v) {
variable.accept(v);
body.accept(v);
}
@override
void transformChildren(Transformer v) {
variable = v.transform(variable);
variable.parent = this;
body = v.transform(body);
body.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
variable = v.transform(variable);
variable.parent = this;
body = v.transform(body);
body.parent = this;
}
@override
String toString() {
return "Let(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('let ');
printer.writeVariableDeclaration(variable);
printer.write(' in ');
printer.writeExpression(body);
}
}
class BlockExpression extends Expression {
Block body;
Expression value;
BlockExpression(this.body, this.value) {
body.parent = this;
value.parent = this;
}
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) =>
value.getStaticType(context);
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitBlockExpression(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitBlockExpression(this, arg);
@override
void visitChildren(Visitor v) {
body.accept(v);
value.accept(v);
}
@override
void transformChildren(Transformer v) {
body = v.transform(body);
body.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
body = v.transform(body);
body.parent = this;
value = v.transform(value);
value.parent = this;
}
@override
String toString() {
return "BlockExpression(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('block ');
printer.writeBlock(body.statements);
printer.write(' => ');
printer.writeExpression(value);
}
}
/// Attempt to load the library referred to by a deferred import.
///
/// This instruction is concerned with:
/// - keeping track whether the deferred import is marked as 'loaded'
/// - keeping track of whether the library code has already been downloaded
/// - actually downloading and linking the library
///
/// Should return a future. The value in this future will be the same value
/// seen by callers of `loadLibrary` functions.
///
/// On backends that link the entire program eagerly, this instruction needs
/// to mark the deferred import as 'loaded' and return a future.
class LoadLibrary extends Expression {
/// Reference to a deferred import in the enclosing library.
LibraryDependency import;
LoadLibrary(this.import);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment
.futureType(const DynamicType(), context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitLoadLibrary(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitLoadLibrary(this, arg);
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "LoadLibrary(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(import.name!);
printer.write('.loadLibrary()');
}
}
/// Checks that the given deferred import has been marked as 'loaded'.
class CheckLibraryIsLoaded extends Expression {
/// Reference to a deferred import in the enclosing library.
LibraryDependency import;
CheckLibraryIsLoaded(this.import);
@override
DartType getStaticType(StaticTypeContext context) =>
getStaticTypeInternal(context);
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return context.typeEnvironment.coreTypes.objectRawType(context.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitCheckLibraryIsLoaded(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitCheckLibraryIsLoaded(this, arg);
@override
void visitChildren(Visitor v) {}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "CheckLibraryIsLoaded(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(import.name!);
printer.write('.checkLibraryIsLoaded()');
}
}
/// Tearing off a constructor of a class.
class ConstructorTearOff extends Expression {
/// The reference to the constructor being torn off.
Reference targetReference;
ConstructorTearOff(Member target)
: assert(
target is Constructor || (target is Procedure && target.isFactory),
"Unexpected constructor tear off target: $target"),
this.targetReference = getNonNullableMemberReferenceGetter(target);
ConstructorTearOff.byReference(this.targetReference);
Member get target => targetReference.asMember;
FunctionNode get function => target.function!;
void set target(Member member) {
assert(member is Constructor ||
(member is Procedure && member.kind == ProcedureKind.Factory));
targetReference = getNonNullableMemberReferenceGetter(member);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return target.function!.computeFunctionType(Nullability.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitConstructorTearOff(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "ConstructorTearOff(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
}
}
/// Tearing off a redirecting factory constructor of a class.
class RedirectingFactoryTearOff extends Expression {
/// The reference to the redirecting factory constructor being torn off.
Reference targetReference;
RedirectingFactoryTearOff(Procedure target)
: assert(target.isRedirectingFactory),
this.targetReference = getNonNullableMemberReferenceGetter(target);
RedirectingFactoryTearOff.byReference(this.targetReference);
Procedure get target => targetReference.asProcedure;
void set target(Procedure target) {
targetReference = getNonNullableMemberReferenceGetter(target);
}
FunctionNode get function => target.function;
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return target.function.computeFunctionType(Nullability.nonNullable);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitRedirectingFactoryTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitRedirectingFactoryTearOff(this, arg);
@override
void visitChildren(Visitor v) {
target.acceptReference(v);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
String toString() {
return "RedirectingFactoryTearOff(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeMemberName(targetReference);
}
}
class TypedefTearOff extends Expression {
final List<StructuralParameter> structuralParameters;
Expression expression;
final List<DartType> typeArguments;
TypedefTearOff(
this.structuralParameters, this.expression, this.typeArguments) {
expression.parent = this;
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
FreshStructuralParameters freshTypeParameters =
getFreshStructuralParameters(structuralParameters);
FunctionType type = expression.getStaticType(context) as FunctionType;
type = freshTypeParameters.substitute(
FunctionTypeInstantiator.instantiate(type, typeArguments))
as FunctionType;
return new FunctionType(
type.positionalParameters, type.returnType, type.declaredNullability,
namedParameters: type.namedParameters,
typeParameters: freshTypeParameters.freshTypeParameters,
requiredParameterCount: type.requiredParameterCount);
}
@override
R accept<R>(ExpressionVisitor<R> v) => v.visitTypedefTearOff(this);
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
v.visitTypedefTearOff(this, arg);
@override
void visitChildren(Visitor v) {
expression.accept(v);
visitList(structuralParameters, v);
visitList(typeArguments, v);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression);
expression.parent = this;
v.transformDartTypeList(typeArguments);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression);
expression.parent = this;
v.transformDartTypeList(typeArguments);
}
@override
String toString() {
return "TypedefTearOff(${toStringInternal()})";
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeStructuralParameters(structuralParameters);
printer.write(".(");
printer.writeExpression(expression);
printer.writeTypeArguments(typeArguments);
printer.write(")");
}
}