New printer for types and constants
Adds labels to ambiguous types and generates a bullet list of the
origins of all raw types contained in printed types.
Change-Id: I9b8a46aa8b1f463c6cd02b9df042075fc1c46404
Reviewed-on: https://dart-review.googlesource.com/c/84602
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 5e23ccb..efef374 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -8476,6 +8476,55 @@
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, Uri uri_)> templateTypeOrigin =
+ const Template<Message Function(String name, Uri uri_)>(
+ messageTemplate: r"""'#name' is from '#uri'.""",
+ withArguments: _withArgumentsTypeOrigin);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, Uri uri_)> codeTypeOrigin =
+ const Code<Message Function(String name, Uri uri_)>(
+ "TypeOrigin",
+ templateTypeOrigin,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsTypeOrigin(String name, Uri uri_) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ String uri = relativizeUri(uri_);
+ return new Message(codeTypeOrigin,
+ message: """'${name}' is from '${uri}'.""",
+ arguments: {'name': name, 'uri': uri_});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, Uri uri_, Uri uri2_)>
+ templateTypeOriginWithFileUri =
+ const Template<Message Function(String name, Uri uri_, Uri uri2_)>(
+ messageTemplate: r"""'#name' is from '#uri' ('#uri2').""",
+ withArguments: _withArgumentsTypeOriginWithFileUri);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, Uri uri_, Uri uri2_)>
+ codeTypeOriginWithFileUri =
+ const Code<Message Function(String name, Uri uri_, Uri uri2_)>(
+ "TypeOriginWithFileUri",
+ templateTypeOriginWithFileUri,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsTypeOriginWithFileUri(String name, Uri uri_, Uri uri2_) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ String uri = relativizeUri(uri_);
+ String uri2 = relativizeUri(uri2_);
+ return new Message(codeTypeOriginWithFileUri,
+ message: """'${name}' is from '${uri}' ('${uri2}').""",
+ arguments: {'name': name, 'uri': uri_, 'uri2': uri2_});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeTypeVariableDuplicatedName =
messageTypeVariableDuplicatedName;
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
new file mode 100644
index 0000000..ffa60c7
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
@@ -0,0 +1,323 @@
+// Copyright (c) 2018, 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' show json;
+
+import 'package:kernel/ast.dart'
+ show
+ BoolConstant,
+ BottomType,
+ Class,
+ Constant,
+ ConstantMapEntry,
+ DartType,
+ DoubleConstant,
+ DynamicType,
+ Field,
+ FunctionType,
+ InvalidType,
+ InstanceConstant,
+ IntConstant,
+ InterfaceType,
+ ListConstant,
+ MapConstant,
+ NullConstant,
+ PartialInstantiationConstant,
+ Procedure,
+ StringConstant,
+ SymbolConstant,
+ TearOffConstant,
+ TypedefType,
+ TypeLiteralConstant,
+ TypeParameter,
+ TypeParameterType,
+ VoidType;
+
+import 'package:kernel/visitor.dart' show ConstantVisitor, DartTypeVisitor;
+
+import '../blacklisted_classes.dart' show blacklistedCoreClasses;
+
+import '../fasta_codes.dart'
+ show Message, templateTypeOrigin, templateTypeOriginWithFileUri;
+
+/// A pretty-printer for Kernel types and constants with the ability to label
+/// raw types with numeric markers in Dart comments (e.g. `/*1*/`) to
+/// distinguish different types with the same name. This is used in diagnostic
+/// messages to indicate the origins of types occurring in the message.
+class TypeLabeler
+ implements DartTypeVisitor<List<Object>>, ConstantVisitor<List<Object>> {
+ List<LabeledClassName> names = <LabeledClassName>[];
+ Map<String, List<LabeledClassName>> nameMap =
+ <String, List<LabeledClassName>>{};
+
+ /// Pretty-print a type.
+ /// When all types and constants appearing in the same message have been
+ /// pretty-printed, the returned list can be converted to its string
+ /// representation (with labels on duplicated names) by the `join()` method.
+ List<Object> labelType(DartType type) {
+ // TODO(askesc): Remove null check when we are completely null clean here.
+ if (type == null) return ["null-type"];
+ return type.accept(this);
+ }
+
+ /// Pretty-print a constant.
+ /// When all types and constants appearing in the same message have been
+ /// pretty-printed, the returned list can be converted to its string
+ /// representation (with labels on duplicated names) by the `join()` method.
+ List<Object> labelConstant(Constant constant) {
+ // TODO(askesc): Remove null check when we are completely null clean here.
+ if (constant == null) return ["null-constant"];
+ return constant.accept(this);
+ }
+
+ /// Get a textual description of the origins of the raw types appearing in
+ /// types and constants that have been pretty-printed using this labeler.
+ String get originMessages {
+ StringBuffer messages = new StringBuffer();
+ for (LabeledClassName name in names) {
+ messages.write(name.originMessage);
+ }
+ return messages.toString();
+ }
+
+ // We don't have access to coreTypes here, so we have our own Object check.
+ static bool isObject(DartType type) {
+ if (type is InterfaceType && type.classNode.name == 'Object') {
+ Uri importUri = type.classNode.enclosingLibrary.importUri;
+ return importUri.scheme == 'dart' && importUri.path == 'core';
+ }
+ return false;
+ }
+
+ LabeledClassName nameForClass(Class classNode) {
+ List<LabeledClassName> classesForName = nameMap[classNode.name];
+ if (classesForName == null) {
+ // First encountered class with this name
+ LabeledClassName name = new LabeledClassName(classNode, this);
+ names.add(name);
+ nameMap[classNode.name] = [name];
+ return name;
+ } else {
+ for (LabeledClassName classForName in classesForName) {
+ if (classForName.classNode == classNode) {
+ // Previously encountered class
+ return classForName;
+ }
+ }
+ // New class with name that was previously encountered
+ LabeledClassName name = new LabeledClassName(classNode, this);
+ names.add(name);
+ classesForName.add(name);
+ return name;
+ }
+ }
+
+ List<Object> defaultDartType(DartType type) => null;
+ List<Object> visitTypedefType(TypedefType node) => null;
+
+ // TODO(askesc): Throw internal error if InvalidType appears in diagnostics.
+ List<Object> visitInvalidType(InvalidType node) => ["invalid-type"];
+
+ // TODO(askesc): Throw internal error if BottomType appears in diagnostics.
+ List<Object> visitBottomType(BottomType node) => ["bottom-type"];
+
+ List<Object> visitDynamicType(DynamicType node) => ["dynamic"];
+ List<Object> visitVoidType(VoidType node) => ["void"];
+ List<Object> visitTypeParameterType(TypeParameterType node) =>
+ [node.parameter.name];
+
+ List<Object> visitFunctionType(FunctionType node) {
+ List<Object> result = node.returnType.accept(this);
+ result.add(" Function");
+ if (node.typeParameters.isNotEmpty) {
+ result.add("<");
+ bool first = true;
+ for (TypeParameter param in node.typeParameters) {
+ if (!first) result.add(", ");
+ result.add(param.name);
+ if (isObject(param.bound) && param.defaultType is DynamicType) {
+ // Bound was not specified, and therefore should not be printed.
+ } else {
+ result.add(" extends ");
+ result.addAll(param.bound.accept(this));
+ }
+ }
+ result.add(">");
+ }
+ result.add("(");
+ bool first = true;
+ for (int i = 0; i < node.requiredParameterCount; i++) {
+ if (!first) result.add(", ");
+ result.addAll(node.positionalParameters[i].accept(this));
+ first = false;
+ }
+ if (node.positionalParameters.length > node.requiredParameterCount) {
+ if (node.requiredParameterCount > 0) result.add(", ");
+ result.add("[");
+ first = true;
+ for (int i = node.requiredParameterCount;
+ i < node.positionalParameters.length;
+ i++) {
+ if (!first) result.add(", ");
+ result.addAll(node.positionalParameters[i].accept(this));
+ first = false;
+ }
+ result.add("]");
+ }
+ if (node.namedParameters.isNotEmpty) {
+ if (node.positionalParameters.isNotEmpty) result.add(", ");
+ result.add("{");
+ first = true;
+ for (int i = 0; i < node.namedParameters.length; i++) {
+ if (!first) result.add(", ");
+ result.addAll(node.namedParameters[i].type.accept(this));
+ result.add(" ${node.namedParameters[i].name}");
+ first = false;
+ }
+ result.add("}");
+ }
+ result.add(")");
+ return result;
+ }
+
+ List<Object> visitInterfaceType(InterfaceType node) {
+ List<Object> result = [nameForClass(node.classNode)];
+ if (node.typeArguments.isNotEmpty) {
+ result.add("<");
+ bool first = true;
+ for (DartType typeArg in node.typeArguments) {
+ if (!first) result.add(", ");
+ result.addAll(typeArg.accept(this));
+ first = false;
+ }
+ result.add(">");
+ }
+ return result;
+ }
+
+ List<Object> defaultConstant(Constant node) => null;
+
+ List<Object> visitNullConstant(NullConstant node) => [node];
+ List<Object> visitBoolConstant(BoolConstant node) => [node];
+ List<Object> visitIntConstant(IntConstant node) => [node];
+ List<Object> visitDoubleConstant(DoubleConstant node) => [node];
+ List<Object> visitSymbolConstant(SymbolConstant node) => [node];
+ List<Object> visitStringConstant(StringConstant node) =>
+ [json.encode(node.value)];
+
+ List<Object> visitInstanceConstant(InstanceConstant node) {
+ List<Object> result =
+ new InterfaceType(node.klass, node.typeArguments).accept(this);
+ result.add(" {");
+ bool first = true;
+ for (Field field in node.klass.fields) {
+ if (!first) result.add(", ");
+ result.add("${field.name}: ");
+ result.addAll(node.fieldValues[field.reference].accept(this));
+ first = false;
+ }
+ result.add("}");
+ return result;
+ }
+
+ List<Object> visitListConstant(ListConstant node) {
+ List<Object> result = ["<"];
+ result.addAll(node.typeArgument.accept(this));
+ result.add(">[");
+ bool first = true;
+ for (Constant constant in node.entries) {
+ if (!first) result.add(", ");
+ result.addAll(constant.accept(this));
+ first = false;
+ }
+ result.add("]");
+ return result;
+ }
+
+ List<Object> visitMapConstant(MapConstant node) {
+ List<Object> result = ["<"];
+ result.addAll(node.keyType.accept(this));
+ result.add(", ");
+ result.addAll(node.valueType.accept(this));
+ result.add(">{");
+ bool first = true;
+ for (ConstantMapEntry entry in node.entries) {
+ if (!first) result.add(", ");
+ result.addAll(entry.key.accept(this));
+ result.add(": ");
+ result.addAll(entry.value.accept(this));
+ first = false;
+ }
+ result.add("}");
+ return result;
+ }
+
+ List<Object> visitTearOffConstant(TearOffConstant node) {
+ List<Object> result = [];
+ Procedure procedure = node.procedure;
+ Class classNode = procedure.enclosingClass;
+ if (classNode != null) {
+ result.add(nameForClass(classNode));
+ result.add(".");
+ }
+ result.add(procedure.name.name);
+ return result;
+ }
+
+ List<Object> visitPartialInstantiationConstant(
+ PartialInstantiationConstant node) {
+ List<Object> result = node.tearOffConstant.accept(this);
+ if (node.types.isNotEmpty) {
+ result.add("<");
+ bool first = true;
+ for (DartType typeArg in node.types) {
+ if (!first) result.add(", ");
+ result.addAll(typeArg.accept(this));
+ first = false;
+ }
+ result.add(">");
+ }
+ return result;
+ }
+
+ List<Object> visitTypeLiteralConstant(TypeLiteralConstant node) {
+ return node.type.accept(this);
+ }
+}
+
+class LabeledClassName {
+ Class classNode;
+ TypeLabeler typeLabeler;
+
+ LabeledClassName(this.classNode, this.typeLabeler);
+
+ String toString() {
+ String name = classNode.name;
+ List<LabeledClassName> classesForName = typeLabeler.nameMap[name];
+ if (classesForName.length == 1) {
+ return name;
+ }
+ return "$name/*${classesForName.indexOf(this) + 1}*/";
+ }
+
+ String get originMessage {
+ Uri importUri = classNode.enclosingLibrary.importUri;
+ if (importUri.scheme == 'dart' && importUri.path == 'core') {
+ String name = classNode.name;
+ if (blacklistedCoreClasses.contains(name)) {
+ // Blacklisted core class. Only print if ambiguous.
+ List<LabeledClassName> classesForName = typeLabeler.nameMap[name];
+ if (classesForName.length == 1) {
+ return "";
+ }
+ }
+ }
+ Uri fileUri = classNode.enclosingLibrary.fileUri;
+ Message message = (importUri == fileUri || importUri.scheme == 'dart')
+ ? templateTypeOrigin.withArguments(toString(), importUri)
+ : templateTypeOriginWithFileUri.withArguments(
+ toString(), importUri, fileUri);
+ return "\n - " + message.message;
+ }
+}
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index f80e641..8efaefb 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3308,3 +3308,15 @@
foo<X>(X x) => null;
bar<Y>(Y y) => null;
main() { foo(bar); }
+
+# These two message templates are used for constructing supplemental text
+# about the origins of raw interface types in error messages containing types.
+TypeOrigin:
+ template: "'#name' is from '#uri'."
+ frontendInternal: true
+ external: test/type_labeler_test.dart
+
+TypeOriginWithFileUri:
+ template: "'#name' is from '#uri' ('#uri2')."
+ frontendInternal: true
+ external: test/type_labeler_test.dart
diff --git a/pkg/front_end/test/type_labeler_test.dart b/pkg/front_end/test/type_labeler_test.dart
new file mode 100644
index 0000000..f24c86d
--- /dev/null
+++ b/pkg/front_end/test/type_labeler_test.dart
@@ -0,0 +1,248 @@
+// Copyright (c) 2018, 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 'package:kernel/ast.dart';
+
+import 'package:front_end/src/fasta/kernel/type_labeler.dart';
+
+import 'package:expect/expect.dart';
+
+main() {
+ void check(Map<Node, String> expectations, int bulletCount) {
+ TypeLabeler labeler = new TypeLabeler();
+ Map<Node, List<Object>> conversions = {};
+ expectations.forEach((Node node, String expected) {
+ if (node is DartType) {
+ conversions[node] = labeler.labelType(node);
+ } else if (node is Constant) {
+ conversions[node] = labeler.labelConstant(node);
+ } else {
+ Expect.fail("Neither type nor constant");
+ }
+ });
+ expectations.forEach((Node node, String expected) {
+ Expect.stringEquals(expected, conversions[node].join());
+ });
+ int newlines = "\n".allMatches(labeler.originMessages).length;
+ Expect.equals(bulletCount, newlines);
+ }
+
+ // Library mocks
+ Library dartCoreLib = new Library(new Uri(scheme: 'dart', path: 'core'));
+ Library myLib = new Library(Uri.parse("org-dartlang-testcase:///mylib.dart"));
+
+ // Set up some classes
+ Class objectClass = new Class(name: "Object")..parent = dartCoreLib;
+ Supertype objectSuper = new Supertype(objectClass, []);
+ Class boolClass = new Class(name: "bool", supertype: objectSuper)
+ ..parent = dartCoreLib;
+ Class numClass = new Class(name: "num", supertype: objectSuper)
+ ..parent = dartCoreLib;
+ Supertype numSuper = new Supertype(numClass, []);
+ Class intClass = new Class(name: "int", supertype: numSuper)
+ ..parent = dartCoreLib;
+ Class fooClass = new Class(name: "Foo", supertype: objectSuper)
+ ..parent = myLib;
+ Class foo2Class = new Class(name: "Foo", supertype: objectSuper)
+ ..parent = myLib;
+ Class barClass = new Class(
+ name: "Bar",
+ supertype: objectSuper,
+ typeParameters: [new TypeParameter("X")])
+ ..parent = myLib;
+ Class bazClass = new Class(
+ name: "Baz",
+ supertype: objectSuper,
+ typeParameters: [new TypeParameter("X"), new TypeParameter("Y")])
+ ..parent = myLib;
+
+ // Test types
+ DartType voidType = const VoidType();
+ check({voidType: "void"}, 0);
+
+ DartType dynamicType = const DynamicType();
+ check({dynamicType: "dynamic"}, 0);
+
+ DartType boolType = new InterfaceType(boolClass);
+ check({boolType: "bool"}, 0);
+
+ DartType numType = new InterfaceType(numClass);
+ check({numType: "num"}, 0);
+
+ DartType intType = new InterfaceType(intClass);
+ check({intType: "int"}, 0);
+
+ DartType object = new InterfaceType(objectClass);
+ check({object: "Object"}, 1);
+
+ DartType foo = new InterfaceType(fooClass);
+ check({foo: "Foo"}, 1);
+
+ DartType foo2 = new InterfaceType(foo2Class);
+ check({foo2: "Foo"}, 1);
+ check({foo: "Foo/*1*/", foo2: "Foo/*2*/"}, 2);
+
+ DartType barVoid = new InterfaceType(barClass, [voidType]);
+ check({barVoid: "Bar<void>"}, 1);
+
+ DartType barObject = new InterfaceType(barClass, [object]);
+ check({barObject: "Bar<Object>"}, 2);
+
+ DartType barBarDynamic = new InterfaceType(barClass, [
+ new InterfaceType(barClass, [dynamicType])
+ ]);
+ check({barBarDynamic: "Bar<Bar<dynamic>>"}, 1);
+
+ DartType parameterY = new TypeParameterType(new TypeParameter("Y"));
+ DartType barY = new InterfaceType(barClass, [parameterY]);
+ check({parameterY: "Y", barY: "Bar<Y>"}, 1);
+
+ DartType bazFooBarBazDynamicVoid = new InterfaceType(bazClass, [
+ foo,
+ new InterfaceType(barClass, [
+ new InterfaceType(bazClass, [dynamicType, voidType])
+ ])
+ ]);
+ check({bazFooBarBazDynamicVoid: "Baz<Foo, Bar<Baz<dynamic, void>>>"}, 3);
+
+ DartType bazFooFoo2 = new InterfaceType(bazClass, [foo, foo2]);
+ check({bazFooFoo2: "Baz<Foo/*1*/, Foo/*2*/>"}, 3);
+
+ DartType funVoid = new FunctionType([], voidType);
+ check({funVoid: "void Function()"}, 0);
+
+ DartType funFooBarVoid = new FunctionType([foo], barVoid);
+ check({funFooBarVoid: "Bar<void> Function(Foo)"}, 2);
+
+ DartType funFooFoo2 = new FunctionType([foo], foo2);
+ check({funFooFoo2: "Foo/*1*/ Function(Foo/*2*/)"}, 2);
+
+ DartType funOptFooVoid =
+ new FunctionType([foo], voidType, requiredParameterCount: 0);
+ check({funOptFooVoid: "void Function([Foo])"}, 1);
+
+ DartType funFooOptIntVoid =
+ new FunctionType([foo, intType], voidType, requiredParameterCount: 1);
+ check({funFooOptIntVoid: "void Function(Foo, [int])"}, 1);
+
+ DartType funOptFooOptIntVoid =
+ new FunctionType([foo, intType], voidType, requiredParameterCount: 0);
+ check({funOptFooOptIntVoid: "void Function([Foo, int])"}, 1);
+
+ DartType funNamedObjectVoid = new FunctionType([], voidType,
+ namedParameters: [new NamedType("obj", object)]);
+ check({funNamedObjectVoid: "void Function({Object obj})"}, 1);
+
+ DartType funFooNamedObjectVoid = new FunctionType([foo], voidType,
+ namedParameters: [new NamedType("obj", object)]);
+ check({funFooNamedObjectVoid: "void Function(Foo, {Object obj})"}, 2);
+
+ TypeParameter t = new TypeParameter("T", object, dynamicType);
+ DartType funGeneric = new FunctionType(
+ [new TypeParameterType(t)], new TypeParameterType(t),
+ typeParameters: [t]);
+ check({funGeneric: "T Function<T>(T)"}, 0);
+
+ TypeParameter tObject = new TypeParameter("T", object, object);
+ DartType funGenericObject = new FunctionType(
+ [new TypeParameterType(tObject)], new TypeParameterType(tObject),
+ typeParameters: [tObject]);
+ check({funGenericObject: "T Function<T extends Object>(T)"}, 1);
+
+ TypeParameter tFoo = new TypeParameter("T", foo, dynamicType);
+ DartType funGenericFoo = new FunctionType(
+ [new TypeParameterType(tFoo)], new TypeParameterType(tFoo),
+ typeParameters: [tFoo]);
+ check({funGenericFoo: "T Function<T extends Foo>(T)"}, 1);
+
+ TypeParameter tBar = new TypeParameter("T", dynamicType, dynamicType);
+ tBar.bound = new InterfaceType(barClass, [new TypeParameterType(tBar)]);
+ DartType funGenericBar = new FunctionType(
+ [new TypeParameterType(tBar)], new TypeParameterType(tBar),
+ typeParameters: [tBar]);
+ check({funGenericBar: "T Function<T extends Bar<T>>(T)"}, 1);
+
+ // Add some members for testing instance constants
+ Field booField = new Field(new Name("boo"), type: boolType);
+ fooClass.fields.add(booField);
+ Field valueField = new Field(new Name("value"), type: intType);
+ foo2Class.fields.add(valueField);
+ Field nextField = new Field(new Name("next"), type: foo2);
+ foo2Class.fields.add(nextField);
+ Field xField = new Field(new Name("x"),
+ type: new TypeParameterType(bazClass.typeParameters[0]));
+ bazClass.fields.add(xField);
+ Field yField = new Field(new Name("y"),
+ type: new TypeParameterType(bazClass.typeParameters[1]));
+ bazClass.fields.add(yField);
+ FunctionNode gooFunction = new FunctionNode(new EmptyStatement(),
+ typeParameters: [new TypeParameter("V")]);
+ Procedure gooMethod = new Procedure(
+ new Name("goo"), ProcedureKind.Method, gooFunction,
+ isStatic: true)
+ ..parent = fooClass;
+
+ // Test constants
+ Constant nullConst = new NullConstant();
+ check({nullConst: "null"}, 0);
+
+ Constant trueConst = new BoolConstant(true);
+ Constant falseConst = new BoolConstant(false);
+ check({trueConst: "true", falseConst: "false"}, 0);
+
+ Constant intConst = new IntConstant(2);
+ Constant doubleConst = new DoubleConstant(2.5);
+ check({intConst: "2", doubleConst: "2.5"}, 0);
+
+ Constant stringConst = new StringConstant("Don't \"quote\" me on that!");
+ check({stringConst: "\"Don't \\\"quote\\\" me on that!\""}, 0);
+
+ Constant symConst = new SymbolConstant("foo", null);
+ Constant symLibConst = new SymbolConstant("bar", dartCoreLib.reference);
+ check({symConst: "#foo", symLibConst: "#dart:core::bar"}, 0);
+
+ Constant fooConst = new InstanceConstant(
+ fooClass.reference, [], {booField.reference: trueConst});
+ check({fooConst: "Foo {boo: true}"}, 1);
+
+ Constant foo2Const = new InstanceConstant(foo2Class.reference, [],
+ {nextField.reference: nullConst, valueField.reference: intConst});
+ check({foo2Const: "Foo {value: 2, next: null}"}, 1);
+
+ Constant foo2nConst = new InstanceConstant(foo2Class.reference, [], {
+ valueField.reference: intConst,
+ nextField.reference: new InstanceConstant(foo2Class.reference, [],
+ {valueField.reference: intConst, nextField.reference: nullConst}),
+ });
+ check({foo2nConst: "Foo {value: 2, next: Foo {value: 2, next: null}}"}, 1);
+
+ Constant bazFooFoo2Const = new InstanceConstant(bazClass.reference,
+ [foo, foo2], {xField.reference: fooConst, yField.reference: foo2Const});
+ check({
+ bazFooFoo2Const: "Baz<Foo/*1*/, Foo/*2*/> " +
+ "{x: Foo/*1*/ {boo: true}, y: Foo/*2*/ {value: 2, next: null}}"
+ }, 3);
+
+ Constant listConst = new ListConstant(dynamicType, [intConst, doubleConst]);
+ check({listConst: "<dynamic>[2, 2.5]"}, 0);
+
+ Constant listBoolConst = new ListConstant(boolType, [falseConst, trueConst]);
+ check({listBoolConst: "<bool>[false, true]"}, 0);
+
+ Constant mapConst = new MapConstant(boolType, numType, [
+ new ConstantMapEntry(trueConst, intConst),
+ new ConstantMapEntry(falseConst, doubleConst)
+ ]);
+ check({mapConst: "<bool, num>{true: 2, false: 2.5}"}, 0);
+
+ Constant tearOffConst = new TearOffConstant(gooMethod);
+ check({tearOffConst: "Foo.goo"}, 1);
+
+ Constant partialInstantiationConst =
+ new PartialInstantiationConstant(tearOffConst, [intType]);
+ check({partialInstantiationConst: "Foo.goo<int>"}, 1);
+
+ Constant typeLiteralConst = new TypeLiteralConstant(foo);
+ check({typeLiteralConst: "Foo"}, 1);
+}