| // 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, | |
| ); |