mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
![Paul Berry](/assets/img/avatar_default.png)
Change-Id: I8ae5e800588ece4e08add7b0c7afe6587c01c478 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200200 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
137 lines
4.5 KiB
Dart
137 lines
4.5 KiB
Dart
// Copyright (c) 2021, 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:convert';
|
|
|
|
import "package:expect/expect.dart";
|
|
|
|
/// Helper class for determining when no argument is passed to a function.
|
|
class Absent {
|
|
const Absent();
|
|
}
|
|
|
|
const absent = Absent();
|
|
|
|
/// Returns an approximate representation of the syntax that was used to
|
|
/// construct [x]. Extra parentheses are used around unary and binary
|
|
/// expressions to disambiguate precedence.
|
|
String syntax(Object? x) {
|
|
var knownSyntax = SyntaxTracker.known[x];
|
|
if (knownSyntax != null) return knownSyntax;
|
|
if (x is SyntaxTracker || x is num) {
|
|
return x.toString();
|
|
} else if (x is List) {
|
|
return '[${x.map(syntax).join(', ')}]';
|
|
} else if (x is Set) {
|
|
if (x.isEmpty) return 'Set()';
|
|
return '{${x.map(syntax).join(', ')}}';
|
|
} else if (x is Map) {
|
|
if (x.isEmpty) return '{}';
|
|
var entries = x.entries
|
|
.map((entry) => '${syntax(entry.key)}: ${syntax(entry.value)}');
|
|
return '{ ${entries.join(', ')} }';
|
|
} else if (x is String) {
|
|
return json.encode(x);
|
|
} else {
|
|
throw UnimplementedError('Unknown syntax for $x. '
|
|
'Consider adding to `SyntaxTracker.known`.');
|
|
}
|
|
}
|
|
|
|
/// Instances of this class record the syntax of operations performed on them.
|
|
class SyntaxTracker {
|
|
final String _syntax;
|
|
|
|
SyntaxTracker(this._syntax);
|
|
|
|
String toString() => _syntax;
|
|
|
|
static String args([Object? x = absent, Object? y = absent]) =>
|
|
'(${[x, y].where((v) => v is! Absent).join(', ')})';
|
|
|
|
static String typeArgs(Type T, Type U) =>
|
|
T == dynamic && U == dynamic ? '' : '<${syntax(T)}, ${syntax(U)}>';
|
|
|
|
/// Simple objects with known syntactic representations. Tests can add to
|
|
/// this map.
|
|
static Map<Object?, String> known = {
|
|
true: 'true',
|
|
false: 'false',
|
|
null: 'null'
|
|
};
|
|
}
|
|
|
|
/// Extension allowing us to detect the syntax of most operations performed on
|
|
/// arbitrary types.
|
|
extension SyntaxTrackingExtension on Object? {
|
|
Object? method<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
|
|
'${syntax(this)}.method${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
|
|
|
|
Object? call<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
|
|
'${syntax(this)}${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
|
|
|
|
Object? get getter => SyntaxTracker('${syntax(this)}.getter');
|
|
|
|
Object? operator [](Object? index) =>
|
|
SyntaxTracker('${syntax(this)}[${syntax(index)}]');
|
|
|
|
Object? operator -() => SyntaxTracker('(-${syntax(this)})');
|
|
|
|
Object? operator ~() => SyntaxTracker('(~${syntax(this)})');
|
|
|
|
Object? operator *(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} * ${syntax(other)})');
|
|
|
|
Object? operator /(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} / ${syntax(other)})');
|
|
|
|
Object? operator ~/(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} ~/ ${syntax(other)})');
|
|
|
|
Object? operator %(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} % ${syntax(other)})');
|
|
|
|
Object? operator +(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} + ${syntax(other)})');
|
|
|
|
Object? operator -(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} - ${syntax(other)})');
|
|
|
|
Object? operator <<(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} << ${syntax(other)})');
|
|
|
|
Object? operator >>(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} >> ${syntax(other)})');
|
|
|
|
Object? operator &(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} & ${syntax(other)})');
|
|
|
|
Object? operator ^(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} ^ ${syntax(other)})');
|
|
|
|
Object? operator |(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} | ${syntax(other)})');
|
|
|
|
Object? operator <(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} < ${syntax(other)})');
|
|
|
|
Object? operator >(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} > ${syntax(other)})');
|
|
|
|
Object? operator <=(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} <= ${syntax(other)})');
|
|
|
|
Object? operator >=(Object? other) =>
|
|
SyntaxTracker('(${syntax(this)} >= ${syntax(other)})');
|
|
}
|
|
|
|
void checkSyntax(Object? x, String expectedSyntax) {
|
|
Expect.equals(expectedSyntax, syntax(x));
|
|
}
|
|
|
|
Object? f<T, U>([Object? x = absent, Object? y = absent]) => SyntaxTracker(
|
|
'f${SyntaxTracker.typeArgs(T, U)}${SyntaxTracker.args(x, y)}');
|
|
|
|
Object? x = SyntaxTracker('x');
|