| // 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 '../types.dart'; |
| |
| /// [StaticType] for a record type. |
| /// |
| /// This models that type aspect of the record using only the structure of the |
| /// record type. This means that the type for `(Object, String)` and |
| /// `(String, int)` will be subtypes of each other. |
| /// |
| /// This is necessary to avoid invalid conclusions on the disjointness of |
| /// spaces base on the their types. For instance in |
| /// |
| /// method((String, Object) o) { |
| /// if (o case (Object _, String s)) {} |
| /// } |
| /// |
| /// the case is not empty even though `(String, Object)` and `(Object, String)` |
| /// are not related type-wise. |
| /// |
| /// Not that the fields of the record types _are_ using the type, so that |
| /// the `$1` field of `(String, Object)` is known to contain only `String`s. |
| class RecordStaticType<Type extends Object> extends TypeBasedStaticType<Type> { |
| RecordStaticType(super.typeOperations, super.fieldLookup, super.type); |
| |
| @override |
| bool get isRecord => true; |
| |
| @override |
| bool isSubtypeOfInternal(StaticType other) { |
| if (other is! RecordStaticType<Type>) { |
| return false; |
| } |
| if (fields.length != other.fields.length) { |
| return false; |
| } |
| for (MapEntry<Key, StaticType> field in fields.entries) { |
| StaticType? type = other.fields[field.key]; |
| if (type == null) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @override |
| String spaceToText( |
| Map<Key, Space> spaceFields, Map<Key, Space> additionalSpaceFields) { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.write('('); |
| String comma = ''; |
| fields.forEach((Key key, StaticType staticType) { |
| if (key is RecordIndexKey) { |
| buffer.write(comma); |
| comma = ', '; |
| buffer.write('${spaceFields[key] ?? staticType}'); |
| } else if (key is RecordNameKey) { |
| buffer.write(comma); |
| comma = ', '; |
| buffer.write('${key.name}: ${spaceFields[key] ?? staticType}'); |
| } |
| }); |
| buffer.write(')'); |
| String additionalStart = '('; |
| String additionalEnd = ''; |
| comma = ''; |
| spaceFields.forEach((Key key, Space value) { |
| if (key is! RecordKey) { |
| buffer.write(additionalStart); |
| additionalStart = ''; |
| additionalEnd = ')'; |
| buffer.write(comma); |
| comma = ', '; |
| buffer.write('${key.name}: ${value}'); |
| } |
| }); |
| additionalSpaceFields.forEach((Key key, Space value) { |
| if (key is! RecordKey) { |
| buffer.write(additionalStart); |
| additionalStart = ''; |
| additionalEnd = ')'; |
| buffer.write(comma); |
| comma = ', '; |
| buffer.write('${key.name}: ${value}'); |
| } |
| }); |
| buffer.write(additionalEnd); |
| return buffer.toString(); |
| } |
| |
| @override |
| void witnessToText(StringBuffer buffer, FieldWitness witness, |
| Map<Key, FieldWitness> witnessFields) { |
| buffer.write('('); |
| String comma = ''; |
| for (Key key in fields.keys) { |
| if (key is RecordIndexKey) { |
| buffer.write(comma); |
| comma = ', '; |
| |
| FieldWitness? field = witnessFields[key]; |
| if (field != null) { |
| field.witnessToText(buffer); |
| } else { |
| buffer.write('_'); |
| } |
| } else if (key is RecordNameKey) { |
| buffer.write(comma); |
| comma = ', '; |
| |
| buffer.write(key.name); |
| buffer.write(': '); |
| FieldWitness? field = witnessFields[key]; |
| if (field != null) { |
| field.witnessToText(buffer); |
| } else { |
| buffer.write('_'); |
| } |
| } |
| } |
| buffer.write(')'); |
| |
| // If we have restrictions on the record type we create an and pattern. |
| String additionalStart = ' && Object('; |
| String additionalEnd = ''; |
| comma = ''; |
| for (MapEntry<Key, FieldWitness> entry in witnessFields.entries) { |
| Key key = entry.key; |
| if (key is! RecordKey) { |
| buffer.write(additionalStart); |
| additionalStart = ''; |
| additionalEnd = ')'; |
| buffer.write(comma); |
| comma = ', '; |
| |
| buffer.write(key.name); |
| buffer.write(': '); |
| FieldWitness field = entry.value; |
| field.witnessToText(buffer); |
| } |
| } |
| buffer.write(additionalEnd); |
| } |
| } |