blob: 21a6f887fae0e30f4035397fe82d360654961b45 [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.
import 'dart_template_buffer.dart';
import 'key.dart';
import 'path.dart';
import 'static_type.dart';
/// Describes a pattern that matches the value or a field accessed from it.
///
/// Used only to generate the witness description.
class Predicate {
/// The path of getters that led from the original matched value to the value
/// tested by this predicate.
final Path path;
/// The static type of the context. [valueType] is a subtype of this type.
final StaticType staticType;
/// The type this predicate tests.
// TODO(johnniwinther): In order to model exhaustiveness on enum types,
// bool values, and maybe integers at some point, we may later want a separate
// kind of predicate that means "this value was equal to this constant".
final StaticType valueType;
Predicate(this.path, this.staticType, this.valueType);
@override
String toString() => 'Predicate(path=$path,type=$valueType)';
}
/// Witness that show an unmatched case.
///
/// This is used to builds a human-friendly pattern-like string for the witness
/// matched by [_predicates].
///
/// For example, given:
///
/// [] is U
/// ['w'] is T
/// ['w', 'x'] is B
/// ['w', 'y'] is B
/// ['z'] is T
/// ['z', 'x'] is C
/// ['z', 'y'] is B
///
/// the [toString] produces:
///
/// 'U(w: T(x: B, y: B), z: T(x: C, y: B))'
class Witness {
final List<Predicate> _predicates;
late final PropertyWitness _witness = _buildWitness();
Witness(this._predicates);
PropertyWitness _buildWitness() {
PropertyWitness witness = new PropertyWitness();
for (Predicate predicate in _predicates) {
PropertyWitness here = witness;
for (Key field in predicate.path.toList()) {
here = here.properties.putIfAbsent(field, () => new PropertyWitness());
}
here.staticType = predicate.staticType;
here.valueType = predicate.valueType;
}
return witness;
}
String get asWitness => _witness.asWitness;
String get asCorrection => _witness.asCorrection;
/// Writes a representation of this witness to the given [buffer].
void toDart(DartTemplateBuffer buffer, {required bool forCorrection}) {
_witness.witnessToDart(buffer, forCorrection: forCorrection);
}
@override
String toString() => _witness.toString();
}
/// Helper class used to turn a list of [Predicate]s into a string.
class PropertyWitness {
StaticType staticType = StaticType.nullableObject;
StaticType valueType = StaticType.nullableObject;
final Map<Key, PropertyWitness> properties = {};
void witnessToDart(DartTemplateBuffer buffer, {required bool forCorrection}) {
if (properties.isNotEmpty) {
Map<StaticType, Map<Key, PropertyWitness>> witnessFieldsByType = {};
for (MapEntry<Key, PropertyWitness> entry in properties.entries) {
Key key = entry.key;
PropertyWitness witness = entry.value;
if (forCorrection && witness.isTrivial) {
continue;
}
if (key is ExtensionKey) {
(witnessFieldsByType[key.receiverType] ??= {})[key] = witness;
} else {
(witnessFieldsByType[valueType] ??= {})[key] = witness;
}
}
if (witnessFieldsByType.isNotEmpty) {
String and = '';
for (MapEntry<StaticType, Map<Key, PropertyWitness>> entry
in witnessFieldsByType.entries) {
StaticType type = entry.key;
Map<Key, PropertyWitness> witnessFields = entry.value;
buffer.write(and);
and = ' && ';
type.witnessToDart(
buffer,
this,
witnessFields,
forCorrection: forCorrection,
);
}
} else {
valueType.witnessToDart(
buffer,
this,
const {},
forCorrection: forCorrection,
);
}
} else {
valueType.witnessToDart(
buffer,
this,
const {},
forCorrection: forCorrection,
);
}
}
bool get isTrivial {
if (!staticType.isSubtypeOf(valueType)) return false;
for (PropertyWitness property in properties.values) {
if (!property.isTrivial) {
return false;
}
}
return true;
}
/// Returns the witness as pattern syntax including all subproperties.
String get asWitness {
DartTemplateBuffer buffer = new SimpleDartBuffer();
witnessToDart(buffer, forCorrection: false);
return buffer.toString();
}
/// Return the witness as pattern syntax without subproperties that fully
/// match the static type.
String get asCorrection {
DartTemplateBuffer buffer = new SimpleDartBuffer();
witnessToDart(buffer, forCorrection: true);
return buffer.toString();
}
@override
String toString() => asWitness;
}