blob: 163507049092cf974f41df1ef3659b440b1b457e [file]
// Copyright (c) 2023, 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';
sealed class Pattern extends TreeNode {
/// Variable declarations induced by nested variable patterns.
///
/// These variables are initialized to the values captured by the variable
/// patterns nested in the pattern.
List<DeclaredVariable> get declaredVariables;
@override
R accept<R>(PatternVisitor<R> visitor);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg);
/// Returns the variable name that this pattern defines, if any.
///
/// This is used to derive an implicit variable name from a pattern to use
/// on object patterns. For instance
///
/// if (o case Foo(:var bar, :var baz!)) { ... }
///
/// the getter names 'bar' and 'baz' are implicitly defined by the patterns.
String? get variableName => null;
}
/// A [Pattern] based on a constant [Expression].
class ConstantPattern extends Pattern {
Expression expression;
/// Static type of the expression as computed during inference.
DartType expressionType;
/// Reference to the `operator ==` procedure on [expression].
Reference equalsTargetReference;
/// The type of the `operator ==` procedure on [expression].
FunctionType equalsType;
/// The [Constant] value for this constant pattern.
///
/// This is set during constant evaluation.
Constant? value;
new({
required Expression expression,
required DartType expressionType,
required Procedure equalsTarget,
required FunctionType equalsType,
}) : this.byReference(
expression: expression,
expressionType: expressionType,
equalsTargetReference: equalsTarget.reference,
equalsType: equalsType,
);
new byReference({
required this.expression,
required this.expressionType,
required this.equalsTargetReference,
required this.equalsType,
}) {
expression.parent = this;
}
/// The `operator ==` procedure on [expression].
///
/// This is set during inference.
Procedure get equalsTarget => equalsTargetReference.asProcedure;
void set equalsTarget(Procedure value) {
equalsTargetReference = value.reference;
}
@override
List<DeclaredVariable> get declaredVariables => const [];
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitConstantPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitConstantPattern(this, arg);
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
}
@override
void visitChildren(Visitor v) {
expression.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
expression.toTextInternal(printer);
}
@override
String toString() {
return "ConstantPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `pattern && pattern`.
class AndPattern extends Pattern {
Pattern left;
Pattern right;
@override
List<DeclaredVariable> get declaredVariables => [
...left.declaredVariables,
...right.declaredVariables,
];
new(this.left, this.right) {
left.parent = this;
right.parent = this;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitAndPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitAndPattern(this, arg);
@override
void transformChildren(Transformer v) {
left = v.transform(left)..parent = this;
right = v.transform(right)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
left = v.transform(left)..parent = this;
right = v.transform(right)..parent = this;
}
@override
void visitChildren(Visitor v) {
left.accept(v);
right.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
left.toTextInternal(printer);
printer.write(' && ');
right.toTextInternal(printer);
}
@override
String toString() {
return "AndPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `pattern || pattern`.
class OrPattern extends Pattern {
Pattern left;
Pattern right;
final List<DeclaredVariable> orPatternJointVariables;
@override
List<DeclaredVariable> get declaredVariables => orPatternJointVariables;
new(
this.left,
this.right, {
required List<DeclaredVariable> orPatternJointVariables,
}) : orPatternJointVariables = orPatternJointVariables {
left.parent = this;
right.parent = this;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitOrPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitOrPattern(this, arg);
@override
void transformChildren(Transformer v) {
left = v.transform(left)..parent = this;
right = v.transform(right)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
left = v.transform(left)..parent = this;
right = v.transform(right)..parent = this;
}
@override
void visitChildren(Visitor v) {
left.accept(v);
right.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
left.toTextInternal(printer);
printer.write(' || ');
right.toTextInternal(printer);
}
@override
String toString() {
return "OrPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `pattern as type`.
class CastPattern extends Pattern {
Pattern pattern;
DartType type;
new(this.pattern, this.type) {
pattern.parent = this;
}
@override
String? get variableName => pattern.variableName;
@override
List<DeclaredVariable> get declaredVariables => pattern.declaredVariables;
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitCastPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitCastPattern(this, arg);
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
type = v.visitDartType(type);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
type = v.visitDartType(type, cannotRemoveSentinel);
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
type.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
pattern.toTextInternal(printer);
printer.write(' as ');
printer.writeType(type);
}
@override
String toString() {
return "CastPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `pattern!`.
class NullAssertPattern extends Pattern {
Pattern pattern;
new(this.pattern) {
pattern.parent = this;
}
@override
String? get variableName => pattern.variableName;
@override
List<DeclaredVariable> get declaredVariables => pattern.declaredVariables;
@override
R accept<R>(PatternVisitor<R> visitor) =>
visitor.visitNullAssertPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitNullAssertPattern(this, arg);
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
pattern.toTextInternal(printer);
printer.write('!');
}
@override
String toString() {
return "NullAssertPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `pattern?`.
class NullCheckPattern extends Pattern {
Pattern pattern;
new(this.pattern) {
pattern.parent = this;
}
@override
String? get variableName => pattern.variableName;
@override
List<DeclaredVariable> get declaredVariables => pattern.declaredVariables;
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitNullCheckPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitNullCheckPattern(this, arg);
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
pattern.toTextInternal(printer);
printer.write('?');
}
@override
String toString() {
return "NullCheckPattern(${toStringInternal()})";
}
}
/// A [Pattern] for `<typeArgument>[pattern0, ... patternN]`.
class ListPattern extends Pattern {
static const int FlagNeedsCheck = 1 << 0;
static const int FlagHasRestPattern = 1 << 1;
int flags = 0;
/// The element type argument as specified by the list pattern syntax.
DartType? typeArgument;
List<Pattern> patterns;
/// The required type of the pattern.
///
/// This is the type the matched expression is checked against, if the
/// [matchedValueType] is not already a subtype of [requiredType].
DartType requiredType;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
/// If `true`, the matched expression must be checked to be a `List`.
bool get needsCheck => flags & FlagNeedsCheck != 0;
void set needsCheck(bool value) {
flags = value ? (flags | FlagNeedsCheck) : (flags & ~FlagNeedsCheck);
}
/// The most specific type of the matched expression. Either the
/// [requiredType] or the [matchedValueType] if it is a subtype of
/// [requiredType].
///
/// This is the type on which pattern accesses from [patterns] are looked up.
DartType lookupType;
/// If `true`, this list pattern contains a rest pattern.
bool get hasRestPattern => flags & FlagHasRestPattern != 0;
void set hasRestPattern(bool value) {
flags = value
? (flags | FlagHasRestPattern)
: (flags & ~FlagHasRestPattern);
}
/// Reference to the target of the `length` property of the list.
Reference lengthTargetReference;
/// The type of the `length` property of the list.
DartType lengthType;
/// Reference to the method used to check the `length` of the list.
///
/// If this pattern has a rest pattern, this is an `operator >=` method.
/// Otherwise this is an `operator ==` method.
Reference lengthCheckTargetReference;
/// The type of the method used to check the `length` of the list.
///
/// If this pattern has a rest pattern, this is an `operator >=` method.
/// Otherwise this is an `operator ==` method.
FunctionType lengthCheckType;
/// Reference to the target of the `sublist` method of the list.
///
/// This is used if this pattern has a rest pattern with a subpattern.
Reference sublistTargetReference;
/// The type of the `sublist` method of the list.
///
/// This is used if this pattern has a rest pattern with a subpattern.
FunctionType sublistType;
/// Reference to the target of the `minus` method of the `length` of this
/// list.
///
/// This is used to compute tail indices if this pattern has a rest pattern.
Reference minusTargetReference;
/// The type of the `minus` method of the `length` of this list.
///
/// This is used to compute tail indices if this pattern has a rest pattern.
FunctionType minusType;
/// Reference to the target of the `operator []` method of the list.
Reference indexGetTargetReference;
/// The type of the `operator []` method of the list.
FunctionType indexGetType;
@override
List<DeclaredVariable> get declaredVariables => [
for (Pattern pattern in patterns) ...pattern.declaredVariables,
];
new({
required DartType? typeArgument,
required List<Pattern> patterns,
required DartType requiredType,
required DartType matchedValueType,
required bool needsCheck,
required DartType lookupType,
required bool hasRestPattern,
required Member lengthTarget,
required DartType lengthType,
required Procedure lengthCheckTarget,
required FunctionType lengthCheckType,
required Procedure sublistTarget,
required FunctionType sublistType,
required Procedure minusTarget,
required FunctionType minusType,
required Procedure indexGetTarget,
required FunctionType indexGetType,
}) : this.byReference(
typeArgument: typeArgument,
patterns: patterns,
requiredType: requiredType,
matchedValueType: matchedValueType,
lookupType: lookupType,
lengthTargetReference: lengthTarget.reference,
lengthType: lengthType,
lengthCheckTargetReference: lengthCheckTarget.reference,
lengthCheckType: lengthCheckType,
sublistTargetReference: sublistTarget.reference,
sublistType: sublistType,
minusTargetReference: minusTarget.reference,
minusType: minusType,
indexGetTargetReference: indexGetTarget.reference,
indexGetType: indexGetType,
flags:
(needsCheck ? FlagNeedsCheck : 0) |
(hasRestPattern ? FlagHasRestPattern : 0),
);
new byReference({
required this.typeArgument,
required this.patterns,
required this.requiredType,
required this.matchedValueType,
required this.lookupType,
required this.lengthTargetReference,
required this.lengthType,
required this.lengthCheckTargetReference,
required this.lengthCheckType,
required this.sublistTargetReference,
required this.sublistType,
required this.minusTargetReference,
required this.minusType,
required this.indexGetTargetReference,
required this.indexGetType,
required this.flags,
}) {
setParents(patterns, this);
}
/// The target of the `length` property of the list.
Member get lengthTarget => lengthTargetReference.asMember;
void set lengthTarget(Member value) {
lengthTargetReference = value.reference;
}
/// The method used to check the `length` of the list.
///
/// If this pattern has a rest pattern, this is an `operator >=` method. If
/// this is the empty list pattern, this an `operator <=` method. Otherwise
/// this is an `operator ==` method.
Procedure get lengthCheckTarget => lengthCheckTargetReference.asProcedure;
void set lengthCheckTarget(Procedure value) {
lengthCheckTargetReference = value.reference;
}
/// The target of the `sublist` method of the list.
///
/// This is used if this pattern has a rest pattern with a subpattern.
Procedure get sublistTarget => sublistTargetReference.asProcedure;
void set sublistTarget(Procedure value) {
sublistTargetReference = value.reference;
}
/// The target of the `minus` method of the `length` of this list.
///
/// This is used to compute tail indices if this pattern has a rest pattern.
Procedure get minusTarget => minusTargetReference.asProcedure;
void set minusTarget(Procedure value) {
minusTargetReference = value.reference;
}
/// The target of the `operator []` method of the list.
///
/// This is set during inference.
Procedure get indexGetTarget => indexGetTargetReference.asProcedure;
void set indexGetTarget(Procedure value) {
indexGetTargetReference = value.reference;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitListPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitListPattern(this, arg);
@override
void transformChildren(Transformer v) {
if (typeArgument != null) {
typeArgument = v.visitDartType(typeArgument!);
}
v.transformList(patterns, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (typeArgument != null) {
DartType newTypeArgument = v.visitDartType(typeArgument!, dummyDartType);
if (identical(newTypeArgument, dummyDartType)) {
typeArgument = null;
} else {
typeArgument = newTypeArgument;
}
}
v.transformList(patterns, this, dummyPattern);
}
@override
void visitChildren(Visitor v) {
typeArgument?.accept(v);
visitList(patterns, v);
}
@override
void toTextInternal(AstPrinter printer) {
if (typeArgument != null) {
printer.write('<');
printer.writeType(typeArgument!);
printer.write('>');
}
printer.write('[');
String comma = '';
for (Pattern pattern in patterns) {
printer.write(comma);
pattern.toTextInternal(printer);
comma = ', ';
}
printer.write(']');
}
@override
String toString() {
return "ListPattern(${toStringInternal()})";
}
}
class ObjectPattern extends Pattern {
/// The type specified as part of the object pattern syntax.
///
/// This is the type the matched expression is checked against, if the
/// [matchedValueType] is not already a subtype of [requiredType].
///
/// This is the type on which pattern accesses from [fields] are looked up.
///
/// This is set during inference.
DartType requiredType;
final List<NamedPattern> fields;
/// The type of the expression against which this pattern is matched.
///
/// This is set during inference.
DartType matchedValueType;
/// If `true`, the matched expression must be checked to be of type
/// [requiredType].
///
/// This is set during inference.
bool needsCheck;
new({
required this.requiredType,
required this.fields,
required this.matchedValueType,
required this.needsCheck,
}) {
setParents(fields, this);
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitObjectPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitObjectPattern(this, arg);
@override
void transformChildren(Transformer v) {
requiredType = v.visitDartType(requiredType);
v.transformList(fields, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
requiredType = v.visitDartType(requiredType, cannotRemoveSentinel);
v.transformList(fields, this, dummyNamedPattern);
}
@override
void visitChildren(Visitor v) {
requiredType.accept(v);
visitList(fields, v);
}
@override
List<DeclaredVariable> get declaredVariables {
return [for (NamedPattern field in fields) ...field.declaredVariables];
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeType(requiredType);
printer.write('(');
String comma = '';
for (Pattern field in fields) {
printer.write(comma);
field.toTextInternal(printer);
comma = ', ';
}
printer.write(')');
}
@override
String toString() {
return "ObjectPattern(${toStringInternal()})";
}
}
enum RelationalPatternKind {
equals,
notEquals,
lessThan,
lessThanEqual,
greaterThan,
greaterThanEqual,
}
/// A [Pattern] for `operator expression` where `operator is either ==, !=,
/// <, <=, >, or >=.
class RelationalPattern extends Pattern {
final RelationalPatternKind kind;
Expression expression;
/// The type of the [expression].
DartType expressionType;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
/// The access kind for performing the relational operation of this pattern.
RelationalAccessKind accessKind;
/// The name of the relational operation called by this pattern.
Name? name;
/// Reference to the target [Procedure] called by this pattern.
///
/// This is used for [RelationalAccessKind.Instance] and
/// [RelationalAccessKind.Static].
Reference? targetReference;
/// The type arguments passed to [target].
///
/// This is used for [RelationalAccessKind.Static].
DartTypeList? typeArguments;
/// The type of [target].
///
/// This is used for [RelationalAccessKind.Instance] and
/// [RelationalAccessKind.Static].
FunctionType? functionType;
/// The [Constant] value for the [expression].
///
/// This is set during constant evaluation.
Constant? expressionValue;
new({
required RelationalPatternKind kind,
required Expression expression,
required DartType expressionType,
required DartType matchedValueType,
required RelationalAccessKind accessKind,
required Name? name,
required Procedure? target,
required DartTypeList? typeArguments,
required FunctionType? functionType,
}) : this.byReference(
kind: kind,
expression: expression,
expressionType: expressionType,
matchedValueType: matchedValueType,
accessKind: accessKind,
name: name,
targetReference: target?.reference,
typeArguments: typeArguments,
functionType: functionType,
);
new byReference({
required this.kind,
required this.expression,
required this.expressionType,
required this.matchedValueType,
required this.accessKind,
required this.name,
required this.targetReference,
required this.typeArguments,
required this.functionType,
}) {
expression.parent = this;
}
/// The target [Procedure] called by this pattern.
///
/// This is used for [RelationalAccessKind.Instance] and
/// [RelationalAccessKind.Static].
///
/// This is set during inference.
Procedure? get target => targetReference?.asProcedure;
void set target(Procedure? value) {
targetReference = value?.reference;
}
@override
List<DeclaredVariable> get declaredVariables => const [];
@override
R accept<R>(PatternVisitor<R> visitor) =>
visitor.visitRelationalPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitRelationalPattern(this, arg);
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
}
@override
void visitChildren(Visitor v) {
expression.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
switch (kind) {
case RelationalPatternKind.equals:
printer.write('== ');
break;
case RelationalPatternKind.notEquals:
printer.write('!= ');
break;
case RelationalPatternKind.lessThan:
printer.write('< ');
break;
case RelationalPatternKind.lessThanEqual:
printer.write('<= ');
break;
case RelationalPatternKind.greaterThan:
printer.write('> ');
break;
case RelationalPatternKind.greaterThanEqual:
printer.write('>= ');
break;
}
printer.writeExpression(expression);
}
@override
String toString() {
return "RelationalPattern(${toStringInternal()})";
}
}
class WildcardPattern extends Pattern {
DartType? type;
new(this.type);
@override
List<DeclaredVariable> get declaredVariables => const [];
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitWildcardPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitWildcardPattern(this, arg);
@override
void transformChildren(Transformer v) {
if (type != null) {
type = v.visitDartType(type!);
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (type != null) {
DartType newType = v.visitDartType(type!, dummyDartType);
if (identical(newType, dummyDartType)) {
type = null;
} else {
type = newType;
}
}
}
@override
void visitChildren(Visitor v) {
type?.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
if (type != null) {
type!.toTextInternal(printer);
printer.write(" ");
}
printer.write("_");
}
@override
String toString() {
return "WildcardPattern(${toStringInternal()})";
}
}
class AssignedVariablePattern extends Pattern {
static const int FlagNeedsCast = 1 << 0;
static const int FlagHasObservableEffect = 1 << 1;
/// The name of the assigned variable.
@override
final String variableName;
/// The declared type of the assigned variable.
final DartType variableType;
/// The variable used to write to the underlying variable.
///
/// If the variable is lowered late variable, this is the
/// [LocalFunctionVariable] for the local function that writes to the
/// underlying variable. Otherwise this is the underlying variable itself.
final Variable writeVariable;
/// The type of the expression against which this pattern is matched.
final DartType matchedValueType;
/// If `true`, the matched expression must be checked to be of the type
/// of [variable].
final bool needsCast;
/// If `true`, the assignment occurs in a context where effects can be
/// observed and must therefore be postponed until the whole pattern has been
/// evaluated.
///
/// This is used an optimized encoding of pattern assignment. It is sound to
/// assume that all [AssignedVariablePattern]s have an observable effect.
///
/// For instance
///
/// class A {
/// get b => throw 'foo';
/// }
/// class B extends A {
/// get b => 42;
/// }
/// method(A a) {
/// var b1;
/// var b2;
/// A(b: b1) = a;
/// try {
/// A(b: b2) = a;
/// } catch (_) {
/// }
/// print(b1);
/// print(b2);
/// }
///
/// Here the assignment to `b2` has an observable effect where as `b1` has
/// not.
final bool hasObservableEffect;
new({
required this.variableName,
required this.variableType,
required this.writeVariable,
required this.matchedValueType,
required this.needsCast,
required this.hasObservableEffect,
});
@override
R accept<R>(PatternVisitor<R> visitor) =>
visitor.visitAssignedVariablePattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitAssignedVariablePattern(this, arg);
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
List<DeclaredVariable> get declaredVariables => const [];
@override
void toTextInternal(AstPrinter printer) {
printer.write(variableName);
}
@override
String toString() {
return "AssignedVariablePattern(${toStringInternal()})";
}
}
class MapPattern extends Pattern {
static const int FlagNeedsCheck = 1 << 0;
int flags = 0;
/// The key type arguments as specific in the map pattern syntax.
DartType? keyType;
/// The value type arguments as specific in the map pattern syntax.
DartType? valueType;
final List<MapPatternEntry> entries;
/// The required type of the pattern.
///
/// This is the type the matched expression is checked against, if the
/// [matchedValueType] is not already a subtype of [requiredType].
DartType requiredType;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
/// If `true`, the matched expression must be checked to be a `Map`.
///
/// This is set during inference.
bool get needsCheck => flags & FlagNeedsCheck != 0;
void set needsCheck(bool value) {
flags = value ? (flags | FlagNeedsCheck) : (flags & ~FlagNeedsCheck);
}
/// The most specific type of the matched expression. Either the
/// [requiredType] or the [matchedValueType] if it is a subtype of
/// [requiredType].
///
/// This is the type on which pattern accesses from [entries] are looked up.
DartType lookupType;
/// Reference to the target of the `containsKey` method of the map.
Reference containsKeyTargetReference;
/// The type of the `containsKey` method of the map.
FunctionType containsKeyType;
/// Reference to the target of the `operator []` method of the map.
Reference indexGetTargetReference;
/// The type of the `operator []` method of the map.
FunctionType indexGetType;
@override
List<DeclaredVariable> get declaredVariables => [
for (MapPatternEntry entry in entries)
if (entry is! MapPatternRestEntry) ...entry.value.declaredVariables,
];
new({
required DartType? keyType,
required DartType? valueType,
required List<MapPatternEntry> entries,
required DartType requiredType,
required DartType matchedValueType,
required bool needsCheck,
required DartType lookupType,
required Procedure containsKeyTarget,
required FunctionType containsKeyType,
required Procedure indexGetTarget,
required FunctionType indexGetType,
}) : this.byReference(
keyType: keyType,
valueType: valueType,
entries: entries,
requiredType: requiredType,
matchedValueType: matchedValueType,
lookupType: lookupType,
containsKeyTargetReference: containsKeyTarget.reference,
containsKeyType: containsKeyType,
indexGetTargetReference: indexGetTarget.reference,
indexGetType: indexGetType,
flags: needsCheck ? FlagNeedsCheck : 0,
);
new byReference({
required this.keyType,
required this.valueType,
required this.entries,
required this.requiredType,
required this.matchedValueType,
required this.lookupType,
required this.containsKeyTargetReference,
required this.containsKeyType,
required this.indexGetTargetReference,
required this.indexGetType,
required this.flags,
}) : assert((keyType == null) == (valueType == null)) {
setParents(entries, this);
}
/// The target of the `containsKey` method of the map.
Procedure get containsKeyTarget => containsKeyTargetReference.asProcedure;
void set containsKeyTarget(Procedure value) {
containsKeyTargetReference = value.reference;
}
/// The target of the `operator []` method of the map.
Procedure get indexGetTarget => indexGetTargetReference.asProcedure;
void set indexGetTarget(Procedure value) {
indexGetTargetReference = value.reference;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitMapPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitMapPattern(this, arg);
@override
void transformChildren(Transformer v) {
if (keyType != null) {
keyType = v.visitDartType(keyType!);
}
if (valueType != null) {
valueType = v.visitDartType(valueType!);
}
v.transformList(entries, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (keyType != null) {
DartType newType = v.visitDartType(keyType!, dummyDartType);
if (identical(newType, dummyDartType)) {
keyType = null;
} else {
keyType = newType;
}
}
if (valueType != null) {
DartType newType = v.visitDartType(valueType!, dummyDartType);
if (identical(newType, dummyDartType)) {
valueType = null;
} else {
valueType = newType;
}
}
v.transformList(entries, this, dummyMapPatternEntry);
}
@override
void visitChildren(Visitor v) {
keyType?.accept(v);
valueType?.accept(v);
visitList(entries, v);
}
@override
void toTextInternal(AstPrinter printer) {
if (keyType != null && valueType != null) {
printer.writeTypeArguments([keyType!, valueType!]);
}
printer.write('{');
String comma = '';
for (MapPatternEntry entry in entries) {
printer.write(comma);
entry.toTextInternal(printer);
comma = ', ';
}
printer.write('}');
}
@override
String toString() {
return 'MapPattern(${toStringInternal()})';
}
}
class NamedPattern extends Pattern {
final String name;
Pattern pattern;
/// When used in an object pattern, this holds the named of the property
/// accessed by this pattern.
///
/// This is set during inference.
Name fieldName = new Name('#');
/// When used in an object pattern, this holds the access kind of used for
/// reading the property value for this pattern.
///
/// This is set during inference.
ObjectAccessKind accessKind = ObjectAccessKind.Invalid;
/// When used in an object pattern, this holds the reference to the target
/// [Member] used to read the property for this pattern.
///
/// This is used for [ObjectAccessKind.Object], [ObjectAccessKind.Instance],
/// and [ObjectAccessKind.Static].
///
/// This is set during inference.
Reference? targetReference;
/// When used in an object pattern, this holds the static property type for
/// this pattern.
///
/// This is used for [ObjectAccessKind.Object], [ObjectAccessKind.Instance],
/// and [ObjectAccessKind.Static].
///
/// This is set during inference.
DartType? resultType;
/// When used in an object pattern, this is set to `true` if the field value
/// needs to be checked against the [resultType]. This is needed for fields
/// whose type contain covariant types that occur in non-covariant positions.
bool checkReturn = false;
/// When used in an object pattern, this holds the record on which the
/// property for this pattern is read.
///
/// This is used for [ObjectAccessKind.RecordNamed] and
/// [ObjectAccessKind.RecordIndexed].
///
/// This is set during inference.
RecordType? recordType;
/// When used in an object pattern, this holds the record field index from
/// which the property for this pattern is read.
///
/// This is used for [ObjectAccessKind.RecordIndexed].
///
/// This is set during inference.
int recordFieldIndex = -1;
/// When used in an object pattern, this holds the type arguments used when
/// called the [target] to read the property for this pattern.
///
/// This is used for [ObjectAccessKind.Static].
///
/// This is set during inference.
DartTypeList? typeArguments;
@override
List<DeclaredVariable> get declaredVariables => pattern.declaredVariables;
new(this.name, this.pattern) {
pattern.parent = this;
}
/// When used in an object pattern, this holds the target [Member] used to
/// read the property for this pattern.
///
/// This is used for [ObjectAccessKind.Object], [ObjectAccessKind.Instance],
/// and [ObjectAccessKind.Static].
///
/// This is set during inference.
Member? get target => targetReference?.asMember;
void set target(Member? value) {
targetReference = value?.reference;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitNamedPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitNamedPattern(this, arg);
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.write(name);
printer.write(': ');
pattern.toTextInternal(printer);
}
@override
String toString() {
return 'NamedPattern(${toStringInternal()})';
}
}
class RecordPattern extends Pattern {
final List<Pattern> patterns;
/// The required type of the pattern.
///
/// This is the type the matched expression is checked against, if the
/// [matchedValueType] is not already a subtype of [requiredType].
RecordType requiredType;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
/// If `true`, the matched expression must be checked to be of type
/// [requiredType].
bool needsCheck;
/// The most specific type of the matched expression. Either the
/// [requiredType] or the [matchedValueType] if it is a subtype of
/// [requiredType].
///
/// This is the type on which pattern accesses from [patterns] are looked up.
RecordType lookupType;
@override
List<DeclaredVariable> get declaredVariables => [
for (Pattern pattern in patterns) ...pattern.declaredVariables,
];
new({
required this.patterns,
required this.requiredType,
required this.matchedValueType,
required this.needsCheck,
required this.lookupType,
}) {
setParents(patterns, this);
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitRecordPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitRecordPattern(this, arg);
@override
void transformChildren(Transformer v) {
v.transformList(patterns, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformList(patterns, this, dummyPattern);
}
@override
void visitChildren(Visitor v) {
visitList(patterns, v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('(');
String comma = '';
for (Pattern pattern in patterns) {
printer.write(comma);
pattern.toTextInternal(printer);
comma = ', ';
}
printer.write(')');
}
@override
String toString() {
return 'RecordPattern(${toStringInternal()})';
}
}
class VariablePattern extends Pattern {
// TODO(johnniwinther): Should this be accessed through [variable] instead?
DartType? type;
DeclaredVariable variable;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
@override
List<DeclaredVariable> get declaredVariables => [variable];
new({
required this.type,
required this.variable,
required this.matchedValueType,
}) {
variable.parent = this;
}
@override
String? get variableName => variable.cosmeticName;
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitVariablePattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitVariablePattern(this, arg);
@override
void transformChildren(Transformer v) {
if (type != null) {
type = v.visitDartType(type!);
}
variable = v.transform(variable)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (type != null) {
DartType newType = v.visitDartType(type!, dummyDartType);
if (identical(newType, dummyDartType)) {
type = null;
} else {
type = newType;
}
}
variable = v.transform(variable)..parent = this;
}
@override
void visitChildren(Visitor v) {
type?.accept(v);
variable.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
if (type != null) {
type!.toTextInternal(printer);
printer.write(" ");
} else {
printer.write("var ");
}
printer.write(variable.cosmeticName!);
}
@override
String toString() {
return "VariablePattern(${toStringInternal()})";
}
}
class RestPattern extends Pattern {
Pattern? subPattern;
new(this.subPattern) {
subPattern?.parent = this;
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitRestPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitRestPattern(this, arg);
@override
void transformChildren(Transformer v) {
if (subPattern != null) {
subPattern = v.transform(subPattern!)..parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
if (subPattern != null) {
subPattern = v.transform(subPattern!)..parent = this;
}
}
@override
void visitChildren(Visitor v) {
subPattern?.accept(v);
}
@override
List<DeclaredVariable> get declaredVariables =>
subPattern?.declaredVariables ?? const [];
@override
void toTextInternal(AstPrinter printer) {
printer.write('...');
if (subPattern != null) {
subPattern!.toTextInternal(printer);
}
}
@override
String toString() {
return "RestPattern(${toStringInternal()})";
}
}
class InvalidPattern extends Pattern {
Expression invalidExpression;
@override
final List<DeclaredVariable> declaredVariables;
new(this.invalidExpression, {required this.declaredVariables}) {
invalidExpression.parent = this;
setParents(declaredVariables, this);
}
@override
R accept<R>(PatternVisitor<R> visitor) => visitor.visitInvalidPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> visitor, A arg) =>
visitor.visitInvalidPattern(this, arg);
@override
void transformChildren(Transformer v) {
invalidExpression = v.transform(invalidExpression)..parent = this;
v.transformList(declaredVariables, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
invalidExpression = v.transform(invalidExpression)..parent = this;
v.transformVariableList(declaredVariables, this);
}
@override
void visitChildren(Visitor v) {
invalidExpression.accept(v);
visitList(declaredVariables, v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.writeExpression(invalidExpression);
}
@override
String toString() {
return "InvalidPattern(${toStringInternal()})";
}
}
/// Abstract subclass of [Pattern] that can be used to add [Pattern]
/// subclasses from outside `package:kernel`.
abstract class AuxiliaryPattern extends Pattern {
@override
R accept<R>(PatternVisitor<R> v) => v.visitAuxiliaryPattern(this);
@override
R accept1<R, A>(PatternVisitor1<R, A> v, A arg) =>
v.visitAuxiliaryPattern(this, arg);
}
class MapPatternEntry extends TreeNode {
Expression key;
Pattern value;
DartType? keyType;
/// The [Constant] value for the [key] expression.
///
/// This is set during constant evaluation.
Constant? keyValue;
new(this.key, this.value) {
key.parent = this;
value.parent = this;
}
@override
R accept<R>(TreeVisitor<R> v) {
return v.visitMapPatternEntry(this);
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
return v.visitMapPatternEntry(this, arg);
}
@override
void transformChildren(Transformer v) {
key = v.transform(key)..parent = this;
if (keyType != null) {
keyType = v.visitDartType(keyType!);
}
value = v.transform(value)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
key = v.transform(key)..parent = this;
if (keyType != null) {
keyType = v.visitDartType(keyType!, cannotRemoveSentinel);
}
value = v.transform(value)..parent = this;
}
@override
void visitChildren(Visitor v) {
key.accept(v);
keyType?.accept(v);
value.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
key.toTextInternal(printer);
printer.write(': ');
value.toTextInternal(printer);
}
@override
String toString() {
return 'MapPatternEntry(${toStringInternal()})';
}
}
class MapPatternRestEntry extends TreeNode implements MapPatternEntry {
new();
@override
Expression get key => throw new UnsupportedError('MapPatternRestEntry.key');
@override
void set key(Expression value) =>
throw new UnsupportedError('MapPatternRestEntry.key=');
@override
DartType? get keyType =>
throw new UnsupportedError('MapPatternRestEntry.keyType');
@override
void set keyType(DartType? value) =>
throw new UnsupportedError('MapPatternRestEntry.keyType=');
@override
Pattern get value => throw new UnsupportedError('MapPatternRestEntry.value');
@override
void set value(Pattern value) =>
throw new UnsupportedError('MapPatternRestEntry.value=');
@override
Constant? get keyValue =>
throw new UnsupportedError('MapPatternRestEntry.keyValue');
@override
void set keyValue(Constant? value) =>
throw new UnsupportedError('MapPatternRestEntry.keyValue=');
@override
R accept<R>(TreeVisitor<R> v) {
return v.visitMapPatternRestEntry(this);
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
return v.visitMapPatternRestEntry(this, arg);
}
@override
void transformChildren(Transformer v) {}
@override
void transformOrRemoveChildren(RemovingTransformer v) {}
@override
void visitChildren(Visitor v) {}
@override
void toTextInternal(AstPrinter printer) {
printer.write('...');
}
@override
String toString() {
return 'MapPatternRestEntry(${toStringInternal()})';
}
}
/// Kinds of lowerings of relational pattern operations.
enum RelationalAccessKind {
/// Operator defined by an interface member.
Instance,
/// Operator defined by an extension or extension type member.
Static,
/// Operator accessed on a receiver of type `dynamic`.
Dynamic,
/// Operator accessed on a receiver of type `Never`.
Never,
/// Operator accessed on a receiver of an invalid type.
Invalid,
}
/// Kinds of lowerings of objects pattern property access.
enum ObjectAccessKind {
/// Property defined by an `Object` member.
Object,
/// Property defined by an interface member.
Instance,
/// Property defined by an extension member.
Extension,
/// Property defined by an extension type member.
ExtensionType,
/// Named record field property.
RecordNamed,
/// Positional record field property.
RecordIndexed,
/// Property accessed on a receiver of type `dynamic`.
Dynamic,
/// Property accessed on a receiver of type `Never`.
Never,
/// Property accessed on a receiver of an invalid type.
Invalid,
/// Access of `call` on a function.
FunctionTearOff,
/// Erroneous property access.
Error,
/// Access of an extension type representation field.
Direct,
}
/// A [Pattern] with an optional guard [Expression].
class PatternGuard extends TreeNode {
Pattern pattern;
Expression? guard;
new(this.pattern, [this.guard]) {
pattern.parent = this;
guard?.parent = this;
}
@override
R accept<R>(TreeVisitor<R> v) {
return v.visitPatternGuard(this);
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
return v.visitPatternGuard(this, arg);
}
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
if (guard != null) {
guard = v.transform(guard!)..parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
if (guard != null) {
guard = v.transformOrRemoveExpression(guard!)?..parent = this;
}
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
guard?.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
pattern.toTextInternal(printer);
if (guard != null) {
printer.write(' when ');
printer.writeExpression(guard!);
}
}
@override
String toString() => 'PatternGuard(${toStringInternal()})';
}
class PatternSwitchCase extends TreeNode implements SwitchCase, ScopeProvider {
final List<int> caseOffsets;
final List<PatternGuard> patternGuards;
// TODO(johnniwinther): Handle this through serialization. Currently this
// cannot be serialized because we have to way of referring to arbitrary
// statements.
// TODO(johnniwinther): Make this a list of [ContinueSwitchStatement]s.
final List<Statement> labelUsers = [];
@override
Statement body;
@override
bool isDefault;
bool hasLabel;
final List<VariableDeclaration> jointVariableDeclarations;
// TODO(johnniwinther): Serialize this field.
final List<int>? jointVariableFirstUseOffsets;
// TODO(cstefantsova): Serialize this field.
final Scope? jointVariableScope;
@override
Scope? scope;
new(
this.caseOffsets,
this.patternGuards,
this.body, {
required this.isDefault,
required this.hasLabel,
required this.jointVariableDeclarations,
required this.jointVariableFirstUseOffsets,
required this.jointVariableScope,
}) {
setParents(patternGuards, this);
setParents(jointVariableDeclarations, this);
body.parent = this;
}
@override
ExpressionList get expressions =>
throw new UnimplementedError('PatternSwitchCase.expressions');
@override
void set expressions(ExpressionList value) =>
throw new UnimplementedError('PatternSwitchCase.expressions=');
@override
List<int> get expressionOffsets =>
throw new UnimplementedError('PatternSwitchCase.expressionOffsets');
@override
R accept<R>(TreeVisitor<R> v) {
return v.visitPatternSwitchCase(this);
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
return v.visitPatternSwitchCase(this, arg);
}
@override
void transformChildren(Transformer v) {
v.transformList(patternGuards, this);
body = v.transform(body)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformList(patternGuards, this, dummyPatternGuard);
body = v.transform(body)..parent = this;
}
@override
void visitChildren(Visitor v) {
visitList(patternGuards, v);
body.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
for (int index = 0; index < patternGuards.length; index++) {
if (index > 0) {
printer.newLine();
}
printer.write('case ');
patternGuards[index].toTextInternal(printer);
printer.write(':');
}
if (isDefault) {
if (patternGuards.isNotEmpty) {
printer.newLine();
}
printer.write('default:');
}
printer.incIndentation();
Statement? block = body;
if (block is Block) {
for (Statement statement in block.statements) {
printer.newLine();
printer.writeStatement(statement);
}
} else {
printer.write(' ');
printer.writeStatement(body);
}
printer.decIndentation();
}
@override
String toString() {
return "PatternSwitchCase(${toStringInternal()})";
}
}
class PatternSwitchStatement extends Statement implements SwitchStatement {
@override
Expression expression;
@override
final List<PatternSwitchCase> cases;
/// The type of the [expression].
///
/// This is set during inference.
@override
DartType? expressionTypeInternal;
/// `true` if the last case terminates.
///
/// This is set during inference.
// TODO(johnniwinther): Serialize this.
bool lastCaseTerminates = false;
new(this.expression, this.cases) {
expression.parent = this;
setParents(cases, this);
}
@override
DartType get expressionType {
assert(
expressionTypeInternal != null,
"Expression type hasn't been computed for $this.",
);
return expressionTypeInternal!;
}
@override
void set expressionType(DartType value) {
expressionTypeInternal = value;
}
@override
bool isExplicitlyExhaustive = false;
/// Whether the switch has a `default` case.
@override
bool get hasDefault {
// TODO(johnniwinther): Establish this, even for erroneous cases.
//assert(cases.every((c) => c == cases.last || !c.isDefault));
return cases.isNotEmpty && cases.last.isDefault;
}
@override
bool get isExhaustive => throw new UnimplementedError();
@override
R accept<R>(StatementVisitor<R> v) {
return v.visitPatternSwitchStatement(this);
}
@override
R accept1<R, A>(StatementVisitor1<R, A> v, A arg) {
return v.visitPatternSwitchStatement(this, arg);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
v.transformList(cases, this);
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
v.transformList(cases, this, dummyPatternSwitchCase);
}
@override
void visitChildren(Visitor v) {
expression.accept(v);
visitList(cases, v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('switch (');
printer.writeExpression(expression);
printer.write(') {');
printer.incIndentation();
for (SwitchCase switchCase in cases) {
printer.newLine();
printer.writeSwitchCase(switchCase);
}
printer.decIndentation();
printer.newLine();
printer.write('}');
}
@override
String toString() {
return "PatternSwitchStatement(${toStringInternal()})";
}
}
class SwitchExpressionCase extends TreeNode implements ScopeProvider {
PatternGuard patternGuard;
Expression expression;
@override
Scope? scope;
new(this.patternGuard, this.expression) {
patternGuard.parent = this;
expression.parent = this;
}
@override
R accept<R>(TreeVisitor<R> v) {
return v.visitSwitchExpressionCase(this);
}
@override
R accept1<R, A>(TreeVisitor1<R, A> v, A arg) {
return v.visitSwitchExpressionCase(this, arg);
}
@override
void transformChildren(Transformer v) {
patternGuard = v.transform(patternGuard)..parent = this;
expression = v.transform(expression)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
patternGuard = v.transform(patternGuard)..parent = this;
expression = v.transform(expression)..parent = this;
}
@override
void visitChildren(Visitor v) {
patternGuard.accept(v);
expression.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('case ');
patternGuard.toTextInternal(printer);
printer.write(' => ');
printer.writeExpression(expression);
}
@override
String toString() {
return 'SwitchExpressionCase(${toStringInternal()})';
}
}
class SwitchExpression extends Expression {
Expression expression;
final List<SwitchExpressionCase> cases;
/// The type of the [expression].
///
/// This is set during inference.
DartType? expressionType;
/// The resulting type of the switch expression.
///
/// This is set during inference.
DartType? staticType;
new(this.expression, this.cases) {
expression.parent = this;
setParents(cases, this);
}
@override
R accept<R>(ExpressionVisitor<R> v) {
return v.visitSwitchExpression(this);
}
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) {
return v.visitSwitchExpression(this, arg);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
v.transformList(cases, this);
if (staticType != null) {
staticType = v.visitDartType(staticType!);
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
v.transformList(cases, this, dummySwitchExpressionCase);
if (staticType != null) {
staticType = v.visitDartType(staticType!, cannotRemoveSentinel);
}
}
@override
void visitChildren(Visitor v) {
expression.accept(v);
visitList(cases, v);
staticType?.accept(v);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) => staticType!;
@override
void toTextInternal(AstPrinter printer) {
printer.write('switch (');
printer.writeExpression(expression);
printer.write(') {');
String comma = ' ';
for (SwitchExpressionCase switchCase in cases) {
printer.write(comma);
switchCase.toTextInternal(printer);
comma = ', ';
}
printer.write(' }');
}
@override
String toString() => 'SwitchExpression(${toStringInternal()})';
}
class PatternVariableDeclaration extends Statement {
Pattern pattern;
Expression initializer;
final bool isFinal;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
new(
this.pattern,
this.initializer, {
required this.isFinal,
required this.matchedValueType,
}) {
pattern.parent = this;
initializer.parent = this;
}
@override
R accept<R>(StatementVisitor<R> v) {
return v.visitPatternVariableDeclaration(this);
}
@override
R accept1<R, A>(StatementVisitor1<R, A> v, A arg) {
return v.visitPatternVariableDeclaration(this, arg);
}
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
initializer = v.transform(initializer)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
initializer = v.transform(initializer)..parent = this;
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
initializer.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
if (isFinal) {
printer.write('final ');
} else {
printer.write('var ');
}
pattern.toTextInternal(printer);
printer.write(" = ");
printer.writeExpression(initializer);
printer.write(';');
}
@override
String toString() {
return "PatternVariableDeclaration(${toStringInternal()})";
}
}
class PatternAssignment extends Expression {
Pattern pattern;
Expression expression;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
new({
required this.pattern,
required this.expression,
required this.matchedValueType,
}) {
pattern.parent = this;
expression.parent = this;
}
@override
R accept<R>(ExpressionVisitor<R> v) {
return v.visitPatternAssignment(this);
}
@override
R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) {
return v.visitPatternAssignment(this, arg);
}
@override
void transformChildren(Transformer v) {
pattern = v.transform(pattern)..parent = this;
expression = v.transform(expression)..parent = this;
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
pattern = v.transform(pattern)..parent = this;
expression = v.transform(expression)..parent = this;
}
@override
void visitChildren(Visitor v) {
pattern.accept(v);
expression.accept(v);
}
@override
DartType getStaticTypeInternal(StaticTypeContext context) {
return expression.getStaticType(context);
}
@override
void toTextInternal(AstPrinter printer) {
pattern.toTextInternal(printer);
printer.write(' = ');
printer.writeExpression(expression);
}
@override
String toString() {
return "PatternAssignment(${toStringInternal()})";
}
}
/// Statement for a if-case statements:
///
/// if (expression case pattern) then
/// if (expression case pattern) then else otherwise
/// if (expression case pattern when guard) then
/// if (expression case pattern when guard) then else otherwise
///
class IfCaseStatement extends Statement implements ScopeProvider {
Expression expression;
PatternGuard patternGuard;
Statement then;
Statement? otherwise;
/// The type of the expression against which this pattern is matched.
DartType matchedValueType;
@override
Scope? scope;
new({
required this.expression,
required this.patternGuard,
required this.then,
required this.otherwise,
required this.matchedValueType,
}) {
expression.parent = this;
patternGuard.parent = this;
then.parent = this;
otherwise?.parent = this;
}
@override
R accept<R>(StatementVisitor<R> v) {
return v.visitIfCaseStatement(this);
}
@override
R accept1<R, A>(StatementVisitor1<R, A> v, A arg) {
return v.visitIfCaseStatement(this, arg);
}
@override
void transformChildren(Transformer v) {
expression = v.transform(expression)..parent = this;
patternGuard = v.transform(patternGuard)..parent = this;
then = v.transform(then)..parent = this;
if (otherwise != null) {
otherwise = v.transform(otherwise!)..parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
expression = v.transform(expression)..parent = this;
patternGuard = v.transform(patternGuard)..parent = this;
then = v.transform(then)..parent = this;
if (otherwise != null) {
otherwise = v.transformOrRemoveStatement(otherwise!)?..parent = this;
}
}
@override
void visitChildren(Visitor v) {
expression.accept(v);
patternGuard.accept(v);
then.accept(v);
otherwise?.accept(v);
}
@override
void toTextInternal(AstPrinter printer) {
printer.write('if (');
printer.writeExpression(expression);
printer.write(' case ');
patternGuard.toTextInternal(printer);
printer.write(') ');
printer.writeStatement(then);
if (otherwise != null) {
printer.write(' else ');
printer.writeStatement(otherwise!);
}
}
@override
String toString() {
return "IfCaseStatement(${toStringInternal()})";
}
}
final Pattern dummyPattern = new ConstantPattern.byReference(
expression: dummyExpression,
expressionType: dummyDartType,
equalsTargetReference: dummyReference,
equalsType: dummyFunctionType,
);
final FunctionType dummyFunctionType = new FunctionType(
DartTypeList.empty,
dummyDartType,
Nullability.nonNullable,
);
final RecordType dummyRecordType = new RecordType(
DartTypeList.empty,
NamedDartTypeList.empty,
Nullability.nonNullable,
);
final NamedPattern dummyNamedPattern = new NamedPattern('', dummyPattern);
final MapPatternEntry dummyMapPatternEntry = new MapPatternEntry(
dummyExpression,
dummyPattern,
);
final PatternGuard dummyPatternGuard = new PatternGuard(dummyPattern);
final PatternSwitchCase dummyPatternSwitchCase = new PatternSwitchCase(
[],
[],
dummyStatement,
isDefault: true,
hasLabel: false,
jointVariableDeclarations: [],
jointVariableFirstUseOffsets: null,
jointVariableScope: null,
);
final SwitchExpressionCase dummySwitchExpressionCase = new SwitchExpressionCase(
dummyPatternGuard,
dummyExpression,
);