in unsupported/juno/crates/juno/src/gen_js.rs [289:3045]
fn gen_node<'gc>(&mut self, ctx: &'gc GCLock, node: &'gc Node<'gc>, path: Option<Path<'gc>>) {
match node {
Node::Empty(_) => {}
Node::Metadata(_) => {}
Node::Program(Program { metadata: _, body }) => {
self.visit_stmt_list(ctx, body, Path::new(node, NodeField::body));
}
Node::Module(Module { metadata: _, body }) => {
self.visit_stmt_list(ctx, body, Path::new(node, NodeField::body));
}
Node::FunctionExpression(FunctionExpression {
metadata: _,
id,
params,
body,
type_parameters,
return_type,
predicate,
generator,
is_async,
})
| Node::FunctionDeclaration(FunctionDeclaration {
metadata: _,
id,
params,
body,
type_parameters,
return_type,
predicate,
generator,
is_async,
}) => {
if *is_async {
out_token!(self, node, "async function");
} else {
out_token!(self, node, "function");
}
if *generator {
out!(self, "*");
if id.is_some() {
self.space(ForceSpace::No);
}
} else if id.is_some() {
self.space(ForceSpace::Yes);
}
if let Some(id) = id {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
self.visit_func_params_body(
ctx,
params,
*type_parameters,
*return_type,
*predicate,
*body,
node,
);
}
Node::ArrowFunctionExpression(ArrowFunctionExpression {
metadata: _,
id: _,
params,
body,
type_parameters,
return_type,
predicate,
expression,
is_async,
}) => {
let mut need_sep = false;
if *is_async {
out!(self, "async");
need_sep = true;
}
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
need_sep = false;
}
// Single parameter without type info doesn't need parens.
// But only in expression mode, otherwise it is ugly.
if params.len() == 1
&& type_parameters.is_none()
&& return_type.is_none()
&& predicate.is_none()
&& node_isa!(Node::Identifier, params[0])
&& node_cast!(Node::Identifier, params[0])
.type_annotation
.is_none()
&& !node_cast!(Node::Identifier, params[0]).optional
&& (*expression || self.pretty == Pretty::No)
{
if need_sep {
out!(self, " ");
}
params[0].visit(ctx, self, Some(Path::new(node, NodeField::params)));
} else {
out!(self, "(");
for (i, param) in params.iter().enumerate() {
if i > 0 {
self.comma();
}
param.visit(ctx, self, Some(Path::new(node, NodeField::params)));
}
out!(self, ")");
}
if let Some(return_type) = return_type {
out!(self, ":");
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(return_type),
Path::new(node, NodeField::return_type),
ChildPos::Anywhere,
);
}
if let Some(predicate) = predicate {
self.space(ForceSpace::Yes);
predicate.visit(ctx, self, Some(Path::new(node, NodeField::predicate)));
}
self.space(ForceSpace::No);
out!(self, "=>");
self.space(ForceSpace::No);
match &body {
Node::BlockStatement(_) => {
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
_ => {
self.print_child(
ctx,
Some(*body),
Path::new(node, NodeField::body),
ChildPos::Right,
);
}
}
}
Node::WhileStatement(WhileStatement {
metadata: _,
body,
test,
}) => {
out!(self, "while");
self.space(ForceSpace::No);
out!(self, "(");
test.visit(ctx, self, Some(Path::new(node, NodeField::test)));
out!(self, ")");
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
}
Node::DoWhileStatement(DoWhileStatement {
metadata: _,
body,
test,
}) => {
out!(self, "do");
let block = self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
if block {
self.space(ForceSpace::No);
} else {
out!(self, ";");
self.newline();
}
out!(self, "while");
self.space(ForceSpace::No);
out!(self, "(");
test.visit(ctx, self, Some(Path::new(node, NodeField::test)));
out!(self, ")");
}
Node::ForInStatement(ForInStatement {
metadata: _,
left,
right,
body,
}) => {
out!(self, "for(");
left.visit(ctx, self, Some(Path::new(node, NodeField::left)));
out!(self, " in ");
right.visit(ctx, self, Some(Path::new(node, NodeField::right)));
out!(self, ")");
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
}
Node::ForOfStatement(ForOfStatement {
metadata: _,
left,
right,
body,
is_await,
}) => {
out!(self, "for{}(", if *is_await { " await" } else { "" });
left.visit(ctx, self, Some(Path::new(node, NodeField::left)));
out!(self, " of ");
right.visit(ctx, self, Some(Path::new(node, NodeField::right)));
out!(self, ")");
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
}
Node::ForStatement(ForStatement {
metadata: _,
init,
test,
update,
body,
}) => {
out!(self, "for(");
self.print_child(ctx, *init, Path::new(node, NodeField::init), ChildPos::Left);
out!(self, ";");
if let Some(test) = test {
self.space(ForceSpace::No);
test.visit(ctx, self, Some(Path::new(node, NodeField::test)));
}
out!(self, ";");
if let Some(update) = update {
self.space(ForceSpace::No);
update.visit(ctx, self, Some(Path::new(node, NodeField::update)));
}
out!(self, ")");
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
}
Node::DebuggerStatement(_) => {
out!(self, "debugger");
}
Node::EmptyStatement(_) => {}
Node::BlockStatement(BlockStatement { metadata: _, body }) => {
if body.is_empty() {
out!(self, "{{}}");
} else {
out!(self, "{{");
self.inc_indent();
self.newline();
self.visit_stmt_list(ctx, body, Path::new(node, NodeField::body));
self.dec_indent();
self.newline();
out!(self, "}}");
}
}
Node::BreakStatement(BreakStatement { metadata: _, label }) => {
out!(self, "break");
if let Some(label) = label {
self.space(ForceSpace::Yes);
label.visit(ctx, self, Some(Path::new(node, NodeField::label)));
}
}
Node::ContinueStatement(ContinueStatement { metadata: _, label }) => {
out!(self, "continue");
if let Some(label) = label {
self.space(ForceSpace::Yes);
label.visit(ctx, self, Some(Path::new(node, NodeField::label)));
}
}
Node::ThrowStatement(ThrowStatement {
metadata: _,
argument,
}) => {
out_token!(self, node, "throw ");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
Node::ReturnStatement(ReturnStatement {
metadata: _,
argument,
}) => {
out_token!(self, node, "return");
if let Some(argument) = argument {
out!(self, " ");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
}
Node::WithStatement(WithStatement {
metadata: _,
object,
body,
}) => {
out_token!(self, node, "with");
self.space(ForceSpace::No);
out!(self, "(");
object.visit(ctx, self, Some(Path::new(node, NodeField::object)));
out!(self, ")");
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::No,
Path::new(node, NodeField::body),
);
}
Node::SwitchStatement(SwitchStatement {
metadata: _,
discriminant,
cases,
}) => {
out_token!(self, node, "switch");
self.space(ForceSpace::No);
out!(self, "(");
discriminant.visit(ctx, self, Some(Path::new(node, NodeField::discriminant)));
out!(self, ")");
self.space(ForceSpace::No);
out!(self, "{{");
self.newline();
for case in cases {
case.visit(ctx, self, Some(Path::new(node, NodeField::cases)));
self.newline();
}
out!(self, "}}");
}
Node::SwitchCase(SwitchCase {
metadata: _,
test,
consequent,
}) => {
match test {
Some(test) => {
out_token!(self, node, "case ");
test.visit(ctx, self, Some(Path::new(node, NodeField::test)));
}
None => {
out_token!(self, node, "default");
}
};
out!(self, ":");
if !consequent.is_empty() {
self.inc_indent();
self.newline();
self.visit_stmt_list(ctx, consequent, Path::new(node, NodeField::consequent));
self.dec_indent();
}
}
Node::LabeledStatement(LabeledStatement {
metadata: _,
label,
body,
}) => {
label.visit(ctx, self, Some(Path::new(node, NodeField::label)));
out!(self, ":");
self.newline();
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
Node::ExpressionStatement(ExpressionStatement {
metadata: _,
expression,
directive: _,
}) => {
self.print_child(
ctx,
Some(*expression),
Path::new(node, NodeField::expression),
ChildPos::Anywhere,
);
}
Node::TryStatement(TryStatement {
metadata: _,
block,
handler,
finalizer,
}) => {
out_token!(self, node, "try");
self.visit_stmt_or_block(
ctx,
*block,
ForceBlock::Yes,
Path::new(node, NodeField::block),
);
if let Some(handler) = handler {
handler.visit(ctx, self, Some(Path::new(node, NodeField::handler)));
}
if let Some(finalizer) = finalizer {
out!(self, "finally");
self.space(ForceSpace::No);
self.visit_stmt_or_block(
ctx,
*finalizer,
ForceBlock::Yes,
Path::new(node, NodeField::finalizer),
);
}
}
Node::IfStatement(IfStatement {
metadata: _,
test,
consequent,
alternate,
}) => {
out_token!(self, node, "if");
self.space(ForceSpace::No);
out!(self, "(");
test.visit(ctx, self, Some(Path::new(node, NodeField::test)));
out!(self, ")");
let force_block = if alternate.is_some() && is_if_without_else(consequent) {
ForceBlock::Yes
} else {
ForceBlock::No
};
self.visit_stmt_or_block(
ctx,
*consequent,
force_block,
Path::new(node, NodeField::consequent),
);
if let Some(alternate) = alternate {
out!(self, "else");
self.space(if matches!(alternate, Node::BlockStatement(_)) {
ForceSpace::No
} else {
ForceSpace::Yes
});
self.visit_stmt_or_block(
ctx,
*alternate,
ForceBlock::No,
Path::new(node, NodeField::alternate),
);
}
}
Node::BooleanLiteral(BooleanLiteral { metadata: _, value }) => {
out_token!(self, node, "{}", if *value { "true" } else { "false" });
}
Node::NullLiteral(_) => {
out_token!(self, node, "null");
}
Node::StringLiteral(StringLiteral { metadata: _, value }) => {
out_token!(self, node, "\"");
self.print_escaped_string_literal(value, '"');
out!(self, "\"");
}
Node::NumericLiteral(NumericLiteral { metadata: _, value }) => {
out_token!(self, node, "{}", convert::number_to_string(*value));
}
Node::RegExpLiteral(RegExpLiteral {
metadata: _,
pattern,
flags,
}) => {
out_token!(self, node, "/");
// Parser doesn't handle escapes when lexing RegExp,
// so we don't need to do any manual escaping here.
self.write_utf8(ctx.str(*pattern));
out!(self, "/");
self.write_utf8(ctx.str(*flags));
}
Node::ThisExpression(_) => {
out_token!(self, node, "this");
}
Node::Super(_) => {
out_token!(self, node, "super");
}
Node::SequenceExpression(SequenceExpression {
metadata: _,
expressions,
}) => {
out!(self, "(");
for (i, expr) in expressions.iter().enumerate() {
if i > 0 {
self.comma();
}
self.print_child(
ctx,
Some(*expr),
Path::new(node, NodeField::expressions),
if i == 1 {
ChildPos::Left
} else {
ChildPos::Right
},
);
}
out!(self, ")");
}
Node::ObjectExpression(ObjectExpression {
metadata: _,
properties,
}) => {
self.visit_props(ctx, properties, Path::new(node, NodeField::properties));
}
Node::ArrayExpression(ArrayExpression {
metadata: _,
elements,
trailing_comma,
}) => {
out_token!(self, node, "[");
for (i, elem) in elements.iter().enumerate() {
if i > 0 {
self.comma();
}
if let Node::SpreadElement(_) = elem {
elem.visit(ctx, self, Some(Path::new(node, NodeField::elements)));
} else {
self.print_comma_expression(
ctx,
*elem,
Path::new(node, NodeField::elements),
);
}
}
if *trailing_comma {
self.comma();
}
out!(self, "]");
}
Node::SpreadElement(SpreadElement {
metadata: _,
argument,
}) => {
out_token!(self, node, "...");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
Node::NewExpression(NewExpression {
metadata: _,
callee,
type_arguments,
arguments,
}) => {
out_token!(self, node, "new ");
self.print_child(
ctx,
Some(*callee),
Path::new(node, NodeField::callee),
ChildPos::Left,
);
if let Some(type_arguments) = type_arguments {
type_arguments.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_arguments)),
);
}
out!(self, "(");
for (i, arg) in arguments.iter().enumerate() {
if i > 0 {
self.comma();
}
self.print_comma_expression(ctx, *arg, Path::new(node, NodeField::arguments));
}
out!(self, ")");
}
Node::YieldExpression(YieldExpression {
metadata: _,
argument,
delegate,
}) => {
out_token!(self, node, "yield");
if *delegate {
out!(self, "*");
self.space(ForceSpace::No);
} else if argument.is_some() {
out!(self, " ");
}
self.print_child(
ctx,
*argument,
Path::new(node, NodeField::argument),
ChildPos::Right,
);
}
Node::AwaitExpression(AwaitExpression {
metadata: _,
argument,
}) => {
out!(self, "await ");
self.print_child(
ctx,
Some(*argument),
Path::new(node, NodeField::argument),
ChildPos::Right,
);
}
Node::ImportExpression(ImportExpression {
metadata: _,
source,
attributes,
}) => {
out_token!(self, node, "import(");
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
if let Some(attributes) = attributes {
out!(self, ",");
self.space(ForceSpace::No);
attributes.visit(ctx, self, Some(Path::new(node, NodeField::attributes)));
}
out!(self, ")");
}
Node::CallExpression(CallExpression {
metadata: _,
callee,
type_arguments,
arguments,
}) => {
self.print_child(
ctx,
Some(*callee),
Path::new(node, NodeField::callee),
ChildPos::Left,
);
if let Some(type_arguments) = type_arguments {
type_arguments.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_arguments)),
);
}
out!(self, "(");
for (i, arg) in arguments.iter().enumerate() {
if i > 0 {
self.comma();
}
self.print_child(
ctx,
Some(*arg),
Path::new(node, NodeField::arguments),
ChildPos::Anywhere,
);
}
out!(self, ")");
}
Node::OptionalCallExpression(OptionalCallExpression {
metadata: _,
callee,
type_arguments,
arguments,
optional,
}) => {
self.print_child(
ctx,
Some(*callee),
Path::new(node, NodeField::callee),
ChildPos::Left,
);
if let Some(type_arguments) = type_arguments {
type_arguments.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_arguments)),
);
}
out!(self, "{}(", if *optional { "?." } else { "" });
for (i, arg) in arguments.iter().enumerate() {
if i > 0 {
self.comma();
}
self.print_child(
ctx,
Some(*arg),
Path::new(node, NodeField::arguments),
ChildPos::Anywhere,
);
}
out!(self, ")");
}
Node::AssignmentExpression(AssignmentExpression {
metadata: _,
operator,
left,
right,
}) => {
self.print_child(
ctx,
Some(*left),
Path::new(node, NodeField::left),
ChildPos::Left,
);
self.space(ForceSpace::No);
out!(self, "{}", operator.as_str());
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(*right),
Path::new(node, NodeField::right),
ChildPos::Right,
);
}
Node::UnaryExpression(UnaryExpression {
metadata: _,
operator,
argument,
prefix,
}) => {
let ident = operator.as_str().chars().next().unwrap().is_alphabetic();
if *prefix {
out!(self, "{}", operator.as_str());
if ident {
out!(self, " ");
}
self.print_child(
ctx,
Some(*argument),
Path::new(node, NodeField::argument),
ChildPos::Right,
);
} else {
self.print_child(
ctx,
Some(*argument),
Path::new(node, NodeField::argument),
ChildPos::Left,
);
if ident {
out!(self, " ");
}
out!(self, "{}", operator.as_str());
}
}
Node::UpdateExpression(UpdateExpression {
metadata: _,
operator,
argument,
prefix,
}) => {
if *prefix {
out!(self, "{}", operator.as_str());
self.print_child(
ctx,
Some(*argument),
Path::new(node, NodeField::argument),
ChildPos::Right,
);
} else {
self.print_child(
ctx,
Some(*argument),
Path::new(node, NodeField::argument),
ChildPos::Left,
);
out!(self, "{}", operator.as_str());
}
}
Node::MemberExpression(MemberExpression {
metadata: _,
object,
property,
computed,
}) => {
match object {
Node::NumericLiteral(NumericLiteral { value, .. }) => {
// Account for possible `50..toString()`.
let string = convert::number_to_string(*value);
// If there is an `e` or a decimal point, no need for an extra `.`.
let suffix = string.find::<&[char]>(&['E', 'e', '.']).map_or(".", |_| "");
out_token!(self, node, "{}{}", string, suffix);
}
_ => {
self.print_child(
ctx,
Some(*object),
Path::new(node, NodeField::object),
ChildPos::Left,
);
}
}
if *computed {
out!(self, "[");
} else {
out!(self, ".");
}
self.print_child(
ctx,
Some(*property),
Path::new(node, NodeField::property),
ChildPos::Right,
);
if *computed {
out!(self, "]");
}
}
Node::OptionalMemberExpression(OptionalMemberExpression {
metadata: _,
object,
property,
computed,
optional,
}) => {
self.print_child(
ctx,
Some(*object),
Path::new(node, NodeField::object),
ChildPos::Left,
);
if *computed {
out!(self, "{}[", if *optional { "?." } else { "" });
} else {
out!(self, "{}.", if *optional { "?" } else { "" });
}
self.print_child(
ctx,
Some(*property),
Path::new(node, NodeField::property),
ChildPos::Right,
);
if *computed {
out!(self, "]");
}
}
Node::BinaryExpression(BinaryExpression {
metadata: _,
left,
right,
operator,
}) => {
let ident = operator.as_str().chars().next().unwrap().is_alphabetic();
self.print_child(
ctx,
Some(*left),
Path::new(node, NodeField::left),
ChildPos::Left,
);
self.space(if ident {
ForceSpace::Yes
} else {
ForceSpace::No
});
out!(self, "{}", operator.as_str());
self.space(if ident {
ForceSpace::Yes
} else {
ForceSpace::No
});
self.print_child(
ctx,
Some(*right),
Path::new(node, NodeField::right),
ChildPos::Right,
);
}
Node::Directive(Directive { metadata: _, value }) => {
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
Node::DirectiveLiteral(DirectiveLiteral { metadata: _, .. }) => {
unimplemented!("No escaping for directive literals");
}
Node::ConditionalExpression(ConditionalExpression {
metadata: _,
test,
consequent,
alternate,
}) => {
self.print_child(
ctx,
Some(*test),
Path::new(node, NodeField::test),
ChildPos::Left,
);
self.space(ForceSpace::No);
out!(self, "?");
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(*consequent),
Path::new(node, NodeField::consequent),
ChildPos::Anywhere,
);
self.space(ForceSpace::No);
out!(self, ":");
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(*alternate),
Path::new(node, NodeField::alternate),
ChildPos::Right,
);
}
Node::Identifier(Identifier {
metadata: _,
name,
type_annotation,
optional,
}) => {
self.add_segment(node);
self.write_utf8(ctx.str(*name).as_ref());
self.annotate_identifier(ctx, node);
if *optional {
out!(self, "?");
}
if let Some(type_annotation) = type_annotation {
out!(self, ":");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
}
Node::PrivateName(PrivateName { metadata: _, id }) => {
out_token!(self, node, "#");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
Node::MetaProperty(MetaProperty {
metadata: _,
meta,
property,
}) => {
meta.visit(ctx, self, Some(Path::new(node, NodeField::meta)));
out!(self, ".");
property.visit(ctx, self, Some(Path::new(node, NodeField::property)));
}
Node::CatchClause(CatchClause {
metadata: _,
param,
body,
}) => {
self.space(ForceSpace::No);
out_token!(self, node, "catch");
if let Some(param) = param {
self.space(ForceSpace::No);
out!(self, "(");
param.visit(ctx, self, Some(Path::new(node, NodeField::param)));
out!(self, ")");
}
self.visit_stmt_or_block(
ctx,
*body,
ForceBlock::Yes,
Path::new(node, NodeField::body),
);
}
Node::VariableDeclaration(VariableDeclaration {
metadata: _,
kind,
declarations,
}) => {
out_token!(self, node, "{} ", kind.as_str());
for (i, decl) in declarations.iter().enumerate() {
if i > 0 {
self.comma();
}
decl.visit(ctx, self, Some(Path::new(node, NodeField::declarations)));
}
}
Node::VariableDeclarator(VariableDeclarator {
metadata: _,
init,
id,
}) => {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(init) = init {
out!(
self,
"{}",
match self.pretty {
Pretty::Yes => " = ",
Pretty::No => "=",
}
);
init.visit(ctx, self, Some(Path::new(node, NodeField::init)));
}
}
Node::TemplateLiteral(TemplateLiteral {
metadata: _,
quasis,
expressions,
}) => {
out_token!(self, node, "`");
let mut it_expr = expressions.iter();
for quasi in quasis {
if let Node::TemplateElement(TemplateElement {
metadata: _,
raw,
tail: _,
cooked: _,
}) = quasi
{
let mut buf = [0u8; 4];
for char in ctx.str(*raw).chars() {
if char == '\n' {
self.force_newline_without_indent();
continue;
}
self.write_char(char, &mut buf);
}
if let Some(expr) = it_expr.next() {
out!(self, "${{");
expr.visit(ctx, self, Some(Path::new(node, NodeField::expressions)));
out!(self, "}}");
}
}
}
out!(self, "`");
}
Node::TaggedTemplateExpression(TaggedTemplateExpression {
metadata: _,
tag,
quasi,
}) => {
self.print_child(
ctx,
Some(*tag),
Path::new(node, NodeField::tag),
ChildPos::Left,
);
self.print_child(
ctx,
Some(*quasi),
Path::new(node, NodeField::quasi),
ChildPos::Right,
);
}
Node::TemplateElement(_) => {
unreachable!("TemplateElement is handled in TemplateLiteral case");
}
Node::Property(Property {
metadata: _,
key,
value,
kind,
computed,
method,
shorthand,
}) => {
let mut need_sep = false;
if *kind != PropertyKind::Init {
out_token!(self, node, "{}", kind.as_str());
need_sep = true;
} else if *method {
match value {
Node::FunctionExpression(FunctionExpression {
metadata: _,
generator,
is_async,
..
}) => {
if *is_async {
out!(self, "async");
need_sep = true;
}
if *generator {
out!(self, "*");
need_sep = false;
self.space(ForceSpace::No);
}
}
_ => unreachable!(),
};
}
if *computed {
if need_sep {
self.space(ForceSpace::No);
}
need_sep = false;
out!(self, "[");
}
if need_sep {
out!(self, " ");
}
if *shorthand {
value.visit(ctx, self, None);
} else {
key.visit(ctx, self, None);
}
if *computed {
out!(self, "]");
}
if *shorthand {
return;
}
if *kind != PropertyKind::Init || *method {
match value {
Node::FunctionExpression(FunctionExpression {
metadata: _,
// Name is handled by the property key.
id: _,
params,
body,
return_type,
predicate,
type_parameters,
// Handled above.
generator: _,
is_async: _,
}) => {
self.visit_func_params_body(
ctx,
params,
*type_parameters,
*return_type,
*predicate,
*body,
*value,
);
}
_ => unreachable!(),
};
} else {
out!(self, ":");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
}
Node::LogicalExpression(LogicalExpression {
metadata: _,
left,
right,
operator,
}) => {
self.print_child(
ctx,
Some(*left),
Path::new(node, NodeField::left),
ChildPos::Left,
);
self.space(ForceSpace::No);
out!(self, "{}", operator.as_str());
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(*right),
Path::new(node, NodeField::right),
ChildPos::Right,
);
}
Node::ClassExpression(ClassExpression {
metadata: _,
id,
type_parameters,
super_class,
super_type_parameters,
implements,
decorators,
body,
})
| Node::ClassDeclaration(ClassDeclaration {
metadata: _,
id,
type_parameters,
super_class,
super_type_parameters,
implements,
decorators,
body,
}) => {
if !decorators.is_empty() {
for decorator in decorators {
decorator.visit(ctx, self, Some(Path::new(node, NodeField::decorators)));
self.force_newline();
}
out!(self, "class");
} else {
out_token!(self, node, "class");
}
if let Some(id) = id {
self.space(ForceSpace::Yes);
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
if let Some(super_class) = super_class {
out!(self, " extends ");
super_class.visit(ctx, self, Some(Path::new(node, NodeField::super_class)));
}
if let Some(super_type_parameters) = super_type_parameters {
super_type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::super_type_parameters)),
);
}
if !implements.is_empty() {
out!(self, " implements ");
for (i, implement) in implements.iter().enumerate() {
if i > 0 {
self.comma();
}
implement.visit(ctx, self, Some(Path::new(node, NodeField::implements)));
}
}
self.space(ForceSpace::No);
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
Node::ClassBody(ClassBody { metadata: _, body }) => {
if body.is_empty() {
out!(self, "{{}}");
} else {
out!(self, "{{");
self.inc_indent();
self.newline();
for prop in body {
prop.visit(ctx, self, Some(Path::new(node, NodeField::body)));
self.newline();
}
out!(self, "}}");
self.dec_indent();
self.newline();
}
}
Node::ClassProperty(ClassProperty {
metadata: _,
key,
value,
computed,
is_static,
declare,
optional,
variance,
type_annotation,
}) => {
if let Some(variance) = variance {
variance.visit(ctx, self, Some(Path::new(node, NodeField::variance)));
}
if *is_static {
out!(self, "static ");
}
if *declare {
out!(self, "declare ");
}
if *computed {
out!(self, "[");
}
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
if *computed {
out!(self, "]");
}
if *optional {
out!(self, "?");
}
if let Some(type_annotation) = type_annotation {
out!(self, ":");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
if let Some(value) = value {
self.space(ForceSpace::No);
out!(self, "=");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
out!(self, ";");
}
Node::ClassPrivateProperty(ClassPrivateProperty {
metadata: _,
key,
value,
is_static,
declare,
optional,
variance,
type_annotation,
}) => {
if let Some(variance) = variance {
variance.visit(ctx, self, Some(Path::new(node, NodeField::variance)));
}
if *is_static {
out!(self, "static ");
}
if *declare {
out!(self, "static ");
}
out!(self, "#");
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
if *optional {
out!(self, "?");
}
if let Some(type_annotation) = type_annotation {
out!(self, ":");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
self.space(ForceSpace::No);
if let Some(value) = value {
out!(self, "=");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
out!(self, ";");
}
Node::MethodDefinition(MethodDefinition {
metadata: _,
key,
value,
kind,
computed,
is_static,
}) => {
let (is_async, generator, params, body, return_type, predicate, type_parameters) =
match value {
Node::FunctionExpression(FunctionExpression {
metadata: _,
id: _,
generator,
is_async,
params,
body,
return_type,
predicate,
type_parameters,
}) => (
*is_async,
*generator,
params,
body,
return_type,
predicate,
type_parameters,
),
_ => {
unreachable!("Invalid method value");
}
};
if *is_static {
out!(self, "static ");
}
if is_async {
out!(self, "async ");
}
if generator {
out!(self, "*");
}
match *kind {
MethodDefinitionKind::Method => {}
MethodDefinitionKind::Constructor => {
// Will be handled by key output.
}
MethodDefinitionKind::Get => {
out!(self, "get ");
}
MethodDefinitionKind::Set => {
out!(self, "set ");
}
};
if *computed {
out!(self, "[");
}
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
if *computed {
out!(self, "]");
}
self.visit_func_params_body(
ctx,
params,
*type_parameters,
*return_type,
*predicate,
*body,
node,
);
}
Node::ImportDeclaration(ImportDeclaration {
metadata: _,
specifiers,
source,
assertions,
import_kind,
}) => {
out_token!(self, node, "import ");
if *import_kind != ImportKind::Value {
out!(self, "{} ", import_kind.as_str());
}
let mut has_named_specs = false;
for (i, spec) in specifiers.iter().enumerate() {
if i > 0 {
self.comma();
}
if let Node::ImportSpecifier(_) = spec {
if !has_named_specs {
has_named_specs = true;
out!(self, "{{");
}
}
spec.visit(ctx, self, Some(Path::new(node, NodeField::specifiers)));
}
if !specifiers.is_empty() {
if has_named_specs {
out!(self, "}}");
self.space(ForceSpace::No);
} else {
out!(self, " ");
}
out!(self, "from ");
}
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
if let Some(assertions) = assertions {
if !assertions.is_empty() {
out!(self, " assert {{");
for (i, attribute) in assertions.iter().enumerate() {
if i > 0 {
self.comma();
}
attribute.visit(
ctx,
self,
Some(Path::new(node, NodeField::assertions)),
);
}
out!(self, "}}");
}
}
}
Node::ImportSpecifier(ImportSpecifier {
metadata: _,
imported,
local,
import_kind,
}) => {
if *import_kind != ImportKind::Value {
out!(self, "{} ", import_kind.as_str());
}
imported.visit(ctx, self, Some(Path::new(node, NodeField::imported)));
out!(self, " as ");
local.visit(ctx, self, Some(Path::new(node, NodeField::local)));
}
Node::ImportDefaultSpecifier(ImportDefaultSpecifier { metadata: _, local }) => {
local.visit(ctx, self, Some(Path::new(node, NodeField::local)));
}
Node::ImportNamespaceSpecifier(ImportNamespaceSpecifier { metadata: _, local }) => {
out!(self, "* as ");
local.visit(ctx, self, Some(Path::new(node, NodeField::local)));
}
Node::ImportAttribute(ImportAttribute {
metadata: _,
key,
value,
}) => {
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
out!(self, ":");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
Node::ExportNamedDeclaration(ExportNamedDeclaration {
metadata: _,
declaration,
specifiers,
source,
export_kind,
}) => {
out_token!(self, node, "export ");
if let Some(declaration) = declaration {
declaration.visit(ctx, self, Some(Path::new(node, NodeField::declaration)));
} else {
if *export_kind != ExportKind::Value {
out!(self, "{} ", export_kind.as_str());
}
out!(self, "{{");
for (i, spec) in specifiers.iter().enumerate() {
if i > 0 {
self.comma();
}
spec.visit(ctx, self, Some(Path::new(node, NodeField::specifiers)));
}
out!(self, "}}");
if let Some(source) = source {
out!(self, " from ");
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
}
}
}
Node::ExportSpecifier(ExportSpecifier {
metadata: _,
exported,
local,
}) => {
local.visit(ctx, self, Some(Path::new(node, NodeField::local)));
out!(self, " as ");
exported.visit(ctx, self, Some(Path::new(node, NodeField::exported)));
}
Node::ExportNamespaceSpecifier(ExportNamespaceSpecifier {
metadata: _,
exported,
}) => {
out!(self, "* as ");
exported.visit(ctx, self, Some(Path::new(node, NodeField::exported)));
}
Node::ExportDefaultDeclaration(ExportDefaultDeclaration {
metadata: _,
declaration,
}) => {
out_token!(self, node, "export default ");
declaration.visit(ctx, self, Some(Path::new(node, NodeField::declaration)));
}
Node::ExportAllDeclaration(ExportAllDeclaration {
metadata: _,
source,
export_kind,
}) => {
out_token!(self, node, "export ");
if *export_kind != ExportKind::Value {
out!(self, "{} ", export_kind.as_str());
}
out!(self, "* from ");
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
}
Node::ObjectPattern(ObjectPattern {
metadata: _,
properties,
type_annotation,
}) => {
self.visit_props(ctx, properties, Path::new(node, NodeField::properties));
if let Some(type_annotation) = type_annotation {
out!(self, ":");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
}
Node::ArrayPattern(ArrayPattern {
metadata: _,
elements,
type_annotation,
}) => {
out!(self, "[");
for (i, elem) in elements.iter().enumerate() {
if i > 0 {
self.comma();
}
elem.visit(ctx, self, Some(Path::new(node, NodeField::elements)));
}
out!(self, "]");
if let Some(type_annotation) = type_annotation {
out!(self, ":");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
}
Node::RestElement(RestElement {
metadata: _,
argument,
}) => {
out!(self, "...");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
Node::AssignmentPattern(AssignmentPattern {
metadata: _,
left,
right,
}) => {
left.visit(ctx, self, Some(Path::new(node, NodeField::left)));
self.space(ForceSpace::No);
out!(self, "=");
self.space(ForceSpace::No);
right.visit(ctx, self, Some(Path::new(node, NodeField::right)));
}
Node::JSXIdentifier(JSXIdentifier { metadata: _, name }) => {
out_token!(self, node, "{}", ctx.str(*name));
}
Node::JSXMemberExpression(JSXMemberExpression {
metadata: _,
object,
property,
}) => {
object.visit(ctx, self, Some(Path::new(node, NodeField::object)));
out!(self, ".");
property.visit(ctx, self, Some(Path::new(node, NodeField::property)));
}
Node::JSXNamespacedName(JSXNamespacedName {
metadata: _,
namespace,
name,
}) => {
namespace.visit(ctx, self, Some(Path::new(node, NodeField::namespace)));
out!(self, ":");
name.visit(ctx, self, Some(Path::new(node, NodeField::name)));
}
Node::JSXEmptyExpression(_) => {}
Node::JSXExpressionContainer(JSXExpressionContainer {
metadata: _,
expression,
}) => {
out!(self, "{{");
expression.visit(ctx, self, Some(Path::new(node, NodeField::expression)));
out!(self, "}}");
}
Node::JSXSpreadChild(JSXSpreadChild {
metadata: _,
expression,
}) => {
out!(self, "{{...");
expression.visit(ctx, self, Some(Path::new(node, NodeField::expression)));
out!(self, "}}");
}
Node::JSXOpeningElement(JSXOpeningElement {
metadata: _,
name,
attributes,
self_closing,
}) => {
out!(self, "<");
name.visit(ctx, self, Some(Path::new(node, NodeField::name)));
for attr in attributes {
self.space(ForceSpace::Yes);
attr.visit(ctx, self, Some(Path::new(node, NodeField::attributes)));
}
if *self_closing {
out!(self, " />");
} else {
out!(self, ">");
}
}
Node::JSXClosingElement(JSXClosingElement { metadata: _, name }) => {
out!(self, "</");
name.visit(ctx, self, Some(Path::new(node, NodeField::name)));
out!(self, ">");
}
Node::JSXAttribute(JSXAttribute {
metadata: _,
name,
value,
}) => {
name.visit(ctx, self, Some(Path::new(node, NodeField::name)));
if let Some(value) = value {
out!(self, "=");
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
}
Node::JSXSpreadAttribute(JSXSpreadAttribute {
metadata: _,
argument,
}) => {
out!(self, "{{...");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
out!(self, "}}");
}
Node::JSXStringLiteral(JSXStringLiteral {
metadata: _,
value: _,
raw,
}) => {
let mut buf = [0u8; 4];
for char in ctx.str(*raw).chars() {
if char == '\n' {
self.force_newline_without_indent();
continue;
}
self.write_char(char, &mut buf);
}
}
Node::JSXText(JSXText {
metadata: _,
value: _,
raw,
}) => {
let mut buf = [0u8; 4];
for char in ctx.str(*raw).chars() {
if char == '\n' {
self.force_newline_without_indent();
continue;
}
self.write_char(char, &mut buf);
}
}
Node::JSXElement(JSXElement {
metadata: _,
opening_element,
children,
closing_element,
}) => {
opening_element.visit(ctx, self, Some(Path::new(node, NodeField::opening_element)));
if let Some(closing_element) = closing_element {
for child in children {
child.visit(ctx, self, Some(Path::new(node, NodeField::children)));
}
closing_element.visit(
ctx,
self,
Some(Path::new(node, NodeField::closing_element)),
);
}
}
Node::JSXFragment(JSXFragment {
metadata: _,
opening_fragment,
children,
closing_fragment,
}) => {
opening_fragment.visit(
ctx,
self,
Some(Path::new(node, NodeField::opening_fragment)),
);
for child in children {
child.visit(ctx, self, Some(Path::new(node, NodeField::children)));
}
closing_fragment.visit(
ctx,
self,
Some(Path::new(node, NodeField::closing_fragment)),
);
}
Node::JSXOpeningFragment(_) => {
out_token!(self, node, "<>");
}
Node::JSXClosingFragment(_) => {
out_token!(self, node, "</>");
}
Node::ExistsTypeAnnotation(_) => {
out_token!(self, node, "*");
}
Node::EmptyTypeAnnotation(_) => {
out_token!(self, node, "empty");
}
Node::StringTypeAnnotation(_) => {
out_token!(self, node, "string");
}
Node::NumberTypeAnnotation(_) => {
out_token!(self, node, "number");
}
Node::StringLiteralTypeAnnotation(StringLiteralTypeAnnotation {
metadata: _,
value,
raw,
}) => {
let quote = raw.str[0] as u8 as char;
out_token!(self, node, "{}", quote);
self.print_escaped_string_literal(value, quote);
out!(self, "{}", quote);
}
Node::NumberLiteralTypeAnnotation(NumberLiteralTypeAnnotation {
metadata: _,
value,
..
}) => {
out_token!(self, node, "{}", convert::number_to_string(*value));
}
Node::BooleanTypeAnnotation(_) => {
out_token!(self, node, "boolean");
}
Node::BooleanLiteralTypeAnnotation(BooleanLiteralTypeAnnotation {
metadata: _,
value,
..
}) => {
out_token!(self, node, "{}", if *value { "true" } else { "false" });
}
Node::NullLiteralTypeAnnotation(_) => {
out_token!(self, node, "null");
}
Node::SymbolTypeAnnotation(_) => {
out_token!(self, node, "symbol");
}
Node::AnyTypeAnnotation(_) => {
out_token!(self, node, "any");
}
Node::MixedTypeAnnotation(_) => {
out_token!(self, node, "mixed");
}
Node::VoidTypeAnnotation(_) => {
out_token!(self, node, "void");
}
Node::FunctionTypeAnnotation(FunctionTypeAnnotation {
metadata: _,
params,
this,
return_type,
rest,
type_parameters,
}) => {
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
out!(self, "(");
let mut need_comma = false;
if let Some(this) = this {
match this {
Node::FunctionTypeParam(FunctionTypeParam {
metadata: _,
type_annotation,
..
}) => {
out!(self, "this:");
self.space(ForceSpace::No);
type_annotation.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_annotation)),
);
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
this.visit(ctx, self, Some(Path::new(node, NodeField::this)));
need_comma = true;
}
for param in params.iter() {
if need_comma {
self.comma();
}
param.visit(ctx, self, Some(Path::new(node, NodeField::param)));
need_comma = true;
}
if let Some(rest) = rest {
if need_comma {
self.comma();
}
out!(self, "...");
rest.visit(ctx, self, Some(Path::new(node, NodeField::rest)));
}
out!(self, ")");
if self.pretty == Pretty::Yes {
out!(self, " => ");
} else {
out!(self, "=>");
}
return_type.visit(ctx, self, Some(Path::new(node, NodeField::return_type)));
}
Node::FunctionTypeParam(FunctionTypeParam {
metadata: _,
name,
type_annotation,
optional,
}) => {
if let Some(name) = name {
name.visit(ctx, self, Some(Path::new(node, NodeField::name)));
if *optional {
out!(self, "?");
}
out!(self, ":");
self.space(ForceSpace::No);
}
type_annotation.visit(ctx, self, Some(Path::new(node, NodeField::type_annotation)));
}
Node::NullableTypeAnnotation(NullableTypeAnnotation {
metadata: _,
type_annotation,
}) => {
out!(self, "?");
self.print_child(
ctx,
Some(type_annotation),
Path::new(node, NodeField::type_annotation),
ChildPos::Right,
);
}
Node::QualifiedTypeIdentifier(QualifiedTypeIdentifier {
metadata: _,
qualification,
id,
}) => {
qualification.visit(ctx, self, Some(Path::new(node, NodeField::qualification)));
out!(self, ".");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
Node::TypeofTypeAnnotation(TypeofTypeAnnotation {
metadata: _,
argument,
}) => {
out!(self, "typeof ");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
Node::TupleTypeAnnotation(TupleTypeAnnotation { metadata: _, types }) => {
out!(self, "[");
for (i, ty) in types.iter().enumerate() {
if i > 0 {
self.comma();
}
ty.visit(ctx, self, Some(Path::new(node, NodeField::types)));
}
out!(self, "]");
}
Node::ArrayTypeAnnotation(ArrayTypeAnnotation {
metadata: _,
element_type,
}) => {
element_type.visit(ctx, self, Some(Path::new(node, NodeField::element_type)));
out!(self, "[]");
}
Node::UnionTypeAnnotation(UnionTypeAnnotation { metadata: _, types }) => {
for (i, ty) in types.iter().enumerate() {
if i > 0 {
self.space(ForceSpace::No);
out!(self, "|");
self.space(ForceSpace::No);
}
self.print_child(
ctx,
Some(*ty),
Path::new(node, NodeField::types),
ChildPos::Anywhere,
);
}
}
Node::IntersectionTypeAnnotation(IntersectionTypeAnnotation { metadata: _, types }) => {
for (i, ty) in types.iter().enumerate() {
if i > 0 {
self.space(ForceSpace::No);
out!(self, "&");
self.space(ForceSpace::No);
}
self.print_child(
ctx,
Some(*ty),
Path::new(node, NodeField::types),
ChildPos::Anywhere,
);
}
}
Node::GenericTypeAnnotation(GenericTypeAnnotation {
metadata: _,
id,
type_parameters,
}) => {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
}
Node::IndexedAccessType(IndexedAccessType {
metadata: _,
object_type,
index_type,
}) => {
object_type.visit(ctx, self, Some(Path::new(node, NodeField::object_type)));
out!(self, "[");
index_type.visit(ctx, self, Some(Path::new(node, NodeField::index_type)));
out!(self, "]");
}
Node::OptionalIndexedAccessType(OptionalIndexedAccessType {
metadata: _,
object_type,
index_type,
optional,
}) => {
object_type.visit(ctx, self, Some(Path::new(node, NodeField::object_type)));
out!(self, "{}[", if *optional { "?." } else { "" });
index_type.visit(ctx, self, Some(Path::new(node, NodeField::index_type)));
out!(self, "]");
}
Node::InterfaceTypeAnnotation(InterfaceTypeAnnotation {
metadata: _,
extends,
body,
}) => {
out!(self, "interface");
if !extends.is_empty() {
out!(self, " extends ");
for (i, extend) in extends.iter().enumerate() {
if i > 0 {
self.comma();
}
extend.visit(ctx, self, Some(Path::new(node, NodeField::extends)));
}
} else {
self.space(ForceSpace::No);
}
if let Some(body) = body {
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
}
Node::TypeAlias(TypeAlias {
metadata: _,
id,
type_parameters,
right,
})
| Node::DeclareTypeAlias(DeclareTypeAlias {
metadata: _,
id,
type_parameters,
right,
}) => {
if matches!(&node, Node::DeclareTypeAlias(_)) {
out_token!(self, node, "declare type");
} else {
out_token!(self, node, "type ");
}
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
if self.pretty == Pretty::Yes {
out!(self, " = ");
} else {
out!(self, "=");
}
right.visit(ctx, self, Some(Path::new(node, NodeField::right)));
}
Node::OpaqueType(OpaqueType {
metadata: _,
id,
type_parameters,
impltype,
supertype,
}) => {
out_token!(self, node, "opaque type ");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
if let Some(supertype) = supertype {
out!(self, ":");
self.space(ForceSpace::No);
supertype.visit(ctx, self, Some(Path::new(node, NodeField::supertype)));
}
if self.pretty == Pretty::Yes {
out!(self, " = ");
} else {
out!(self, "=");
}
impltype.visit(ctx, self, Some(Path::new(node, NodeField::impltype)));
}
Node::InterfaceDeclaration(InterfaceDeclaration {
metadata: _,
id,
type_parameters,
extends,
body,
})
| Node::DeclareInterface(DeclareInterface {
metadata: _,
id,
type_parameters,
extends,
body,
}) => {
self.visit_interface(
ctx,
if matches!(node, Node::InterfaceDeclaration(_)) {
"interface"
} else {
"declare interface"
},
*id,
*type_parameters,
extends,
*body,
node,
);
}
Node::DeclareOpaqueType(DeclareOpaqueType {
metadata: _,
id,
type_parameters,
impltype,
supertype,
}) => {
out_token!(self, node, "opaque type ");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
if let Some(supertype) = supertype {
out!(self, ":");
self.space(ForceSpace::No);
supertype.visit(ctx, self, Some(Path::new(node, NodeField::supertype)));
}
if self.pretty == Pretty::Yes {
out!(self, " = ");
} else {
out!(self, "=");
}
if let Some(impltype) = impltype {
impltype.visit(ctx, self, Some(Path::new(node, NodeField::impltype)));
}
}
Node::DeclareClass(DeclareClass {
metadata: _,
id,
type_parameters,
extends,
implements,
mixins,
body,
}) => {
out_token!(self, node, "declare class ");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
if !extends.is_empty() {
out!(self, " extends ");
for (i, extend) in extends.iter().enumerate() {
if i > 0 {
self.comma();
}
extend.visit(ctx, self, Some(Path::new(node, NodeField::extends)));
}
}
if !mixins.is_empty() {
out!(self, " mixins ");
for (i, mixin) in mixins.iter().enumerate() {
if i > 0 {
self.comma();
}
mixin.visit(ctx, self, Some(Path::new(node, NodeField::mixins)));
}
}
if !implements.is_empty() {
out!(self, " implements ");
for (i, implement) in implements.iter().enumerate() {
if i > 0 {
self.comma();
}
implement.visit(ctx, self, Some(Path::new(node, NodeField::implements)));
}
}
self.space(ForceSpace::No);
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
Node::DeclareFunction(DeclareFunction {
metadata: _,
id,
predicate,
}) => {
// This AST type uses the Identifier/TypeAnnotation
// pairing to put a name on a function header-looking construct,
// so we have to do some deep matching to get it to come out right.
out_token!(self, node, "declare function ");
match id {
Node::Identifier(Identifier {
metadata: _,
name,
type_annotation,
..
}) => {
out!(self, "{}", &ctx.str(*name));
match type_annotation {
Some(Node::TypeAnnotation(TypeAnnotation {
metadata: _,
type_annotation:
Node::FunctionTypeAnnotation(FunctionTypeAnnotation {
metadata: _,
params,
this,
return_type,
rest,
type_parameters,
}),
})) => {
self.visit_func_type_params(
ctx,
params,
*this,
*rest,
*type_parameters,
node,
);
out!(self, ":");
self.space(ForceSpace::No);
return_type.visit(
ctx,
self,
Some(Path::new(node, NodeField::return_type)),
);
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
if let Some(predicate) = predicate {
self.space(ForceSpace::No);
predicate.visit(ctx, self, Some(Path::new(node, NodeField::predicate)));
}
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
}
Node::DeclareVariable(DeclareVariable { metadata: _, id }) => {
if let Some(path) = path {
if !matches!(path.parent, Node::DeclareExportDeclaration(_)) {
out!(self, "declare ");
}
}
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
Node::DeclareExportDeclaration(DeclareExportDeclaration {
metadata: _,
declaration,
specifiers,
source,
default,
}) => {
out_token!(self, node, "declare export ");
if *default {
out!(self, "default ");
}
if let Some(declaration) = declaration {
declaration.visit(ctx, self, Some(Path::new(node, NodeField::declaration)));
} else {
out!(self, "{{");
for (i, spec) in specifiers.iter().enumerate() {
if i > 0 {
self.comma();
}
spec.visit(ctx, self, Some(Path::new(node, NodeField::specifiers)));
}
out!(self, "}}");
if let Some(source) = source {
out!(self, " from ");
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
}
}
}
Node::DeclareExportAllDeclaration(DeclareExportAllDeclaration {
metadata: _,
source,
}) => {
out_token!(self, node, "declare export * from ");
source.visit(ctx, self, Some(Path::new(node, NodeField::source)));
}
Node::DeclareModule(DeclareModule {
metadata: _,
id,
body,
..
}) => {
out!(self, "declare module ");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
self.space(ForceSpace::No);
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
Node::DeclareModuleExports(DeclareModuleExports {
metadata: _,
type_annotation,
}) => {
out!(self, "declare module.exports:");
self.space(ForceSpace::No);
type_annotation.visit(ctx, self, Some(Path::new(node, NodeField::type_annotation)));
}
Node::InterfaceExtends(InterfaceExtends {
metadata: _,
id,
type_parameters,
})
| Node::ClassImplements(ClassImplements {
metadata: _,
id,
type_parameters,
}) => {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if let Some(type_parameters) = type_parameters {
type_parameters.visit(
ctx,
self,
Some(Path::new(node, NodeField::type_parameters)),
);
}
}
Node::TypeAnnotation(TypeAnnotation {
metadata: _,
type_annotation,
}) => {
type_annotation.visit(ctx, self, Some(Path::new(node, NodeField::type_annotation)));
}
Node::ObjectTypeAnnotation(ObjectTypeAnnotation {
metadata: _,
properties,
indexers,
call_properties,
internal_slots,
inexact,
exact,
}) => {
out!(self, "{}", if *exact { "{|" } else { "{" });
self.inc_indent();
self.newline();
let mut need_comma = false;
for prop in properties {
if need_comma {
self.comma();
}
prop.visit(ctx, self, Some(Path::new(node, NodeField::properties)));
self.newline();
need_comma = true;
}
for prop in indexers {
if need_comma {
self.comma();
}
prop.visit(ctx, self, Some(Path::new(node, NodeField::indexers)));
self.newline();
need_comma = true;
}
for prop in call_properties {
if need_comma {
self.comma();
}
prop.visit(ctx, self, Some(Path::new(node, NodeField::call_properties)));
self.newline();
need_comma = true;
}
for prop in internal_slots {
if need_comma {
self.comma();
}
prop.visit(ctx, self, Some(Path::new(node, NodeField::internal_slots)));
self.newline();
need_comma = true;
}
if *inexact {
if need_comma {
self.comma();
}
out!(self, "...");
}
self.dec_indent();
self.newline();
out!(self, "{}", if *exact { "|}" } else { "}" });
}
Node::ObjectTypeProperty(ObjectTypeProperty {
metadata: _,
key,
value,
method,
optional,
is_static,
proto,
variance,
..
}) => {
if let Some(variance) = variance {
variance.visit(ctx, self, Some(Path::new(node, NodeField::variance)));
}
if *is_static {
out!(self, "static ");
}
if *proto {
out!(self, "proto ");
}
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
if *optional {
out!(self, "?");
}
if *method {
match value {
Node::FunctionTypeAnnotation(FunctionTypeAnnotation {
metadata: _,
params,
this,
return_type,
rest,
type_parameters,
}) => {
self.visit_func_type_params(
ctx,
params,
*this,
*rest,
*type_parameters,
node,
);
out!(self, ":");
self.space(ForceSpace::No);
return_type.visit(
ctx,
self,
Some(Path::new(node, NodeField::return_type)),
);
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
} else {
out!(self, ":");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
}
Node::ObjectTypeSpreadProperty(ObjectTypeSpreadProperty {
metadata: _,
argument,
}) => {
out!(self, "...");
argument.visit(ctx, self, Some(Path::new(node, NodeField::argument)));
}
Node::ObjectTypeInternalSlot(ObjectTypeInternalSlot {
metadata: _,
id,
value,
optional,
is_static,
method,
}) => {
if *is_static {
out!(self, "static ");
}
out!(self, "[[");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
if *optional {
out!(self, "?");
}
out!(self, "]]");
if *method {
match value {
Node::FunctionTypeAnnotation(FunctionTypeAnnotation {
metadata: _,
params,
this,
return_type,
rest,
type_parameters,
}) => {
self.visit_func_type_params(
ctx,
params,
*this,
*rest,
*type_parameters,
node,
);
out!(self, ":");
self.space(ForceSpace::No);
return_type.visit(
ctx,
self,
Some(Path::new(node, NodeField::return_type)),
);
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
} else {
out!(self, ":");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
}
Node::ObjectTypeCallProperty(ObjectTypeCallProperty {
metadata: _,
value,
is_static,
}) => {
if *is_static {
out!(self, "static ");
}
match value {
Node::FunctionTypeAnnotation(FunctionTypeAnnotation {
metadata: _,
params,
this,
return_type,
rest,
type_parameters,
}) => {
self.visit_func_type_params(
ctx,
params,
*this,
*rest,
*type_parameters,
node,
);
out!(self, ":");
self.space(ForceSpace::No);
return_type.visit(ctx, self, Some(Path::new(node, NodeField::return_type)));
}
_ => {
unimplemented!("Malformed AST: Need to handle error");
}
}
}
Node::ObjectTypeIndexer(ObjectTypeIndexer {
metadata: _,
id,
key,
value,
is_static,
variance,
}) => {
if *is_static {
out!(self, "static ");
}
if let Some(variance) = variance {
variance.visit(ctx, self, Some(Path::new(node, NodeField::variance)));
}
out!(self, "[");
if let Some(id) = id {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
out!(self, ":");
self.space(ForceSpace::No);
}
key.visit(ctx, self, Some(Path::new(node, NodeField::key)));
out!(self, "]");
out!(self, ":");
self.space(ForceSpace::No);
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
}
Node::Variance(Variance { metadata: _, kind }) => {
out_token!(
self,
node,
"{}",
match ctx.str(*kind) {
"plus" => "+",
"minus" => "-",
_ => unimplemented!("Malformed variance"),
}
)
}
Node::TypeParameterDeclaration(TypeParameterDeclaration {
metadata: _,
params,
})
| Node::TypeParameterInstantiation(TypeParameterInstantiation {
metadata: _,
params,
}) => {
out!(self, "<");
for (i, param) in params.iter().enumerate() {
if i > 0 {
self.comma();
}
param.visit(ctx, self, Some(Path::new(node, NodeField::params)));
}
out!(self, ">");
}
Node::TypeParameter(TypeParameter {
metadata: _,
name,
bound,
variance,
default,
}) => {
if let Some(variance) = variance {
variance.visit(ctx, self, Some(Path::new(node, NodeField::variance)));
}
out!(self, "{}", ctx.str(*name));
if let Some(bound) = bound {
out!(self, ":");
self.space(ForceSpace::No);
bound.visit(ctx, self, Some(Path::new(node, NodeField::bound)));
}
if let Some(default) = default {
out!(self, "=");
self.space(ForceSpace::No);
default.visit(ctx, self, Some(Path::new(node, NodeField::default)));
}
}
Node::TypeCastExpression(TypeCastExpression {
metadata: _,
expression,
type_annotation,
}) => {
// Type casts are required to have pathheses.
out!(self, "(");
self.print_child(
ctx,
Some(*expression),
Path::new(node, NodeField::expression),
ChildPos::Left,
);
out!(self, ":");
self.space(ForceSpace::No);
self.print_child(
ctx,
Some(*type_annotation),
Path::new(node, NodeField::type_annotation),
ChildPos::Right,
);
out!(self, ")");
}
Node::InferredPredicate(_) => {
out_token!(self, node, "%checks");
}
Node::DeclaredPredicate(DeclaredPredicate { metadata: _, value }) => {
out_token!(self, node, "%checks(");
value.visit(ctx, self, Some(Path::new(node, NodeField::value)));
out!(self, ")");
}
Node::EnumDeclaration(EnumDeclaration {
metadata: _,
id,
body,
}) => {
out_token!(self, node, "enum ");
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
body.visit(ctx, self, Some(Path::new(node, NodeField::body)));
}
Node::EnumStringBody(EnumStringBody {
metadata: _,
members,
explicit_type,
has_unknown_members,
}) => {
self.visit_enum_body(
ctx,
"string",
members,
*explicit_type,
*has_unknown_members,
node,
);
}
Node::EnumNumberBody(EnumNumberBody {
metadata: _,
members,
explicit_type,
has_unknown_members,
}) => {
self.visit_enum_body(
ctx,
"number",
members,
*explicit_type,
*has_unknown_members,
node,
);
}
Node::EnumBooleanBody(EnumBooleanBody {
metadata: _,
members,
explicit_type,
has_unknown_members,
}) => {
self.visit_enum_body(
ctx,
"boolean",
members,
*explicit_type,
*has_unknown_members,
node,
);
}
Node::EnumSymbolBody(EnumSymbolBody {
metadata: _,
members,
has_unknown_members,
}) => {
self.visit_enum_body(ctx, "symbol", members, true, *has_unknown_members, node);
}
Node::EnumDefaultedMember(EnumDefaultedMember { metadata: _, id }) => {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
}
Node::EnumStringMember(EnumStringMember {
metadata: _,
id,
init,
})
| Node::EnumNumberMember(EnumNumberMember {
metadata: _,
id,
init,
})
| Node::EnumBooleanMember(EnumBooleanMember {
metadata: _,
id,
init,
}) => {
id.visit(ctx, self, Some(Path::new(node, NodeField::id)));
out!(
self,
"{}",
match self.pretty {
Pretty::Yes => " = ",
Pretty::No => "=",
}
);
init.visit(ctx, self, Some(Path::new(node, NodeField::init)));
}
_ => {
unimplemented!("Cannot generate node kind: {}", node.name());
}
};
}