blob: 94b7cf23c078a3ac1d1723846aad7711a53dab45 [file] [log] [blame]
// Copyright (c) 2016, 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.
library kernel.ast_to_text;
import 'dart:core';
import 'dart:convert' show json;
import '../ast.dart';
import '../import_table.dart';
import '../src/text_util.dart';
abstract class Namer<T> {
int index = 0;
final Map<T, String> map = <T, String>{};
String getName(T key) => map.putIfAbsent(key, () => '$prefix${++index}');
String get prefix;
}
class NormalNamer<T> extends Namer<T> {
@override
final String prefix;
NormalNamer(this.prefix);
}
class ConstantNamer extends RecursiveVisitor with Namer<Constant> {
@override
final String prefix;
ConstantNamer(this.prefix);
@override
String getName(Constant constant) {
if (!map.containsKey(constant)) {
// When printing a non-fully linked kernel AST (i.e. some [Reference]s
// are not bound) to text, we need to avoid dereferencing any
// references.
//
// The normal visitor API causes references to be dereferenced in order
// to call the `visit<name>(<name>)` / `visit<name>Reference(<name>)`.
//
// We therefore handle any subclass of [Constant] which has [Reference]s
// specially here.
//
if (constant is InstanceConstant) {
// Avoid visiting `InstanceConstant.classReference`.
for (final Constant value in constant.fieldValues.values) {
// Name everything in post-order visit of DAG.
getName(value);
}
} else if (constant is StaticTearOffConstant) {
// We only care about naming the constants themselves. [TearOffConstant]
// has no Constant children.
// Avoid visiting `TearOffConstant.procedureReference`.
} else {
// Name everything in post-order visit of DAG.
constant.visitChildren(this);
}
}
return super.getName(constant);
}
@override
void defaultConstantReference(Constant constant) {
getName(constant);
}
@override
void defaultDartType(DartType type) {
// No need to recurse into dart types, we only care about naming the
// constants themselves.
}
}
class Disambiguator<T, U> {
final Map<T, String> namesT = <T, String>{};
final Map<U, String> namesU = <U, String>{};
final Set<String> usedNames = new Set<String>();
String disambiguate(T? key1, U? key2, String proposeName()) {
String getNewName() {
String proposedName = proposeName();
if (usedNames.add(proposedName)) return proposedName;
int i = 2;
while (!usedNames.add('$proposedName$i')) {
++i;
}
return '$proposedName$i';
}
if (key1 != null) {
String? result = namesT[key1];
if (result != null) return result;
return namesT[key1] = getNewName();
}
if (key2 != null) {
String? result = namesU[key2];
if (result != null) return result;
return namesU[key2] = getNewName();
}
throw "Cannot disambiguate";
}
}
NameSystem globalDebuggingNames = new NameSystem();
String debugLibraryName(Library? node) {
return node == null
? 'null'
: node.name ?? globalDebuggingNames.nameLibrary(node);
}
String debugClassName(Class? node) {
return node == null ? 'null' : node.name;
}
String debugQualifiedClassName(Class node) {
return debugLibraryName(node.enclosingLibrary) + '::' + debugClassName(node);
}
String debugMemberName(Member node) {
return node.name.text;
}
String debugQualifiedMemberName(Member node) {
if (node.enclosingClass != null) {
return debugQualifiedClassName(node.enclosingClass!) +
'::' +
debugMemberName(node);
} else {
return debugLibraryName(node.enclosingLibrary) +
'::' +
debugMemberName(node);
}
}
String debugTypeParameterName(TypeParameter node) {
return node.name ?? globalDebuggingNames.nameTypeParameter(node);
}
String debugQualifiedTypeParameterName(TypeParameter node) {
TreeNode? parent = node.parent;
if (parent is Class) {
return debugQualifiedClassName(parent) +
'::' +
debugTypeParameterName(node);
}
if (parent is Member) {
return debugQualifiedMemberName(parent) +
'::' +
debugTypeParameterName(node);
}
return debugTypeParameterName(node);
}
String debugVariableDeclarationName(VariableDeclaration node) {
return node.name ?? globalDebuggingNames.nameVariable(node);
}
String debugNodeToString(Node node) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: globalDebuggingNames).writeNode(node);
return '$buffer';
}
String debugLibraryToString(Library library) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: globalDebuggingNames)
.writeLibraryFile(library);
return '$buffer';
}
String debugComponentToString(Component component) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: new NameSystem())
.writeComponentFile(component);
return '$buffer';
}
String componentToString(Component node) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: new NameSystem())
.writeComponentFile(node);
return '$buffer';
}
class NameSystem {
final Namer<VariableDeclaration> variables =
new NormalNamer<VariableDeclaration>('#t');
final Namer<Member> members = new NormalNamer<Member>('#m');
final Namer<Class> classes = new NormalNamer<Class>('#class');
final Namer<Extension> extensions = new NormalNamer<Extension>('#extension');
final Namer<Library> libraries = new NormalNamer<Library>('#lib');
final Namer<TypeParameter> typeParameters =
new NormalNamer<TypeParameter>('#T');
final Namer<TreeNode> labels = new NormalNamer<TreeNode>('#L');
final Namer<Constant> constants = new ConstantNamer('#C');
final Disambiguator<Reference, CanonicalName> prefixes =
new Disambiguator<Reference, CanonicalName>();
String nameVariable(VariableDeclaration node) => variables.getName(node);
String nameMember(Member node) => members.getName(node);
String nameClass(Class node) => classes.getName(node);
String nameExtension(Extension node) => extensions.getName(node);
String nameLibrary(Library node) => libraries.getName(node);
String nameTypeParameter(TypeParameter node) => typeParameters.getName(node);
String nameSwitchCase(SwitchCase node) => labels.getName(node);
String nameLabeledStatement(LabeledStatement node) => labels.getName(node);
String nameConstant(Constant node) => constants.getName(node);
final RegExp pathSeparator = new RegExp('[\\/]');
String nameLibraryPrefix(Library node, {String? proposedName}) {
return prefixes.disambiguate(node.reference, node.reference.canonicalName,
() {
if (proposedName != null) {
return proposedName;
}
String? name = node.name;
if (name != null) {
return abbreviateName(name);
}
// ignore: unnecessary_null_comparison
if (node.importUri != null) {
String path = node.importUri.hasEmptyPath
? '${node.importUri}'
: node.importUri.pathSegments.last;
if (path.endsWith('.dart')) {
path = path.substring(0, path.length - '.dart'.length);
}
return abbreviateName(path);
}
return 'L';
});
}
String nameCanonicalNameAsLibraryPrefix(Reference? node, CanonicalName? name,
{String? proposedName}) {
return prefixes.disambiguate(node, name, () {
if (proposedName != null) return proposedName;
CanonicalName? canonicalName = name ?? node?.canonicalName;
if (canonicalName?.name != null) {
String path = canonicalName!.name;
int slash = path.lastIndexOf(pathSeparator);
if (slash >= 0) {
path = path.substring(slash + 1);
}
if (path.endsWith('.dart')) {
path = path.substring(0, path.length - '.dart'.length);
}
return abbreviateName(path);
}
return 'L';
});
}
final RegExp punctuation = new RegExp('[.:]');
String abbreviateName(String name) {
int dot = name.lastIndexOf(punctuation);
if (dot != -1) {
name = name.substring(dot + 1);
}
if (name.length > 4) {
return name.substring(0, 3);
}
return name;
}
}
abstract class Annotator {
String annotateVariable(Printer printer, VariableDeclaration node);
String annotateReturn(Printer printer, FunctionNode node);
String annotateField(Printer printer, Field node);
}
/// A quick and dirty ambiguous text printer.
class Printer extends Visitor<void> with VisitorVoidMixin {
final NameSystem syntheticNames;
final StringSink sink;
final Annotator? annotator;
final Map<String, MetadataRepository<dynamic>>? metadata;
ImportTable? importTable;
int indentation = 0;
int column = 0;
bool showOffsets;
bool showMetadata;
static int SPACE = 0;
static int WORD = 1;
static int SYMBOL = 2;
int state = SPACE;
Printer(this.sink,
{NameSystem? syntheticNames,
this.showOffsets: false,
this.showMetadata: false,
this.importTable,
this.annotator,
this.metadata})
: this.syntheticNames = syntheticNames ?? new NameSystem();
Printer createInner(ImportTable importTable,
Map<String, MetadataRepository<dynamic>>? metadata) {
return new Printer(sink,
importTable: importTable,
metadata: metadata,
syntheticNames: syntheticNames,
annotator: annotator,
showOffsets: showOffsets,
showMetadata: showMetadata);
}
bool shouldHighlight(Node node) {
return false;
}
void startHighlight(Node node) {}
void endHighlight(Node node) {}
String getLibraryName(Library node) {
return node.name ?? syntheticNames.nameLibrary(node);
}
String getLibraryReference(Library node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Library>';
if (importTable != null && importTable?.getImportIndex(node) != -1) {
return syntheticNames.nameLibraryPrefix(node);
}
return getLibraryName(node);
}
String getClassName(Class node) {
return node.name;
}
String getExtensionName(Extension node) {
return node.name;
}
String getClassReference(Class node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Class>';
String name = getClassName(node);
String library = getLibraryReference(node.enclosingLibrary);
return '$library::$name';
}
String getExtensionReference(Extension node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Extension>';
String name = getExtensionName(node);
String library = getLibraryReference(node.enclosingLibrary);
return '$library::$name';
}
String getTypedefReference(Typedef node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Typedef>';
String library = getLibraryReference(node.enclosingLibrary);
return '$library::${node.name}';
}
static final String emptyNameString = '•';
static final Name emptyName = new Name(emptyNameString);
Name getMemberName(Member node) {
if (node.name.text == '') return emptyName;
// ignore: unnecessary_null_comparison
if (node.name != null) return node.name;
return new Name(syntheticNames.nameMember(node));
}
String getMemberReference(Member node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No Member>';
String name = getMemberName(node).text;
Class? enclosingClass = node.enclosingClass;
if (enclosingClass != null) {
String className = getClassReference(enclosingClass);
return '$className::$name';
} else {
String library = getLibraryReference(node.enclosingLibrary);
return '$library::$name';
}
}
String getVariableName(VariableDeclaration node) {
return node.name ?? syntheticNames.nameVariable(node);
}
String getVariableReference(VariableDeclaration node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No VariableDeclaration>';
return getVariableName(node);
}
String getTypeParameterName(TypeParameter node) {
return node.name ?? syntheticNames.nameTypeParameter(node);
}
String getTypeParameterReference(TypeParameter node) {
// ignore: unnecessary_null_comparison
if (node == null) return '<No TypeParameter>';
String name = getTypeParameterName(node);
TreeNode? parent = node.parent;
if (parent is FunctionNode && parent.parent is Member) {
String member = getMemberReference(parent.parent as Member);
return '$member::$name';
} else if (parent is Class) {
String className = getClassReference(parent);
return '$className::$name';
} else {
return name; // Bound inside a function type.
}
}
void writeComponentProblems(Component component) {
writeProblemsAsJson("Problems in component", component.problemsAsJson);
}
void writeProblemsAsJson(String header, List<String>? problemsAsJson) {
if (problemsAsJson != null && problemsAsJson.isNotEmpty) {
endLine("//");
write("// ");
write(header);
endLine(":");
endLine("//");
for (String s in problemsAsJson) {
Map<String, dynamic> decoded = json.decode(s);
List<dynamic> plainTextFormatted =
decoded["plainTextFormatted"] as List<dynamic>;
List<String> lines = plainTextFormatted.join("\n").split("\n");
for (int i = 0; i < lines.length; i++) {
write("//");
String trimmed = lines[i].trimRight();
if (trimmed.isNotEmpty) write(" ");
endLine(trimmed);
}
if (lines.isNotEmpty) endLine("//");
}
}
}
void writeLibraryFile(Library library) {
writeAnnotationList(library.annotations);
writeWord('library');
String? name = library.name;
if (name != null) {
writeWord(name);
}
List<String> flags = [];
if (library.isUnsupported) {
flags.add('isUnsupported');
}
if (library.isNonNullableByDefault) {
flags.add('isNonNullableByDefault');
}
if (flags.isNotEmpty) {
writeWord('/*${flags.join(',')}*/');
}
endLine(';');
LibraryImportTable imports = new LibraryImportTable(library);
Printer inner = createInner(imports, library.enclosingComponent?.metadata);
inner.writeStandardLibraryContent(library,
outerPrinter: this, importsToPrint: imports);
}
void printLibraryImportTable(LibraryImportTable imports) {
for (Library library in imports.importedLibraries) {
String importPath = imports.getImportPath(library);
if (importPath == "") {
String prefix =
syntheticNames.nameLibraryPrefix(library, proposedName: 'self');
endLine('import self as $prefix;');
} else {
String prefix = syntheticNames.nameLibraryPrefix(library);
endLine('import "$importPath" as $prefix;');
}
}
}
void writeStandardLibraryContent(Library library,
{Printer? outerPrinter, LibraryImportTable? importsToPrint}) {
outerPrinter ??= this;
outerPrinter.writeProblemsAsJson(
"Problems in library", library.problemsAsJson);
if (importsToPrint != null) {
outerPrinter.printLibraryImportTable(importsToPrint);
}
writeAdditionalExports(library.additionalExports);
endLine();
library.dependencies.forEach(writeNode);
if (library.dependencies.isNotEmpty) endLine();
library.parts.forEach(writeNode);
library.typedefs.forEach(writeNode);
library.classes.forEach(writeNode);
library.extensions.forEach(writeNode);
library.fields.forEach(writeNode);
library.procedures.forEach(writeNode);
}
void writeAdditionalExports(List<Reference> additionalExports) {
if (additionalExports.isEmpty) return;
write('additionalExports = (');
for (int i = 0; i < additionalExports.length; i++) {
Reference reference = additionalExports[i];
NamedNode? node = reference.node;
if (node is Class) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name);
} else if (node is Extension) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name);
} else if (node is Field) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name.text);
} else if (node is Procedure) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name.text);
} else if (node is Typedef) {
Library nodeLibrary = node.enclosingLibrary;
String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
write(prefix + '::' + node.name);
} else if (reference.canonicalName != null) {
write(reference.canonicalName.toString());
} else {
throw new UnimplementedError('${node.runtimeType}');
}
if (i + 1 == additionalExports.length) {
endLine(")");
} else {
endLine(",");
write(" ");
}
}
}
void writeComponentFile(Component component) {
ImportTable imports = new ComponentImportTable(component);
Printer inner = createInner(imports, component.metadata);
writeWord('main');
writeSpaced('=');
inner.writeMemberReferenceFromReference(component.mainMethodName);
endLine(';');
if (showMetadata) {
inner.writeMetadata(component);
}
writeComponentProblems(component);
for (Library library in component.libraries) {
if (showMetadata) {
inner.writeMetadata(library);
}
writeAnnotationList(library.annotations);
writeWord('library');
String? name = library.name;
if (name != null) {
writeWord(name);
}
// ignore: unnecessary_null_comparison
if (library.importUri != null) {
writeSpaced('from');
writeWord('"${library.importUri}"');
}
String prefix = syntheticNames.nameLibraryPrefix(library);
writeSpaced('as');
writeWord(prefix);
endLine(' {');
++inner.indentation;
inner.writeStandardLibraryContent(library);
--inner.indentation;
endLine('}');
}
writeConstantTable(component);
}
void writeConstantTable(Component component) {
if (syntheticNames.constants.map.isEmpty) return;
ImportTable imports = new ComponentImportTable(component);
Printer inner = createInner(imports, component.metadata);
writeWord('constants ');
endLine(' {');
++inner.indentation;
for (final Constant constant
in syntheticNames.constants.map.keys.toList()) {
inner.writeNode(constant);
}
--inner.indentation;
endLine('}');
}
int getPrecedence(Expression node) {
return Precedence.of(node);
}
void write(String string) {
sink.write(string);
column += string.length;
}
void writeSpace([String string = ' ']) {
write(string);
state = SPACE;
}
void ensureSpace() {
if (state != SPACE) writeSpace();
}
void writeSymbol(String string) {
write(string);
state = SYMBOL;
}
void writeSpaced(String string) {
ensureSpace();
write(string);
writeSpace();
}
void writeComma([String string = ',']) {
write(string);
writeSpace();
}
void writeWord(String string) {
if (string.isEmpty) return;
ensureWordBoundary();
write(string);
state = WORD;
}
void ensureWordBoundary() {
if (state == WORD) {
writeSpace();
}
}
void writeIndentation() {
writeSpace(' ' * indentation);
}
void writeNode(Node? node) {
if (node == null) {
writeSymbol("<Null>");
} else {
final bool highlight = shouldHighlight(node);
if (highlight) {
startHighlight(node);
}
if (showOffsets && node is TreeNode) {
writeWord("[${node.fileOffset}]");
}
if (showMetadata && node is TreeNode) {
writeMetadata(node);
}
node.accept(this);
if (highlight) {
endHighlight(node);
}
}
}
void writeMetadata(TreeNode node) {
if (metadata != null) {
for (MetadataRepository<dynamic> md in metadata!.values) {
final dynamic nodeMetadata = md.mapping[node];
if (nodeMetadata != null) {
writeWord("[@${md.tag}=${nodeMetadata}]");
}
}
}
}
void writeAnnotatedType(DartType type, String? annotation) {
writeType(type);
if (annotation != null) {
write('/');
write(annotation);
state = WORD;
}
}
void writeType(DartType type) {
// ignore: unnecessary_null_comparison
if (type == null) {
write('<No DartType>');
} else {
type.accept(this);
}
}
void writeOptionalType(DartType type) {
// ignore: unnecessary_null_comparison
if (type != null) {
type.accept(this);
}
}
@override
void visitSupertype(Supertype type) {
// ignore: unnecessary_null_comparison
if (type == null) {
write('<No Supertype>');
} else {
writeClassReferenceFromReference(type.className);
if (type.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(type.typeArguments, writeType);
writeSymbol('>');
}
}
}
@override
void visitTypedefType(TypedefType type) {
writeTypedefReference(type.typedefNode);
if (type.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(type.typeArguments, writeType);
writeSymbol('>');
}
}
void writeModifier(bool isThere, String name) {
if (isThere) {
writeWord(name);
}
}
void writeName(Name name) {
if (name.text == '') {
writeWord(emptyNameString);
} else {
writeWord(name.text); // TODO: write library name
}
}
void endLine([String? string]) {
if (string != null) {
write(string);
}
write('\n');
state = SPACE;
column = 0;
}
void writeFunction(FunctionNode function,
{name, List<Initializer>? initializers, bool terminateLine: true}) {
if (name is String) {
writeWord(name);
} else if (name is Name) {
writeName(name);
} else {
assert(name == null);
}
writeTypeParameterList(function.typeParameters);
writeParameterList(function.positionalParameters, function.namedParameters,
function.requiredParameterCount);
writeReturnType(
function.returnType, annotator?.annotateReturn(this, function));
if (initializers != null && initializers.isNotEmpty) {
endLine();
++indentation;
writeIndentation();
writeComma(':');
writeList(initializers, writeNode);
--indentation;
}
if (function.asyncMarker != AsyncMarker.Sync) {
writeSpaced(getAsyncMarkerKeyword(function.asyncMarker));
}
if (function.futureValueType != null) {
writeSpaced("/* futureValueType=");
writeNode(function.futureValueType);
writeSpaced("*/");
}
if (function.dartAsyncMarker != AsyncMarker.Sync &&
function.dartAsyncMarker != function.asyncMarker) {
writeSpaced("/* originally");
writeSpaced(getAsyncMarkerKeyword(function.dartAsyncMarker));
writeSpaced("*/");
}
Statement? body = function.body;
if (body != null) {
writeFunctionBody(body, terminateLine: terminateLine);
} else if (terminateLine) {
endLine(';');
} else {
writeSymbol(';');
}
}
String getAsyncMarkerKeyword(AsyncMarker marker) {
switch (marker) {
case AsyncMarker.Sync:
return 'sync';
case AsyncMarker.SyncStar:
return 'sync*';
case AsyncMarker.Async:
return 'async';
case AsyncMarker.AsyncStar:
return 'async*';
case AsyncMarker.SyncYielding:
return 'yielding';
default:
return '<Invalid async marker: $marker>';
}
}
void writeFunctionBody(Statement body, {bool terminateLine: true}) {
if (body is Block && body.statements.isEmpty) {
ensureSpace();
writeSymbol('{}');
state = WORD;
if (terminateLine) {
endLine();
}
} else if (body is Block) {
ensureSpace();
endLine('{');
++indentation;
body.statements.forEach(writeNode);
--indentation;
writeIndentation();
writeSymbol('}');
state = WORD;
if (terminateLine) {
endLine();
}
} else if (body is ReturnStatement && !terminateLine) {
writeSpaced('=>');
writeExpression(body.expression!);
} else {
writeBody(body);
}
}
void writeFunctionType(FunctionType node,
{List<VariableDeclaration>? typedefPositional,
List<VariableDeclaration>? typedefNamed}) {
if (state == WORD) {
ensureSpace();
}
writeTypeParameterList(node.typeParameters);
writeSymbol('(');
List<DartType> positional = node.positionalParameters;
bool parametersAnnotated = false;
if (typedefPositional != null) {
for (VariableDeclaration formal in typedefPositional) {
parametersAnnotated =
parametersAnnotated || formal.annotations.length > 0;
}
}
if (typedefNamed != null) {
for (VariableDeclaration formal in typedefNamed) {
parametersAnnotated =
parametersAnnotated || formal.annotations.length > 0;
}
}
if (parametersAnnotated && typedefPositional != null) {
writeList(typedefPositional.take(node.requiredParameterCount),
writeVariableDeclaration);
} else {
writeList(positional.take(node.requiredParameterCount), writeType);
}
if (node.requiredParameterCount < positional.length) {
if (node.requiredParameterCount > 0) {
writeComma();
}
writeSymbol('[');
if (parametersAnnotated && typedefPositional != null) {
writeList(typedefPositional.skip(node.requiredParameterCount),
writeVariableDeclaration);
} else {
writeList(positional.skip(node.requiredParameterCount), writeType);
}
writeSymbol(']');
}
if (node.namedParameters.isNotEmpty) {
if (node.positionalParameters.isNotEmpty) {
writeComma();
}
writeSymbol('{');
if (parametersAnnotated && typedefNamed != null) {
writeList(typedefNamed, writeVariableDeclaration);
} else {
writeList(node.namedParameters, visitNamedType);
}
writeSymbol('}');
}
writeSymbol(')');
ensureSpace();
write('→');
writeNullability(node.nullability);
writeSpace();
writeType(node.returnType);
}
void writeBody(Statement body) {
if (body is Block) {
endLine(' {');
++indentation;
body.statements.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
} else {
endLine();
++indentation;
writeNode(body);
--indentation;
}
}
void writeReturnType(DartType type, String? annotation) {
// ignore: unnecessary_null_comparison
if (type == null) return;
writeSpaced('→');
writeAnnotatedType(type, annotation);
}
void writeTypeParameterList(List<TypeParameter> typeParameters) {
if (typeParameters.isEmpty) return;
writeSymbol('<');
writeList(typeParameters, writeNode);
writeSymbol('>');
state = WORD; // Ensure space if not followed by another symbol.
}
void writeParameterList(List<VariableDeclaration> positional,
List<VariableDeclaration> named, int requiredParameterCount) {
writeSymbol('(');
writeList(
positional.take(requiredParameterCount), writeVariableDeclaration);
if (requiredParameterCount < positional.length) {
if (requiredParameterCount > 0) {
writeComma();
}
writeSymbol('[');
writeList(
positional.skip(requiredParameterCount), writeVariableDeclaration);
writeSymbol(']');
}
if (named.isNotEmpty) {
if (positional.isNotEmpty) {
writeComma();
}
writeSymbol('{');
writeList(named, writeVariableDeclaration);
writeSymbol('}');
}
writeSymbol(')');
}
void writeList<T>(Iterable<T> nodes, void callback(T x),
{String separator: ','}) {
bool first = true;
for (T node in nodes) {
if (first) {
first = false;
} else {
writeComma(separator);
}
callback(node);
}
}
void writeClassReferenceFromReference(Reference reference) {
writeWord(getClassReferenceFromReference(reference));
}
String getClassReferenceFromReference(Reference reference) {
// ignore: unnecessary_null_comparison
if (reference == null) return '<No Class>';
if (reference.node != null) return getClassReference(reference.asClass);
if (reference.canonicalName != null) {
return getCanonicalNameString(reference.canonicalName!);
}
throw "Neither node nor canonical name found";
}
void writeExtensionReferenceFromReference(Reference reference) {
writeWord(getExtensionReferenceFromReference(reference));
}
String getExtensionReferenceFromReference(Reference reference) {
// ignore: unnecessary_null_comparison
if (reference == null) return '<No Extension>';
if (reference.node != null) {
return getExtensionReference(reference.asExtension);
}
if (reference.canonicalName != null) {
return getCanonicalNameString(reference.canonicalName!);
}
throw "Neither node nor canonical name found";
}
void writeMemberReferenceFromReference(Reference? reference) {
writeWord(getMemberReferenceFromReference(reference));
}
String getMemberReferenceFromReference(Reference? reference) {
if (reference == null) return '<No Member>';
if (reference.node != null) return getMemberReference(reference.asMember);
if (reference.canonicalName != null) {
return getCanonicalNameString(reference.canonicalName!);
}
throw "Neither node nor canonical name found";
}
String getCanonicalNameString(CanonicalName name) {
if (name.isRoot) throw 'unexpected root';
if (name.name.startsWith('@')) throw 'unexpected @';
String libraryString(CanonicalName lib) {
if (lib.reference.node != null) {
return getLibraryReference(lib.reference.asLibrary);
}
return syntheticNames.nameCanonicalNameAsLibraryPrefix(
lib.reference, lib);
}
String classString(CanonicalName cls) =>
libraryString(cls.parent!) + '::' + cls.name;
if (name.parent!.isRoot) return libraryString(name);
if (name.parent!.parent!.isRoot) return classString(name);
CanonicalName atNode = name.parent!;
while (!atNode.name.startsWith('@')) {
atNode = atNode.parent!;
}
String parent = "";
if (atNode.parent!.parent!.isRoot) {
parent = libraryString(atNode.parent!);
} else {
parent = classString(atNode.parent!);
}
if (name.name == '') return "$parent::$emptyNameString";
return "$parent::${name.name}";
}
void writeTypedefReference(Typedef typedefNode) {
writeWord(getTypedefReference(typedefNode));
}
void writeVariableReference(VariableDeclaration variable) {
final bool highlight = shouldHighlight(variable);
if (highlight) {
startHighlight(variable);
}
writeWord(getVariableReference(variable));
if (highlight) {
endHighlight(variable);
}
}
void writeTypeParameterReference(TypeParameter node) {
writeWord(getTypeParameterReference(node));
}
void writeExpression(Expression node, [int? minimumPrecedence]) {
final bool highlight = shouldHighlight(node);
if (highlight) {
startHighlight(node);
}
if (showOffsets) writeWord("[${node.fileOffset}]");
bool needsParenteses = false;
if (minimumPrecedence != null && getPrecedence(node) < minimumPrecedence) {
needsParenteses = true;
writeSymbol('(');
}
writeNode(node);
if (needsParenteses) {
writeSymbol(')');
}
if (highlight) {
endHighlight(node);
}
}
void writeAnnotation(Expression node) {
writeSymbol('@');
if (node is ConstructorInvocation) {
writeMemberReferenceFromReference(node.targetReference);
visitArguments(node.arguments);
} else {
writeExpression(node);
}
}
void writeAnnotationList(List<Expression> nodes, {bool separateLines: true}) {
for (Expression node in nodes) {
if (separateLines) {
writeIndentation();
}
writeAnnotation(node);
if (separateLines) {
endLine();
} else {
writeSpace();
}
}
}
@override
void visitLibrary(Library node) {}
@override
void visitField(Field node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isLate, 'late');
writeModifier(node.isStatic, 'static');
writeModifier(node.isCovariantByDeclaration, 'covariant-by-declaration');
writeModifier(node.isCovariantByClass, 'covariant-by-class');
writeModifier(node.isFinal, 'final');
writeModifier(node.isConst, 'const');
// Only show implicit getter/setter modifiers in cases where they are
// out of the ordinary.
if (node.isFinal) {
writeModifier(node.hasSetter, '[setter]');
}
writeWord('field');
writeSpace();
writeAnnotatedType(node.type, annotator?.annotateField(this, node));
writeName(getMemberName(node));
Expression? initializer = node.initializer;
if (initializer != null) {
writeSpaced('=');
writeExpression(initializer);
}
List<String> features = <String>[];
if (node.enclosingLibrary.isNonNullableByDefault !=
node.isNonNullableByDefault) {
if (node.isNonNullableByDefault) {
features.add("isNonNullableByDefault");
} else {
features.add("isLegacy");
}
}
Class? enclosingClass = node.enclosingClass;
if ((enclosingClass == null &&
node.enclosingLibrary.fileUri != node.fileUri) ||
(enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
features.add(" from ${node.fileUri} ");
}
if (features.isNotEmpty) {
writeWord("/*${features.join(',')}*/");
}
endLine(';');
}
@override
void visitProcedure(Procedure node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isExternal, 'external');
writeModifier(node.isStatic, 'static');
writeModifier(node.isAbstract, 'abstract');
writeModifier(node.isForwardingStub, 'forwarding-stub');
writeModifier(node.isForwardingSemiStub, 'forwarding-semi-stub');
switch (node.stubKind) {
case ProcedureStubKind.Regular:
case ProcedureStubKind.AbstractForwardingStub:
case ProcedureStubKind.ConcreteForwardingStub:
break;
case ProcedureStubKind.NoSuchMethodForwarder:
writeWord('no-such-method-forwarder');
break;
case ProcedureStubKind.MemberSignature:
writeWord('member-signature');
break;
case ProcedureStubKind.AbstractMixinStub:
writeWord('mixin-stub');
break;
case ProcedureStubKind.ConcreteMixinStub:
writeWord('mixin-super-stub');
break;
}
writeWord(procedureKindToString(node.kind));
List<String> features = <String>[];
if (node.enclosingLibrary.isNonNullableByDefault !=
node.isNonNullableByDefault) {
if (node.isNonNullableByDefault) {
features.add("isNonNullableByDefault");
} else {
features.add("isLegacy");
}
}
Class? enclosingClass = node.enclosingClass;
if ((enclosingClass == null &&
node.enclosingLibrary.fileUri != node.fileUri) ||
(enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
features.add(" from ${node.fileUri} ");
}
if (features.isNotEmpty) {
writeWord("/*${features.join(',')}*/");
}
if (node.signatureType != null) {
writeWord('/* signature-type:');
writeType(node.signatureType!);
writeWord('*/');
}
switch (node.stubKind) {
case ProcedureStubKind.Regular:
case ProcedureStubKind.AbstractForwardingStub:
case ProcedureStubKind.ConcreteForwardingStub:
case ProcedureStubKind.NoSuchMethodForwarder:
case ProcedureStubKind.ConcreteMixinStub:
writeFunction(node.function, name: getMemberName(node));
break;
case ProcedureStubKind.MemberSignature:
case ProcedureStubKind.AbstractMixinStub:
writeFunction(node.function,
name: getMemberName(node), terminateLine: false);
if (node.function.body is ReturnStatement) {
writeSymbol(';');
}
writeSymbol(' -> ');
writeMemberReferenceFromReference(node.stubTargetReference!);
endLine();
break;
}
}
@override
void visitConstructor(Constructor node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isExternal, 'external');
writeModifier(node.isConst, 'const');
writeModifier(node.isSynthetic, 'synthetic');
writeWord('constructor');
List<String> features = <String>[];
if (node.enclosingLibrary.isNonNullableByDefault !=
node.isNonNullableByDefault) {
if (node.isNonNullableByDefault) {
features.add("isNonNullableByDefault");
} else {
features.add("isLegacy");
}
}
if (features.isNotEmpty) {
writeWord("/*${features.join(',')}*/");
}
writeFunction(node.function,
name: node.name, initializers: node.initializers);
}
@override
void visitRedirectingFactory(RedirectingFactory node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isExternal, 'external');
writeModifier(node.isConst, 'const');
writeWord('redirecting_factory');
writeFunction(node.function, name: node.name);
writeSpaced('=');
writeMemberReferenceFromReference(node.targetReference!);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
List<String> features = <String>[];
if (node.enclosingLibrary.isNonNullableByDefault !=
node.isNonNullableByDefault) {
if (node.isNonNullableByDefault) {
features.add("isNonNullableByDefault");
} else {
features.add("isLegacy");
}
}
if (features.isNotEmpty) {
writeWord("/*${features.join(',')}*/");
}
endLine(';');
}
@override
void visitClass(Class node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeModifier(node.isAbstract, 'abstract');
writeModifier(node.isMacro, 'macro');
writeWord('class');
writeWord(getClassName(node));
writeTypeParameterList(node.typeParameters);
if (node.isMixinApplication) {
writeSpaced('=');
visitSupertype(node.supertype!);
writeSpaced('with');
visitSupertype(node.mixedInType!);
} else if (node.supertype != null) {
writeSpaced('extends');
visitSupertype(node.supertype!);
}
if (node.implementedTypes.isNotEmpty) {
writeSpaced('implements');
writeList(node.implementedTypes, visitSupertype);
}
List<String> features = <String>[];
if (node.isEnum) {
features.add('isEnum');
}
if (node.isAnonymousMixin) {
features.add('isAnonymousMixin');
}
if (node.isEliminatedMixin) {
features.add('isEliminatedMixin');
}
if (node.isMixinDeclaration) {
features.add('isMixinDeclaration');
}
if (node.hasConstConstructor) {
features.add('hasConstConstructor');
}
if (features.isNotEmpty) {
writeSpaced('/*${features.join(',')}*/');
}
String endLineString = ' {';
if (node.enclosingLibrary.fileUri != node.fileUri) {
endLineString += ' // from ${node.fileUri}';
}
endLine(endLineString);
++indentation;
node.fields.forEach(writeNode);
node.constructors.forEach(writeNode);
node.procedures.forEach(writeNode);
node.redirectingFactories.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
}
@override
void visitExtension(Extension node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord('extension');
if (node.isExtensionTypeDeclaration) {
writeWord('type');
}
writeWord(getExtensionName(node));
writeTypeParameterList(node.typeParameters);
writeSpaced('on');
writeType(node.onType);
ExtensionTypeShowHideClause? showHideClause = node.showHideClause;
if (showHideClause != null) {
// 'Show' clause elements.
if (showHideClause.shownSupertypes.isNotEmpty) {
writeSpaced('show-types');
writeList(showHideClause.shownSupertypes, visitSupertype);
}
if (showHideClause.shownMethods.isNotEmpty) {
writeSpaced('show-methods');
writeList(
showHideClause.shownMethods, writeMemberReferenceFromReference);
}
if (showHideClause.shownGetters.isNotEmpty) {
writeSpaced('show-getters');
writeList(
showHideClause.shownGetters, writeMemberReferenceFromReference);
}
if (showHideClause.shownSetters.isNotEmpty) {
writeSpaced('show-setters');
writeList(
showHideClause.shownSetters, writeMemberReferenceFromReference);
}
if (showHideClause.shownOperators.isNotEmpty) {
writeSpaced('show-operators');
writeList(
showHideClause.shownOperators, writeMemberReferenceFromReference);
}
// 'Hide' clause elements.
if (showHideClause.hiddenSupertypes.isNotEmpty) {
writeSpaced('hide-types');
writeList(showHideClause.hiddenSupertypes, visitSupertype);
}
if (showHideClause.hiddenMethods.isNotEmpty) {
writeSpaced('hide-methods');
writeList(
showHideClause.hiddenMethods, writeMemberReferenceFromReference);
}
if (showHideClause.hiddenGetters.isNotEmpty) {
writeSpaced('hide-getters');
writeList(
showHideClause.hiddenGetters, writeMemberReferenceFromReference);
}
if (showHideClause.hiddenSetters.isNotEmpty) {
writeSpaced('hide-setters');
writeList(
showHideClause.hiddenSetters, writeMemberReferenceFromReference);
}
if (showHideClause.hiddenOperators.isNotEmpty) {
writeSpaced('hide-operators');
writeList(
showHideClause.hiddenOperators, writeMemberReferenceFromReference);
}
}
String endLineString = ' {';
if (node.enclosingLibrary.fileUri != node.fileUri) {
endLineString += ' // from ${node.fileUri}';
}
endLine(endLineString);
++indentation;
node.members.forEach((ExtensionMemberDescriptor descriptor) {
writeIndentation();
writeModifier(descriptor.isStatic, 'static');
switch (descriptor.kind) {
case ExtensionMemberKind.Method:
writeWord('method');
break;
case ExtensionMemberKind.Getter:
writeWord('get');
break;
case ExtensionMemberKind.Setter:
writeWord('set');
break;
case ExtensionMemberKind.Operator:
writeWord('operator');
break;
case ExtensionMemberKind.Field:
writeWord('field');
break;
case ExtensionMemberKind.TearOff:
writeWord('tearoff');
break;
}
writeName(descriptor.name);
writeSpaced('=');
Member member = descriptor.member.asMember;
if (member is Procedure) {
if (member.isGetter) {
writeWord('get');
} else if (member.isSetter) {
writeWord('set');
}
}
writeMemberReferenceFromReference(descriptor.member);
endLine(';');
});
--indentation;
writeIndentation();
endLine('}');
}
@override
void visitTypedef(Typedef node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord('typedef');
writeWord(node.name);
writeTypeParameterList(node.typeParameters);
writeSpaced('=');
DartType? type = node.type;
if (type is FunctionType) {
writeFunctionType(type,
typedefPositional: node.positionalParameters,
typedefNamed: node.namedParameters);
} else {
writeNode(type);
}
endLine(';');
}
@override
void visitInvalidExpression(InvalidExpression node) {
writeWord('invalid-expression');
if (node.message != null) {
writeWord('"${escapeString(node.message!)}"');
}
if (node.expression != null) {
writeSpaced('in');
writeNode(node.expression!);
}
}
void _writeDynamicAccessKind(DynamicAccessKind kind) {
switch (kind) {
case DynamicAccessKind.Dynamic:
writeSymbol('{dynamic}.');
break;
case DynamicAccessKind.Never:
writeSymbol('{Never}.');
break;
case DynamicAccessKind.Invalid:
writeSymbol('{<invalid>}.');
break;
case DynamicAccessKind.Unresolved:
writeSymbol('{<unresolved>}.');
break;
}
}
@override
void visitDynamicInvocation(DynamicInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
_writeDynamicAccessKind(node.kind);
writeName(
node.name,
);
writeNode(node.arguments);
}
void _writeFunctionAccessKind(FunctionAccessKind kind) {
switch (kind) {
case FunctionAccessKind.Function:
case FunctionAccessKind.FunctionType:
break;
case FunctionAccessKind.Inapplicable:
writeSymbol('{<inapplicable>}.');
break;
case FunctionAccessKind.Nullable:
writeSymbol('{<nullable>}.');
break;
}
}
@override
void visitFunctionInvocation(FunctionInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
_writeFunctionAccessKind(node.kind);
writeNode(node.arguments);
if (node.functionType != null) {
writeSymbol('{');
writeType(node.functionType!);
writeSymbol('}');
}
}
@override
void visitLocalFunctionInvocation(LocalFunctionInvocation node) {
writeVariableReference(node.variable);
writeNode(node.arguments);
writeSymbol('{');
writeType(node.functionType);
writeSymbol('}');
}
void _writeInstanceAccessKind(InstanceAccessKind kind) {
switch (kind) {
case InstanceAccessKind.Instance:
case InstanceAccessKind.Object:
break;
case InstanceAccessKind.Inapplicable:
writeSymbol('{<inapplicable>}.');
break;
case InstanceAccessKind.Nullable:
writeSymbol('{<nullable>}.');
break;
}
}
@override
void visitInstanceInvocation(InstanceInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
_writeInstanceAccessKind(node.kind);
List<String> flags = <String>[];
if (node.isInvariant) {
flags.add('Invariant');
}
if (node.isBoundsSafe) {
flags.add('BoundsSafe');
}
if (flags.isNotEmpty) {
write('{${flags.join(',')}}');
}
writeNode(node.arguments);
writeSymbol('{');
writeType(node.functionType);
writeSymbol('}');
}
@override
void visitInstanceGetterInvocation(InstanceGetterInvocation node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
_writeInstanceAccessKind(node.kind);
writeNode(node.arguments);
if (node.functionType != null) {
writeSymbol('{');
writeType(node.functionType!);
writeSymbol('}');
}
}
@override
void visitEqualsCall(EqualsCall node) {
int precedence = Precedence.EQUALITY;
writeExpression(node.left, precedence);
writeSpace();
writeSymbol('==');
writeInterfaceTarget(Name.equalsName, node.interfaceTargetReference);
writeSymbol('{');
writeType(node.functionType);
writeSymbol('}');
writeSpace();
writeExpression(node.right, precedence + 1);
}
@override
void visitEqualsNull(EqualsNull node) {
writeExpression(node.expression, Precedence.EQUALITY);
writeSpace();
writeSymbol('==');
writeSpace();
writeSymbol('null');
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeNode(node.arguments);
}
@override
void visitStaticInvocation(StaticInvocation node) {
writeModifier(node.isConst, 'const');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
@override
void visitConstructorInvocation(ConstructorInvocation node) {
writeWord(node.isConst ? 'const' : 'new');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
@override
void visitNot(Not node) {
writeSymbol('!');
writeExpression(node.operand, Precedence.PREFIX);
}
@override
void visitNullCheck(NullCheck node) {
writeExpression(node.operand, Precedence.POSTFIX);
writeSymbol('!');
}
@override
void visitLogicalExpression(LogicalExpression node) {
int precedence = Precedence.binaryPrecedence[
logicalExpressionOperatorToString(node.operatorEnum)]!;
writeExpression(node.left, precedence);
writeSpaced(logicalExpressionOperatorToString(node.operatorEnum));
writeExpression(node.right, precedence + 1);
}
@override
void visitConditionalExpression(ConditionalExpression node) {
writeExpression(node.condition, Precedence.LOGICAL_OR);
ensureSpace();
write('?');
writeStaticType(node.staticType);
writeSpace();
writeExpression(node.then);
writeSpaced(':');
writeExpression(node.otherwise);
}
@override
void visitStringConcatenation(StringConcatenation node) {
if (state == WORD) {
writeSpace();
}
write('"');
for (Expression part in node.expressions) {
if (part is StringLiteral) {
writeSymbol(escapeString(part.value));
} else {
writeSymbol(r'${');
writeExpression(part);
writeSymbol('}');
}
}
write('"');
state = WORD;
}
@override
void visitListConcatenation(ListConcatenation node) {
bool first = true;
for (Expression part in node.lists) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
@override
void visitSetConcatenation(SetConcatenation node) {
bool first = true;
for (Expression part in node.sets) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
@override
void visitMapConcatenation(MapConcatenation node) {
bool first = true;
for (Expression part in node.maps) {
if (!first) writeSpaced('+');
writeExpression(part);
first = false;
}
}
@override
void visitInstanceCreation(InstanceCreation node) {
writeClassReferenceFromReference(node.classReference);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
writeSymbol('{');
bool first = true;
node.fieldValues.forEach((Reference fieldRef, Expression value) {
if (!first) {
writeComma();
}
writeWord('${fieldRef.asField.name.text}');
writeSymbol(':');
writeExpression(value);
first = false;
});
for (AssertStatement assert_ in node.asserts) {
if (!first) {
writeComma();
}
write('assert(');
writeExpression(assert_.condition);
Expression? message = assert_.message;
if (message != null) {
writeComma();
writeExpression(message);
}
write(')');
first = false;
}
for (Expression unusedArgument in node.unusedArguments) {
if (!first) {
writeComma();
}
writeExpression(unusedArgument);
first = false;
}
writeSymbol('}');
}
@override
void visitFileUriExpression(FileUriExpression node) {
writeExpression(node.expression);
}
@override
void visitIsExpression(IsExpression node) {
writeExpression(node.operand, Precedence.BITWISE_OR);
writeSpaced(
node.isForNonNullableByDefault ? 'is{ForNonNullableByDefault}' : 'is');
writeType(node.type);
}
@override
void visitAsExpression(AsExpression node) {
writeExpression(node.operand, Precedence.BITWISE_OR);
List<String> flags = <String>[];
if (node.isTypeError) {
flags.add('TypeError');
}
if (node.isCovarianceCheck) {
flags.add('CovarianceCheck');
}
if (node.isForDynamic) {
flags.add('ForDynamic');
}
if (node.isForNonNullableByDefault) {
flags.add('ForNonNullableByDefault');
}
writeSpaced(flags.isNotEmpty ? 'as{${flags.join(',')}}' : 'as');
writeType(node.type);
}
@override
void visitSymbolLiteral(SymbolLiteral node) {
writeSymbol('#');
writeWord(node.value);
}
@override
void visitTypeLiteral(TypeLiteral node) {
writeType(node.type);
}
@override
void visitThisExpression(ThisExpression node) {
writeWord('this');
}
@override
void visitRethrow(Rethrow node) {
writeWord('rethrow');
}
@override
void visitThrow(Throw node) {
writeWord('throw');
writeSpace();
writeExpression(node.expression);
}
@override
void visitListLiteral(ListLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
// ignore: unnecessary_null_comparison
if (node.typeArgument != null) {
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>');
}
writeSymbol('[');
writeList(node.expressions, writeNode);
writeSymbol(']');
}
@override
void visitSetLiteral(SetLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
// ignore: unnecessary_null_comparison
if (node.typeArgument != null) {
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>');
}
writeSymbol('{');
writeList(node.expressions, writeNode);
writeSymbol('}');
}
@override
void visitMapLiteral(MapLiteral node) {
if (node.isConst) {
writeWord('const');
writeSpace();
}
// ignore: unnecessary_null_comparison
if (node.keyType != null) {
writeSymbol('<');
writeList([node.keyType, node.valueType], writeType);
writeSymbol('>');
}
writeSymbol('{');
writeList(node.entries, writeNode);
writeSymbol('}');
}
@override
void visitMapLiteralEntry(MapLiteralEntry node) {
writeExpression(node.key);
writeComma(':');
writeExpression(node.value);
}
@override
void visitAwaitExpression(AwaitExpression node) {
writeWord('await');
writeExpression(node.operand);
}
@override
void visitFunctionExpression(FunctionExpression node) {
writeFunction(node.function, terminateLine: false);
}
@override
void visitStringLiteral(StringLiteral node) {
writeWord('"${escapeString(node.value)}"');
}
@override
void visitIntLiteral(IntLiteral node) {
writeWord('${node.value}');
}
@override
void visitDoubleLiteral(DoubleLiteral node) {
writeWord('${node.value}');
}
@override
void visitBoolLiteral(BoolLiteral node) {
writeWord('${node.value}');
}
@override
void visitNullLiteral(NullLiteral node) {
writeWord('null');
}
@override
void visitLet(Let node) {
writeWord('let');
writeVariableDeclaration(node.variable);
writeSpaced('in');
writeExpression(node.body);
}
@override
void visitBlockExpression(BlockExpression node) {
writeSpaced('block');
writeBlockBody(node.body.statements, asExpression: true);
writeSymbol(' =>');
writeExpression(node.value);
}
@override
void visitInstantiation(Instantiation node) {
writeExpression(node.expression, Precedence.TYPE_LITERAL);
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
@override
void visitLoadLibrary(LoadLibrary node) {
writeWord('LoadLibrary');
writeSymbol('(');
writeWord(node.import.name!);
writeSymbol(')');
state = WORD;
}
@override
void visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
writeWord('CheckLibraryIsLoaded');
writeSymbol('(');
writeWord(node.import.name!);
writeSymbol(')');
state = WORD;
}
@override
void visitLibraryPart(LibraryPart node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord('part');
writeWord(node.partUri);
endLine(";");
}
@override
void visitLibraryDependency(LibraryDependency node) {
writeAnnotationList(node.annotations);
writeIndentation();
writeWord(node.isImport ? 'import' : 'export');
String uriString;
if (node.importedLibraryReference.node != null) {
uriString = '${node.targetLibrary.importUri}';
} else {
uriString = '${node.importedLibraryReference.canonicalName?.name}';
}
writeWord('"$uriString"');
if (node.isDeferred) {
writeWord('deferred');
}
String? name = node.name;
if (name != null) {
writeWord('as');
writeWord(name);
}
String? last;
final String show = 'show';
final String hide = 'hide';
if (node.combinators.isNotEmpty) {
for (Combinator combinator in node.combinators) {
if (combinator.isShow && last != show) {
last = show;
writeWord(show);
} else if (combinator.isHide && last != hide) {
last = hide;
writeWord(hide);
}
bool first = true;
for (String name in combinator.names) {
if (!first) writeComma();
writeWord(name);
first = false;
}
}
}
endLine(';');
}
@override
void defaultExpression(Expression node) {
writeWord('${node.runtimeType}');
}
@override
void visitVariableGet(VariableGet node) {
writeVariableReference(node.variable);
DartType? promotedType = node.promotedType;
if (promotedType != null) {
writeSymbol('{');
writeNode(promotedType);
writeSymbol('}');
state = WORD;
}
}
@override
void visitVariableSet(VariableSet node) {
writeVariableReference(node.variable);
writeSpaced('=');
writeExpression(node.value);
}
void writeInterfaceTarget(Name name, Reference? target) {
if (target != null) {
writeSymbol('{');
writeMemberReferenceFromReference(target);
writeSymbol('}');
} else {
writeName(name);
}
}
void writeStaticType(DartType type) {
// ignore: unnecessary_null_comparison
if (type != null) {
writeSymbol('{');
writeType(type);
writeSymbol('}');
}
}
@override
void visitDynamicGet(DynamicGet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
_writeDynamicAccessKind(node.kind);
writeName(node.name);
}
@override
void visitFunctionTearOff(FunctionTearOff node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeSymbol('call');
}
@override
void visitInstanceGet(InstanceGet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
_writeInstanceAccessKind(node.kind);
writeSymbol('{');
writeType(node.resultType);
writeSymbol('}');
}
@override
void visitInstanceTearOff(InstanceTearOff node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
_writeInstanceAccessKind(node.kind);
writeSymbol('{');
writeType(node.resultType);
writeSymbol('}');
}
@override
void visitDynamicSet(DynamicSet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
_writeDynamicAccessKind(node.kind);
writeName(node.name);
writeSpaced('=');
writeExpression(node.value);
}
@override
void visitInstanceSet(InstanceSet node) {
writeExpression(node.receiver, Precedence.PRIMARY);
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
_writeInstanceAccessKind(node.kind);
writeSpaced('=');
writeExpression(node.value);
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
writeWord('super');
writeSymbol('.');
writeInterfaceTarget(node.name, node.interfaceTargetReference);
writeSpaced('=');
writeExpression(node.value);
}
@override
void visitStaticTearOff(StaticTearOff node) {
writeMemberReferenceFromReference(node.targetReference);
}
@override
void visitStaticGet(StaticGet node) {
writeMemberReferenceFromReference(node.targetReference);
}
@override
void visitStaticSet(StaticSet node) {
writeMemberReferenceFromReference(node.targetReference);
writeSpaced('=');
writeExpression(node.value);
}
@override
void visitConstructorTearOff(ConstructorTearOff node) {
writeMemberReferenceFromReference(node.targetReference);
}
@override
void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
writeMemberReferenceFromReference(node.targetReference);
}
@override
void visitTypedefTearOff(TypedefTearOff node) {
writeTypeParameterList(node.typeParameters);
state = SYMBOL;
writeSymbol('.(');
writeNode(node.expression);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
writeSymbol(')');
state = WORD;
}
@override
void visitExpressionStatement(ExpressionStatement node) {
writeIndentation();
writeExpression(node.expression);
endLine(';');
}
void writeBlockBody(List<Statement> statements, {bool asExpression = false}) {
if (statements.isEmpty) {
asExpression ? writeSymbol('{}') : endLine('{}');
return;
}
endLine('{');
++indentation;
statements.forEach(writeNode);
--indentation;
writeIndentation();
asExpression ? writeSymbol('}') : endLine('}');
}
@override
void visitBlock(Block node) {
writeIndentation();
writeBlockBody(node.statements);
}
@override
void visitAssertBlock(AssertBlock node) {
writeIndentation();
writeSpaced('assert');
writeBlockBody(node.statements);
}
@override
void visitEmptyStatement(EmptyStatement node) {
writeIndentation();
endLine(';');
}
@override
void visitAssertStatement(AssertStatement node, {bool asExpression = false}) {
if (!asExpression) {
writeIndentation();
}
writeWord('assert');
writeSymbol('(');
writeExpression(node.condition);
Expression? message = node.message;
if (message != null) {
writeComma();
writeExpression(message);
}
if (!asExpression) {
endLine(');');
} else {
writeSymbol(')');
}
}
@override
void visitLabeledStatement(LabeledStatement node) {
writeIndentation();
writeWord(syntheticNames.nameLabeledStatement(node));
endLine(':');
writeNode(node.body);
}
@override
void visitBreakStatement(BreakStatement node) {
writeIndentation();
writeWord('break');
writeWord(syntheticNames.nameLabeledStatement(node.target));
endLine(';');
}
@override
void visitWhileStatement(WhileStatement node) {
writeIndentation();
writeSpaced('while');
writeSymbol('(');
writeExpression(node.condition);
writeSymbol(')');
writeBody(node.body);
}
@override
void visitDoStatement(DoStatement node) {
writeIndentation();
writeWord('do');
writeBody(node.body);
writeIndentation();
writeSpaced('while');
writeSymbol('(');
writeExpression(node.condition);
endLine(')');
}
@override
void visitForStatement(ForStatement node) {
writeIndentation();
writeSpaced('for');
writeSymbol('(');
writeList(node.variables, writeVariableDeclaration);
writeComma(';');
Expression? condition = node.condition;
if (condition != null) {
writeExpression(condition);
}
writeComma(';');
writeList(node.updates, writeExpression);
writeSymbol(')');
writeBody(node.body);
}
@override
void visitForInStatement(ForInStatement node) {
writeIndentation();
if (node.isAsync) {
writeSpaced('await');
}
writeSpaced('for');
writeSymbol('(');
writeVariableDeclaration(node.variable, useVarKeyword: true);
writeSpaced('in');
writeExpression(node.iterable);
writeSymbol(')');
writeBody(node.body);
}
@override
void visitSwitchStatement(SwitchStatement node) {
writeIndentation();
writeWord('switch');
writeSymbol('(');
writeExpression(node.expression);
endLine(') {');
++indentation;
node.cases.forEach(writeNode);
--indentation;
writeIndentation();
endLine('}');
}
@override
void visitSwitchCase(SwitchCase node) {
String label = syntheticNames.nameSwitchCase(node);
writeIndentation();
writeWord(label);
endLine(':');
for (Expression expression in node.expressions) {
writeIndentation();
writeWord('case');
writeExpression(expression);
endLine(':');
}
if (node.isDefault) {
writeIndentation();
writeWord('default');
endLine(':');
}
++indentation;
writeNode(node.body);
--indentation;
}
@override
void visitContinueSwitchStatement(ContinueSwitchStatement node) {
writeIndentation();
writeWord('continue');
writeWord(syntheticNames.nameSwitchCase(node.target));
endLine(';');
}
@override
void visitIfStatement(IfStatement node) {
writeIndentation();
writeWord('if');
writeSymbol('(');
writeExpression(node.condition);
writeSymbol(')');
writeBody(node.then);
Statement? otherwise = node.otherwise;
if (otherwise != null) {
writeIndentation();
writeWord('else');
writeBody(otherwise);
}
}
@override
void visitReturnStatement(ReturnStatement node) {
writeIndentation();
writeWord('return');
Expression? expression = node.expression;
if (expression != null) {
writeSpace();
writeExpression(expression);
}
endLine(';');
}
@override
void visitTryCatch(TryCatch node) {
writeIndentation();
writeWord('try');
writeBody(node.body);
node.catches.forEach(writeNode);
}
@override
void visitCatch(Catch node) {
writeIndentation();
// ignore: unnecessary_null_comparison
if (node.guard != null) {
writeWord('on');
writeType(node.guard);
writeSpace();
}
writeWord('catch');
writeSymbol('(');
VariableDeclaration? exception = node.exception;
if (exception != null) {
writeVariableDeclaration(exception);
} else {
writeWord('no-exception-var');
}
VariableDeclaration? stackTrace = node.stackTrace;
if (stackTrace != null) {
writeComma();
writeVariableDeclaration(stackTrace);
}
writeSymbol(')');
writeBody(node.body);
}
@override
void visitTryFinally(TryFinally node) {
writeIndentation();
writeWord('try');
writeBody(node.body);
writeIndentation();
writeWord('finally');
writeBody(node.finalizer);
}
@override
void visitYieldStatement(YieldStatement node) {
writeIndentation();
if (node.isYieldStar) {
writeWord('yield*');
} else if (node.isNative) {
writeWord('[yield]');
} else {
writeWord('yield');
}
writeExpression(node.expression);
endLine(';');
}
@override
void visitVariableDeclaration(VariableDeclaration node) {
writeIndentation();
writeVariableDeclaration(node, useVarKeyword: true);
endLine(';');
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
writeAnnotationList(node.variable.annotations);
writeIndentation();
writeWord('function');
// ignore: unnecessary_null_comparison
if (node.function != null) {
writeFunction(node.function, name: getVariableName(node.variable));
} else {
writeWord(getVariableName(node.variable));
endLine('...;');
}
}
void writeVariableDeclaration(VariableDeclaration node,
{bool useVarKeyword: false}) {
if (showOffsets) writeWord("[${node.fileOffset}]");
if (showMetadata) writeMetadata(node);
writeAnnotationList(node.annotations, separateLines: false);
writeModifier(node.isLowered, 'lowered');
writeModifier(node.isLate, 'late');
writeModifier(node.isRequired, 'required');
writeModifier(node.isCovariantByDeclaration, 'covariant-by-declaration');
writeModifier(node.isCovariantByClass, 'covariant-by-class');
writeModifier(node.isFinal, 'final');
writeModifier(node.isConst, 'const');
// ignore: unnecessary_null_comparison
if (node.type != null) {
writeAnnotatedType(node.type, annotator?.annotateVariable(this, node));
}
// ignore: unnecessary_null_comparison
if (useVarKeyword && !node.isFinal && !node.isConst && node.type == null) {
writeWord('var');
}
writeWord(getVariableName(node));
Expression? initializer = node.initializer;
if (initializer != null) {
writeSpaced('=');
writeExpression(initializer);
}
}
@override
void visitArguments(Arguments node) {
if (node.types.isNotEmpty) {
writeSymbol('<');
writeList(node.types, writeType);
writeSymbol('>');
}
writeSymbol('(');
Iterable<TreeNode> allArgs =
<List<TreeNode>>[node.positional, node.named].expand((x) => x);
writeList(allArgs, writeNode);
writeSymbol(')');
}
@override
void visitNamedExpression(NamedExpression node) {
writeWord(node.name);
writeComma(':');
writeExpression(node.value);
}
@override
void defaultStatement(Statement node) {
writeIndentation();
endLine('${node.runtimeType}');
}
@override
void visitInvalidInitializer(InvalidInitializer node) {
writeWord('invalid-initializer');
}
@override
void visitFieldInitializer(FieldInitializer node) {
writeMemberReferenceFromReference(node.fieldReference);
writeSpaced('=');
writeExpression(node.value);
}
@override
void visitSuperInitializer(SuperInitializer node) {
writeWord('super');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
writeWord('this');
writeMemberReferenceFromReference(node.targetReference);
writeNode(node.arguments);
}
@override
void visitLocalInitializer(LocalInitializer node) {
writeVariableDeclaration(node.variable);
}
@override
void visitAssertInitializer(AssertInitializer node) {
visitAssertStatement(node.statement, asExpression: true);
}
@override
void defaultInitializer(Initializer node) {
writeIndentation();
endLine(': ${node.runtimeType}');
}
void writeNullability(Nullability nullability, {bool inComment = false}) {
switch (nullability) {
case Nullability.legacy:
writeSymbol('*');
if (!inComment) {
state = WORD; // Disallow a word immediately after the '*'.
}
break;
case Nullability.nullable:
writeSymbol('?');
if (!inComment) {
state = WORD; // Disallow a word immediately after the '?'.
}
break;
case Nullability.undetermined:
writeSymbol('%');
if (!inComment) {
state = WORD; // Disallow a word immediately after the '%'.
}
break;
case Nullability.nonNullable:
if (inComment) {
writeSymbol("!");
}
break;
}
}
void writeDartTypeNullability(DartType type, {bool inComment = false}) {
if (type is InvalidType) {
writeNullability(Nullability.undetermined);
} else {
writeNullability(type.nullability, inComment: inComment);
}
}
@override
void visitInvalidType(InvalidType node) {
writeWord('invalid-type');
}
@override
void visitDynamicType(DynamicType node) {
writeWord('dynamic');
}
@override
void visitVoidType(VoidType node) {
writeWord('void');
}
@override
void visitNeverType(NeverType node) {
writeWord('Never');
writeNullability(node.nullability);
}
@override
void visitNullType(NullType node) {
writeWord('Null');
}
@override
void visitInterfaceType(InterfaceType node) {
writeClassReferenceFromReference(node.className);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
state = WORD; // Disallow a word immediately after the '>'.
}
writeNullability(node.nullability);
}
@override
void visitExtensionType(ExtensionType node) {
writeExtensionReferenceFromReference(node.extensionReference);
if (node.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
state = Printer.WORD;
}
writeNullability(node.declaredNullability);
}
@override
void visitFutureOrType(FutureOrType node) {
writeWord('FutureOr');
writeSymbol('<');
writeNode(node.typeArgument);
writeSymbol('>');
writeNullability(node.declaredNullability);
}
@override
void visitFunctionType(FunctionType node) {
writeFunctionType(node);
}
@override
void visitNamedType(NamedType node) {
writeModifier(node.isRequired, 'required');
writeWord(node.name);
writeSymbol(':');
writeSpace();
writeType(node.type);
}
@override
void visitTypeParameterType(TypeParameterType node) {
writeTypeParameterReference(node.parameter);
writeNullability(node.declaredNullability);
DartType? promotedBound = node.promotedBound;
if (promotedBound != null) {
writeSpaced('&');
writeType(promotedBound);
writeWord("/* '");
writeNullability(node.declaredNullability, inComment: true);
writeWord("' & '");
writeDartTypeNullability(promotedBound, inComment: true);
writeWord("' = '");
writeNullability(node.nullability, inComment: true);
writeWord("' */");
}
}
@override
void visitTypeParameter(TypeParameter node) {
writeModifier(node.isCovariantByClass, 'covariant-by-class');
writeAnnotationList(node.annotations, separateLines: false);
if (node.variance != Variance.covariant) {
writeWord(const <String>[
"unrelated",
"covariant",
"contravariant",
"invariant"
][node.variance]);
}
writeWord(getTypeParameterName(node));
writeSpaced('extends');
writeType(node.bound);
// ignore: unnecessary_null_comparison
if (node.defaultType != node.bound) {
writeSpaced('=');
writeType(node.defaultType);
}
}
void writeConstantReference(Constant node) {
writeWord(syntheticNames.nameConstant(node));
}
@override
void visitConstantExpression(ConstantExpression node) {
writeConstantReference(node.constant);
}
@override
void defaultConstant(Constant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('${node.runtimeType}');
}
@override
void visitNullConstant(NullConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('${node.value}');
}
@override
void visitBoolConstant(BoolConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('${node.value}');
}
@override
void visitIntConstant(IntConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('${node.value}');
}
@override
void visitDoubleConstant(DoubleConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('${node.value}');
}
@override
void visitSymbolConstant(SymbolConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
Reference? libraryReference = node.libraryReference;
String text = libraryReference != null
? '#${libraryReference.asLibrary.importUri}::${node.name}'
: '#${node.name}';
endLine('${text}');
}
@override
void visitListConstant(ListConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>[');
writeList(node.entries, writeConstantReference);
endLine(']');
}
@override
void visitSetConstant(SetConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeSymbol('<');
writeType(node.typeArgument);
writeSymbol('>{');
writeList(node.entries, writeConstantReference);
endLine('}');
}
@override
void visitMapConstant(MapConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeSymbol('<');
writeList([node.keyType, node.valueType], writeType);
writeSymbol('>{');
writeList(node.entries, (ConstantMapEntry entry) {
writeConstantReference(entry.key);
writeSymbol(':');
writeConstantReference(entry.value);
});
endLine(')');
}
@override
void visitTypeLiteralConstant(TypeLiteralConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('${node.runtimeType}');
writeSymbol('(');
writeNode(node.type);
endLine(')');
}
@override
void visitInstanceConstant(InstanceConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeClassReferenceFromReference(node.classReference);
if (!node.typeArguments.isEmpty) {
writeSymbol('<');
writeList(node.typeArguments, writeType);
writeSymbol('>');
}
writeSymbol(' {');
writeList(node.fieldValues.entries, (MapEntry<Reference, Constant> entry) {
if (entry.key.node != null) {
writeWord('${entry.key.asField.name.text}');
} else {
writeWord('${entry.key.canonicalName!.name}');
}
writeSymbol(':');
writeConstantReference(entry.value);
});
endLine('}');
}
@override
void visitInstantiationConstant(InstantiationConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('instantiation');
writeSpace();
writeConstantReference(node.tearOffConstant);
writeSpace();
writeSymbol('<');
writeList(node.types, writeType);
writeSymbol('>');
endLine();
}
@override
void visitStringConstant(StringConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
endLine('"${escapeString(node.value)}"');
}
@override
void visitStaticTearOffConstant(StaticTearOffConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('static-tearoff');
writeSpace();
writeMemberReferenceFromReference(node.targetReference);
endLine();
}
@override
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('typedef-tearoff');
writeSpace();
writeTypeParameterList(node.parameters);
state = SYMBOL;
writeSymbol('.(');
writeConstantReference(node.tearOffConstant);
if (node.types.isNotEmpty) {
writeSymbol('<');
writeList(node.types, writeType);
writeSymbol('>');
}
writeSymbol(')');
state = WORD;
endLine();
}
@override
void visitUnevaluatedConstant(UnevaluatedConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeSymbol('eval');
writeSpace();
writeExpression(node.expression);
endLine();
}
@override
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('constructor-tearoff');
writeSpace();
writeMemberReferenceFromReference(node.targetReference);
endLine();
}
@override
void visitRedirectingFactoryTearOffConstant(
RedirectingFactoryTearOffConstant node) {
writeIndentation();
writeConstantReference(node);
writeSpaced('=');
writeWord('redirecting-factory-tearoff');
writeSpace();
writeMemberReferenceFromReference(node.targetReference);
endLine();
}
@override
void defaultNode(Node node) {
write('<${node.runtimeType}>');
}
}
class Precedence implements ExpressionVisitor<int> {
static final Precedence instance = new Precedence();
static int of(Expression node) => node.accept(instance);
static const int EXPRESSION = 1;
static const int CONDITIONAL = 2;
static const int LOGICAL_NULL_AWARE = 3;
static const int LOGICAL_OR = 4;
static const int LOGICAL_AND = 5;
static const int EQUALITY = 6;
static const int RELATIONAL = 7;
static const int BITWISE_OR = 8;
static const int BITWISE_XOR = 9;
static const int BITWISE_AND = 10;
static const int SHIFT = 11;
static const int ADDITIVE = 12;
static const int MULTIPLICATIVE = 13;
static const int PREFIX = 14;
static const int POSTFIX = 15;
static const int TYPE_LITERAL = 19;
static const int PRIMARY = 20;
static const int CALLEE = 21;
static const Map<String?, int> binaryPrecedence = const {
'&&': LOGICAL_AND,
'||': LOGICAL_OR,
'??': LOGICAL_NULL_AWARE,
'==': EQUALITY,
'!=': EQUALITY,
'>': RELATIONAL,
'>=': RELATIONAL,
'<': RELATIONAL,
'<=': RELATIONAL,
'|': BITWISE_OR,
'^': BITWISE_XOR,
'&': BITWISE_AND,
'>>': SHIFT,
'<<': SHIFT,
'+': ADDITIVE,
'-': ADDITIVE,
'*': MULTIPLICATIVE,
'%': MULTIPLICATIVE,
'/': MULTIPLICATIVE,
'~/': MULTIPLICATIVE,
null: EXPRESSION,
};
static bool isAssociativeBinaryOperator(int precedence) {
return precedence != EQUALITY && precedence != RELATIONAL;
}
@override
int defaultExpression(Expression node) => EXPRESSION;
@override
int visitInvalidExpression(InvalidExpression node) => CALLEE;
@override
int visitInstanceInvocation(InstanceInvocation node) => CALLEE;
@override
int visitInstanceGetterInvocation(InstanceGetterInvocation node) => CALLEE;
@override
int visitDynamicInvocation(DynamicInvocation node) => CALLEE;
@override
int visitFunctionInvocation(FunctionInvocation node) => CALLEE;
@override
int visitLocalFunctionInvocation(LocalFunctionInvocation node) => CALLEE;
@override
int visitEqualsCall(EqualsCall node) => EQUALITY;
@override
int visitEqualsNull(EqualsNull node) => EQUALITY;
@override
int visitSuperMethodInvocation(SuperMethodInvocation node) => CALLEE;
@override
int visitStaticInvocation(StaticInvocation node) => CALLEE;
@override
int visitConstructorInvocation(ConstructorInvocation node) => CALLEE;
@override
int visitNot(Not node) => PREFIX;
@override
int visitNullCheck(NullCheck node) => PRIMARY;
@override
int visitLogicalExpression(LogicalExpression node) =>
binaryPrecedence[logicalExpressionOperatorToString(node.operatorEnum)]!;
@override
int visitConditionalExpression(ConditionalExpression node) => CONDITIONAL;
@override
int visitStringConcatenation(StringConcatenation node) => PRIMARY;
@override
int visitIsExpression(IsExpression node) => RELATIONAL;
@override
int visitAsExpression(AsExpression node) => RELATIONAL;
@override
int visitSymbolLiteral(SymbolLiteral node) => PRIMARY;
@override
int visitTypeLiteral(TypeLiteral node) => PRIMARY;
@override
int visitThisExpression(ThisExpression node) => CALLEE;
@override
int visitRethrow(Rethrow node) => PRIMARY;
@override
int visitThrow(Throw node) => EXPRESSION;
@override
int visitListLiteral(ListLiteral node) => PRIMARY;
@override
int visitSetLiteral(SetLiteral node) => PRIMARY;
@override
int visitMapLiteral(MapLiteral node) => PRIMARY;
@override
int visitAwaitExpression(AwaitExpression node) => PREFIX;
@override
int visitFunctionExpression(FunctionExpression node) => EXPRESSION;
@override
int visitStringLiteral(StringLiteral node) => CALLEE;
@override
int visitIntLiteral(IntLiteral node) => CALLEE;
@override
int visitDoubleLiteral(DoubleLiteral node) => CALLEE;
@override
int visitBoolLiteral(BoolLiteral node) => CALLEE;
@override
int visitNullLiteral(NullLiteral node) => CALLEE;
@override
int visitVariableGet(VariableGet node) => PRIMARY;
@override
int visitVariableSet(VariableSet node) => EXPRESSION;
@override
int visitInstanceGet(InstanceGet node) => PRIMARY;
@override
int visitDynamicGet(DynamicGet node) => PRIMARY;
@override
int visitInstanceTearOff(InstanceTearOff node) => PRIMARY;
@override
int visitFunctionTearOff(FunctionTearOff node) => PRIMARY;
@override
int visitSuperPropertyGet(SuperPropertyGet node) => PRIMARY;
@override
int visitSuperPropertySet(SuperPropertySet node) => EXPRESSION;
@override
int visitStaticGet(StaticGet node) => PRIMARY;
@override
int visitStaticTearOff(StaticTearOff node) => PRIMARY;
@override
int visitStaticSet(StaticSet node) => EXPRESSION;
@override
int visitConstructorTearOff(ConstructorTearOff node) => PRIMARY;
@override
int visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) => PRIMARY;
@override
int visitTypedefTearOff(TypedefTearOff node) => EXPRESSION;
@override
int visitLet(Let node) => EXPRESSION;
@override
int defaultBasicLiteral(BasicLiteral node) => CALLEE;
@override
int visitBlockExpression(BlockExpression node) => EXPRESSION;
@override
int visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) => EXPRESSION;
@override
int visitConstantExpression(ConstantExpression node) => PRIMARY;
@override
int visitDynamicSet(DynamicSet node) => EXPRESSION;
@override
int visitFileUriExpression(FileUriExpression node) => EXPRESSION;
@override
int visitInstanceCreation(InstanceCreation node) => EXPRESSION;
@override
int visitInstanceSet(InstanceSet node) => EXPRESSION;
@override
int visitInstantiation(Instantiation node) => EXPRESSION;
@override
int visitListConcatenation(ListConcatenation node) => EXPRESSION;
@override
int visitLoadLibrary(LoadLibrary node) => EXPRESSION;
@override
int visitMapConcatenation(MapConcatenation node) => EXPRESSION;
@override
int visitSetConcatenation(SetConcatenation node) => EXPRESSION;
}
String procedureKindToString(ProcedureKind kind) {
switch (kind) {
case ProcedureKind.Method:
return 'method';
case ProcedureKind.Getter:
return 'get';
case ProcedureKind.Setter:
return 'set';
case ProcedureKind.Operator:
return 'operator';
case ProcedureKind.Factory:
return 'factory';
}
}