// Copyright (C) 2020-2023 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// .
#include "rust-ast-dump.h"
namespace Rust {
namespace AST {
Indent::Indent () : tabs (0) {}
std::ostream &
operator<< (std::ostream &stream, const Indent &indent)
{
return stream << std::string (indent.tabs, '\t');
}
void
Indent::increment ()
{
tabs++;
}
void
Indent::decrement ()
{
rust_assert (tabs != 0);
tabs--;
}
Dump::Dump (std::ostream &stream) : stream (stream), indentation (Indent ()) {}
void
Dump::go (AST::Crate &crate)
{
visit_items_as_lines (crate.items, "");
}
void
Dump::go (AST::Item &item)
{
item.accept_vis (*this);
}
template
void
Dump::visit (std::unique_ptr &node)
{
node->accept_vis (*this);
}
template
void
Dump::visit (T &node)
{
node.accept_vis (*this);
}
template
void
Dump::visit_items_joined_by_separator (T &collection,
const std::string &separator,
size_t start_offset, size_t end_offset)
{
if (collection.size () > start_offset)
{
visit (collection.at (start_offset));
auto size = collection.size () - end_offset;
for (size_t i = start_offset + 1; i < size; i++)
{
stream << separator;
visit (collection.at (i));
}
}
}
template
void
Dump::visit_as_line (T &item, const std::string &trailing)
{
stream << indentation;
visit (item);
stream << trailing << '\n';
}
template
void
Dump::visit_items_as_lines (T &collection, const std::string &trailing)
{
for (auto &item : collection)
visit_as_line (item, trailing);
}
template
void
Dump::visit_items_as_block (T &collection, const std::string &line_trailing,
char left_brace, char right_brace)
{
if (collection.empty ())
{
stream << left_brace << right_brace << '\n';
}
else
{
stream << left_brace << '\n';
indentation.increment ();
visit_items_as_lines (collection, line_trailing);
indentation.decrement ();
stream << indentation << right_brace << '\n';
}
}
void
Dump::visit (FunctionParam ¶m)
{
visit (param.get_pattern ());
stream << ": ";
visit (param.get_type ());
}
void
Dump::visit (Attribute &attrib)
{
stream << "#[";
visit_items_joined_by_separator (attrib.get_path ().get_segments (), "::");
if (attrib.has_attr_input ())
{
stream << " = ";
bool is_literal = attrib.get_attr_input ().get_attr_input_type ()
== AST::AttrInput::AttrInputType::LITERAL;
if (is_literal)
{
auto &literal
= static_cast (attrib.get_attr_input ());
const auto &value = literal.get_literal ().as_string ();
stream << "\"" << value << "\"";
}
else
{
stream << "FIXME";
}
}
stream << "]";
}
void
Dump::visit (SimplePathSegment &segment)
{
stream << segment.get_segment_name ();
}
void
Dump::visit (Visibility &vis)
{
switch (vis.get_vis_type ())
{
case Visibility::PUB:
stream << "pub ";
break;
case Visibility::PUB_CRATE:
stream << "pub(crate) ";
break;
case Visibility::PUB_SELF:
stream << "pub(self) ";
break;
case Visibility::PUB_SUPER:
stream << "pub(super) ";
break;
case Visibility::PUB_IN_PATH:
stream << "pub(in " << vis.get_path ().as_string () << ") ";
break;
case Visibility::PRIV:
break;
}
}
void
Dump::visit (NamedFunctionParam ¶m)
{
stream << param.get_name () << ": ";
visit (param.get_type ());
}
void
Dump::visit (std::vector> ¶ms)
{
stream << "<";
visit_items_joined_by_separator (params, ", ");
stream << ">";
}
void
Dump::visit (TupleField &field)
{
// TODO: do we need to emit outer attrs here?
visit (field.get_visibility ());
visit (field.get_field_type ());
}
void
Dump::visit (StructField &field)
{
// TODO: do we need to emit outer attrs here?
visit (field.get_visibility ());
stream << field.get_field_name () << ": ";
visit (field.get_field_type ());
}
// TODO is this unique by type?
void
Dump::visit (std::vector &for_lifetimes)
{
// ForLifetimes :
// for GenericParams
//
// GenericParams :
// < >
// | < (GenericParam ,)* GenericParam ,? >
//
// GenericParam :
// OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )
//
// LifetimeParam :
// LIFETIME_OR_LABEL ( : LifetimeBounds )?
stream << "for <";
visit_items_joined_by_separator (for_lifetimes, " + ");
stream << "> ";
}
void
Dump::visit (FunctionQualifiers &qualifiers)
{
// Syntax:
// `const`? `async`? `unsafe`? (`extern` Abi?)?
// unsafe? (extern Abi?)?
switch (qualifiers.get_const_status ())
{
case NONE:
break;
case CONST_FN:
stream << "const ";
break;
case ASYNC_FN:
stream << "async ";
break;
}
if (qualifiers.is_unsafe ())
stream << "unsafe ";
if (qualifiers.is_extern ())
{
stream << "extern ";
if (qualifiers.has_abi ())
stream << "\"" << qualifiers.get_extern_abi () << "\" ";
}
} // namespace AST
void
Dump::visit (MaybeNamedParam ¶m)
{
// Syntax:
// OuterAttribute* ( ( IDENTIFIER | _ ) : )? Type
visit_items_joined_by_separator (param.get_outer_attrs (), " ");
switch (param.get_param_kind ())
{
case MaybeNamedParam::UNNAMED:
break;
case MaybeNamedParam::IDENTIFIER:
stream << " " << param.get_name () << ": ";
break;
case MaybeNamedParam::WILDCARD:
stream << " _: ";
break;
}
visit (param.get_type ());
}
void
Dump::visit (Token &tok)
{
stream << tok.as_string ();
}
void
Dump::visit (DelimTokenTree &delim_tok_tree)
{
indentation.increment ();
stream << '\n' << indentation;
auto tokens = delim_tok_tree.to_token_stream ();
visit_items_joined_by_separator (tokens, " ");
indentation.decrement ();
stream << '\n' << indentation;
}
void
Dump::visit (AttrInputMetaItemContainer &)
{}
void
Dump::visit (IdentifierExpr &ident_expr)
{
stream << ident_expr.get_ident ();
}
void
Dump::visit (Lifetime &lifetime)
{
// Syntax:
// Lifetime :
// LIFETIME_OR_LABEL
// | 'static
// | '_
stream << lifetime.as_string ();
}
void
Dump::visit (LifetimeParam &lifetime_param)
{
// Syntax:
// LIFETIME_OR_LABEL ( : LifetimeBounds )?
// LifetimeBounds :
// ( Lifetime + )* Lifetime?
// TODO what to do with outer attr? They are not mentioned in the reference.
auto lifetime = lifetime_param.get_lifetime ();
visit (lifetime);
if (lifetime_param.has_lifetime_bounds ())
{
stream << ": ";
visit_items_joined_by_separator (lifetime_param.get_lifetime_bounds (),
" + ");
}
}
void
Dump::visit (ConstGenericParam &)
{}
// rust-path.h
void
Dump::visit (PathInExpression &path)
{
stream << path.as_string ();
}
void
Dump::visit (TypePathSegment &segment)
{
// Syntax:
// PathIdentSegment
stream << segment.get_ident_segment ().as_string ();
}
void
Dump::visit (TypePathSegmentGeneric &segment)
{
// Syntax:
// PathIdentSegment `::`? (GenericArgs)?
// GenericArgs :
// `<` `>`
// | `<` ( GenericArg `,` )* GenericArg `,`? `>`
stream << segment.get_ident_segment ().as_string ();
if (segment.get_separating_scope_resolution ())
stream << "::";
stream << "<";
{
// Here we join 3 lists (each possibly empty) with a separator.
auto &lifetime_args = segment.get_generic_args ().get_lifetime_args ();
auto &generic_args = segment.get_generic_args ().get_generic_args ();
auto &binding_args = segment.get_generic_args ().get_binding_args ();
visit_items_joined_by_separator (lifetime_args, ", ");
if (!lifetime_args.empty ()
&& (!generic_args.empty () || !binding_args.empty ()))
{
// Insert separator if some items have been already emitted and some
// more are to be emitted from any of the following collections.
stream << ", ";
}
visit_items_joined_by_separator (generic_args, ", ");
if (!generic_args.empty () && !binding_args.empty ())
{
// Insert separator if some item vas emitted from the previous
// collection and more are to be emitted from the last.
stream << ", ";
}
visit_items_joined_by_separator (binding_args, ", ");
}
stream << ">";
}
void
Dump::visit (GenericArgsBinding &binding)
{
// Syntax:
// IDENTIFIER `=` Type
stream << binding.get_identifier () << " << ";
visit (binding.get_type ());
}
void
Dump::visit (GenericArg &arg)
{
// `GenericArg` implements `accept_vis` but it is not useful for this case as
// it ignores unresolved cases (`Kind::Either`).
switch (arg.get_kind ())
{
case GenericArg::Kind::Const:
visit (arg.get_expression ());
break;
case GenericArg::Kind::Type:
visit (arg.get_type ());
break;
case GenericArg::Kind::Either:
stream << arg.get_path ();
break;
case GenericArg::Kind::Error:
gcc_unreachable ();
}
} // namespace AST
void
Dump::visit (TypePathSegmentFunction &segment)
{
// Syntax:
// PathIdentSegment `::`? (TypePathFn)?
stream << segment.get_ident_segment ().as_string ();
if (segment.get_separating_scope_resolution ())
stream << "::";
if (!segment.is_ident_only ())
visit (segment.get_type_path_function ());
}
void
Dump::visit (TypePathFunction &type_path_fn)
{
// Syntax:
// `(` TypePathFnInputs? `)` (`->` Type)?
// TypePathFnInputs :
// Type (`,` Type)* `,`?
stream << '(';
if (type_path_fn.has_inputs ())
visit_items_joined_by_separator (type_path_fn.get_params (), ", ");
stream << ')';
if (type_path_fn.has_return_type ())
{
stream << "->";
visit (type_path_fn.get_return_type ());
}
}
void
Dump::visit (TypePath &path)
{
// Syntax:
// `::`? TypePathSegment (`::` TypePathSegment)*
if (path.has_opening_scope_resolution_op ())
stream << "::";
visit_items_joined_by_separator (path.get_segments (), "::");
}
void
Dump::visit (QualifiedPathInExpression &path)
{
stream << path.as_string ();
}
void
Dump::visit (QualifiedPathInType &)
{}
// rust-expr.h
void
Dump::visit (LiteralExpr &expr)
{
stream << expr.as_string ();
}
void
Dump::visit (AttrInputLiteral &)
{}
void
Dump::visit (MetaItemLitExpr &)
{}
void
Dump::visit (MetaItemPathLit &)
{}
void
Dump::visit (BorrowExpr &expr)
{
stream << '&';
if (expr.get_is_double_borrow ())
stream << '&';
if (expr.get_is_mut ())
stream << "mut ";
visit (expr.get_borrowed_expr ());
}
void
Dump::visit (DereferenceExpr &expr)
{
stream << '*';
visit (expr.get_dereferenced_expr ());
}
void
Dump::visit (ErrorPropagationExpr &expr)
{
visit (expr.get_propagating_expr ());
stream << '?';
}
void
Dump::visit (NegationExpr &expr)
{
switch (expr.get_expr_type ())
{
case NegationOperator::NEGATE:
stream << '-';
break;
case NegationOperator::NOT:
stream << '!';
break;
}
visit (expr.get_negated_expr ());
}
void
Dump::visit (ArithmeticOrLogicalExpr &expr)
{
auto op = "";
switch (expr.get_expr_type ())
{
case ArithmeticOrLogicalOperator::ADD:
op = "+";
break;
case ArithmeticOrLogicalOperator::SUBTRACT:
op = "-";
break;
case ArithmeticOrLogicalOperator::MULTIPLY:
op = "*";
break;
case ArithmeticOrLogicalOperator::DIVIDE:
op = "/";
break;
case ArithmeticOrLogicalOperator::MODULUS:
op = "%";
break;
case ArithmeticOrLogicalOperator::BITWISE_AND:
op = "&";
break;
case ArithmeticOrLogicalOperator::BITWISE_OR:
op = "|";
break;
case ArithmeticOrLogicalOperator::BITWISE_XOR:
op = "^";
break;
case ArithmeticOrLogicalOperator::LEFT_SHIFT:
op = "<<";
break;
case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
op = ">>";
break;
}
visit (expr.get_left_expr ());
stream << " " << op << " ";
visit (expr.get_right_expr ());
}
void
Dump::visit (ComparisonExpr &expr)
{
auto op = "";
switch (expr.get_expr_type ())
{
case ComparisonOperator::EQUAL:
op = "==";
break;
case ComparisonOperator::NOT_EQUAL:
op = "!=";
break;
case ComparisonOperator::GREATER_THAN:
op = ">";
break;
case ComparisonOperator::LESS_THAN:
op = "<";
break;
case ComparisonOperator::GREATER_OR_EQUAL:
op = ">=";
break;
case ComparisonOperator::LESS_OR_EQUAL:
op = "<=";
break;
}
visit (expr.get_left_expr ());
stream << " " << op << " ";
visit (expr.get_right_expr ());
}
void
Dump::visit (LazyBooleanExpr &expr)
{
auto op = "";
switch (expr.get_expr_type ())
{
case LazyBooleanOperator::LOGICAL_AND:
op = "&&";
break;
case LazyBooleanOperator::LOGICAL_OR:
op = "||";
break;
}
visit (expr.get_left_expr ());
stream << " " << op << " ";
visit (expr.get_right_expr ());
}
void
Dump::visit (TypeCastExpr &expr)
{
visit (expr.get_casted_expr ());
stream << " as ";
visit (expr.get_type_to_cast_to ());
}
void
Dump::visit (AssignmentExpr &expr)
{
expr.visit_lhs (*this);
stream << " = ";
expr.visit_rhs (*this);
}
void
Dump::visit (CompoundAssignmentExpr &expr)
{
auto op = "";
switch (expr.get_expr_type ())
{
case CompoundAssignmentOperator::ADD:
op = "+";
break;
case CompoundAssignmentOperator::SUBTRACT:
op = "-";
break;
case CompoundAssignmentOperator::MULTIPLY:
op = "*";
break;
case CompoundAssignmentOperator::DIVIDE:
op = "/";
break;
case CompoundAssignmentOperator::MODULUS:
op = "%";
break;
case CompoundAssignmentOperator::BITWISE_AND:
op = "&";
break;
case CompoundAssignmentOperator::BITWISE_OR:
op = "|";
break;
case CompoundAssignmentOperator::BITWISE_XOR:
op = "^";
break;
case CompoundAssignmentOperator::LEFT_SHIFT:
op = "<<";
break;
case CompoundAssignmentOperator::RIGHT_SHIFT:
op = ">>";
break;
}
visit (expr.get_left_expr ());
stream << " " << op << "= ";
visit (expr.get_right_expr ());
}
void
Dump::visit (GroupedExpr &expr)
{
stream << '(';
visit (expr.get_expr_in_parens ());
stream << ')';
}
void
Dump::visit (ArrayElemsValues &elems)
{
visit_items_joined_by_separator (elems.get_values (), ", ");
}
void
Dump::visit (ArrayElemsCopied &elems)
{
visit (elems.get_elem_to_copy ());
stream << "; ";
visit (elems.get_num_copies ());
}
void
Dump::visit (ArrayExpr &expr)
{
stream << '[';
visit (expr.get_array_elems ());
stream << ']';
}
void
Dump::visit (ArrayIndexExpr &expr)
{
visit (expr.get_array_expr ());
stream << '[';
visit (expr.get_index_expr ());
stream << ']';
}
void
Dump::visit (TupleExpr &)
{}
void
Dump::visit (TupleIndexExpr &)
{}
void
Dump::visit (StructExprStruct &)
{}
void
Dump::visit (StructExprFieldIdentifier &)
{}
void
Dump::visit (StructExprFieldIdentifierValue &)
{}
void
Dump::visit (StructExprFieldIndexValue &)
{}
void
Dump::visit (StructExprStructFields &)
{}
void
Dump::visit (StructExprStructBase &)
{}
void
Dump::visit (CallExpr &expr)
{
visit (expr.get_function_expr ());
stream << '(' << '\n';
indentation.increment ();
visit_items_as_lines (expr.get_params (), ",");
indentation.decrement ();
stream << indentation << ')';
}
void
Dump::visit (MethodCallExpr &)
{}
void
Dump::visit (FieldAccessExpr &)
{}
void
Dump::visit (ClosureExprInner &)
{}
void
Dump::visit (BlockExpr &expr)
{
stream << "{\n";
indentation.increment ();
visit_items_as_lines (expr.get_statements (), ";");
if (expr.has_tail_expr ())
visit_as_line (expr.get_tail_expr (), " /* tail expr */\n");
indentation.decrement ();
stream << indentation << "}\n";
}
void
Dump::visit (ClosureExprInnerTyped &)
{}
void
Dump::visit (ContinueExpr &)
{}
void
Dump::visit (BreakExpr &)
{}
void
Dump::visit (RangeFromToExpr &expr)
{
visit (expr.get_from_expr ());
stream << "..";
visit (expr.get_to_expr ());
}
void
Dump::visit (RangeFromExpr &expr)
{
visit (expr.get_from_expr ());
stream << "..";
}
void
Dump::visit (RangeToExpr &expr)
{
stream << "..";
visit (expr.get_to_expr ());
}
void
Dump::visit (RangeFullExpr &)
{
stream << "..";
}
void
Dump::visit (RangeFromToInclExpr &expr)
{
visit (expr.get_from_expr ());
stream << "..=";
visit (expr.get_to_expr ());
}
void
Dump::visit (RangeToInclExpr &expr)
{
stream << "..=";
visit (expr.get_to_expr ());
}
void
Dump::visit (ReturnExpr &)
{}
void
Dump::visit (UnsafeBlockExpr &)
{}
void
Dump::visit (LoopExpr &)
{}
void
Dump::visit (WhileLoopExpr &)
{}
void
Dump::visit (WhileLetLoopExpr &)
{}
void
Dump::visit (ForLoopExpr &)
{}
void
Dump::visit (IfExpr &expr)
{
stream << "if ";
visit (expr.get_condition_expr ());
stream << " ";
visit (expr.get_if_block ());
}
void
Dump::visit (IfExprConseqElse &expr)
{
stream << "if ";
visit (expr.get_condition_expr ());
stream << " ";
visit (expr.get_if_block ());
stream << indentation << "else ";
visit (expr.get_else_block ());
}
void
Dump::visit (IfExprConseqIf &expr)
{
stream << "if ";
visit (expr.get_condition_expr ());
stream << " ";
visit (expr.get_if_block ());
stream << indentation << "else ";
// The "if" part of the "else if" is printed by the next visitor
visit (expr.get_conseq_if_expr ());
}
void
Dump::visit (IfExprConseqIfLet &)
{}
void
Dump::visit (IfLetExpr &)
{}
void
Dump::visit (IfLetExprConseqElse &)
{}
void
Dump::visit (IfLetExprConseqIf &)
{}
void
Dump::visit (IfLetExprConseqIfLet &)
{}
void
Dump::visit (MatchExpr &)
{}
void
Dump::visit (AwaitExpr &)
{}
void
Dump::visit (AsyncBlockExpr &)
{}
// rust-item.h
void
Dump::visit (TypeParam ¶m)
{
// Syntax:
// IDENTIFIER( : TypeParamBounds? )? ( = Type )?
// TypeParamBounds :
// TypeParamBound ( + TypeParamBound )* +?
stream << param.get_type_representation ();
if (param.has_type_param_bounds ())
{
stream << ": ";
visit_items_joined_by_separator (param.get_type_param_bounds (), " + ");
}
if (param.has_type ())
{
stream << " = ";
visit (param.get_type ());
}
}
void
Dump::visit (WhereClause &rule)
{
// Syntax:
// where ( WhereClauseItem , )* WhereClauseItem ?
// WhereClauseItem :
// LifetimeWhereClauseItem
// | TypeBoundWhereClauseItem
stream << " where\n";
indentation.increment ();
visit_items_as_lines (rule.get_items (), ",");
indentation.decrement ();
}
void
Dump::visit (LifetimeWhereClauseItem &item)
{
// Syntax:
// Lifetime : LifetimeBounds
// LifetimeBounds :
// ( Lifetime + )* Lifetime?
visit (item.get_lifetime ());
stream << ": ";
visit_items_joined_by_separator (item.get_lifetime_bounds (), " + ");
}
void
Dump::visit (TypeBoundWhereClauseItem &item)
{
// Syntax:
// ForLifetimes? Type : TypeParamBounds?
// TypeParamBounds :
// TypeParamBound ( + TypeParamBound )* +?
// TypeParamBound :
// Lifetime | TraitBound
if (item.has_for_lifetimes ())
visit (item.get_for_lifetimes ());
visit (item.get_type ());
stream << ": ";
visit_items_joined_by_separator (item.get_type_param_bounds (), " + ");
}
void
Dump::visit (Method &method)
{
visit (method.get_visibility ());
stream << "fn " << method.get_method_name () << '(';
stream << method.get_self_param ().as_string ();
if (!method.get_function_params ().empty ())
{
stream << ", ";
visit_items_joined_by_separator (method.get_function_params (), ", ");
}
stream << ") ";
if (method.has_return_type ())
{
stream << "-> ";
visit (method.get_return_type ());
stream << " ";
}
auto &block = method.get_definition ();
if (!block)
stream << ';';
else
visit (block);
stream << '\n';
}
void
Dump::visit (Module &module)
{
// Syntax:
// mod IDENTIFIER ;
// | mod IDENTIFIER {
// InnerAttribute*
// Item*
// }
visit (module.get_visibility ());
stream << "mod " << module.get_name ();
if (module.get_kind () == Module::UNLOADED)
{
stream << ";\n";
}
else /* Module::LOADED */
{
stream << " {\n";
indentation.increment ();
visit_items_as_lines (module.get_inner_attrs ());
visit_items_as_lines (module.get_items ());
indentation.decrement ();
stream << indentation << "}\n";
}
}
void
Dump::visit (ExternCrate &)
{}
void
Dump::visit (UseTreeGlob &)
{}
void
Dump::visit (UseTreeList &)
{}
void
Dump::visit (UseTreeRebind &)
{}
void
Dump::visit (UseDeclaration &)
{}
void
Dump::visit (Function &function)
{
// Syntax:
// FunctionQualifiers fn IDENTIFIER GenericParams?
// ( FunctionParameters? )
// FunctionReturnType? WhereClause?
// ( BlockExpression | ; )
visit (function.get_visibility ());
stream << "fn " << function.get_function_name ();
if (function.has_generics ())
visit (function.get_generic_params ());
stream << '(';
visit_items_joined_by_separator (function.get_function_params ());
stream << ") ";
if (function.has_return_type ())
{
stream << "-> ";
visit (function.get_return_type ());
stream << " ";
}
if (function.has_where_clause ())
visit (function.get_where_clause ());
auto &block = function.get_definition ();
if (!block)
stream << ';';
else
visit (block);
stream << '\n';
}
void
Dump::visit (TypeAlias &type_alias)
{
// Syntax:
// Visibility? type IDENTIFIER GenericParams? WhereClause? = Type;
// Note: Associated types are handled by `AST::TraitItemType`.
if (type_alias.has_visibility ())
visit (type_alias.get_visibility ());
stream << "type " << type_alias.get_new_type_name ();
if (type_alias.has_generics ())
visit (type_alias.get_generic_params ());
if (type_alias.has_where_clause ())
visit (type_alias.get_where_clause ());
stream << " = ";
visit (type_alias.get_type_aliased ());
}
void
Dump::visit (StructStruct &struct_item)
{
stream << "struct " << struct_item.get_identifier ();
if (struct_item.has_generics ())
visit (struct_item.get_generic_params ());
if (struct_item.has_where_clause ())
visit (struct_item.get_where_clause ());
if (struct_item.is_unit_struct ())
stream << ";\n";
else
visit_items_as_block (struct_item.get_fields (), ",");
}
void
Dump::visit (TupleStruct &tuple_struct)
{
stream << "struct " << tuple_struct.get_identifier ();
if (tuple_struct.has_generics ())
visit (tuple_struct.get_generic_params ());
if (tuple_struct.has_where_clause ())
visit (tuple_struct.get_where_clause ());
stream << '(';
visit_items_joined_by_separator (tuple_struct.get_fields (), ", ");
stream << ");\n";
}
void
Dump::visit (EnumItem &item)
{
stream << item.get_identifier ();
}
void
Dump::visit (EnumItemTuple &item)
{
stream << item.get_identifier () << '(';
visit_items_joined_by_separator (item.get_tuple_fields (), ", ");
stream << ')';
}
void
Dump::visit (EnumItemStruct &item)
{
stream << item.get_identifier ();
visit_items_as_block (item.get_struct_fields (), ",");
}
void
Dump::visit (EnumItemDiscriminant &item)
{
stream << item.get_identifier () << " = ";
visit (item.get_expr ());
}
void
Dump::visit (Enum &enum_item)
{
stream << "enum " << enum_item.get_identifier ();
if (enum_item.has_generics ())
visit (enum_item.get_generic_params ());
if (enum_item.has_where_clause ())
visit (enum_item.get_where_clause ());
visit_items_as_block (enum_item.get_variants (), ",");
}
void
Dump::visit (Union &union_item)
{
stream << "union " << union_item.get_identifier ();
if (union_item.has_generics ())
visit (union_item.get_generic_params ());
if (union_item.has_where_clause ())
visit (union_item.get_where_clause ());
visit_items_as_block (union_item.get_variants (), ",");
}
void
Dump::visit (ConstantItem &)
{}
void
Dump::visit (StaticItem &)
{}
void
Dump::visit_function_common (std::unique_ptr &return_type,
std::unique_ptr &block)
{
// FIXME: This should format the ` fn ( [args] )` as well
if (return_type)
{
stream << "-> ";
visit (return_type);
}
if (block)
{
if (return_type)
{
stream << ' ';
visit (block);
}
}
else
stream << ";\n";
}
void
Dump::visit (TraitItemFunc &item)
{
auto func = item.get_trait_function_decl ();
stream << "fn " << func.get_identifier () << '(';
visit_items_joined_by_separator (func.get_function_params ());
stream << ") ";
visit_function_common (func.get_return_type (), item.get_definition ());
}
void
Dump::visit (TraitItemMethod &item)
{
auto method = item.get_trait_method_decl ();
// FIXME: Can we have visibility here?
// emit_visibility (method.get_visibility ());
stream << "fn " << method.get_identifier () << '(';
stream << method.get_self_param ().as_string ();
if (!method.get_function_params ().empty ())
{
stream << ", ";
visit_items_joined_by_separator (method.get_function_params (), ", ");
}
stream << ") ";
visit_function_common (method.get_return_type (), item.get_definition ());
}
void
Dump::visit (TraitItemConst &item)
{
stream << indentation << "const " << item.get_identifier () << ": ";
visit (item.get_type ());
stream << ";\n";
}
void
Dump::visit (TraitItemType &item)
{
stream << indentation << "type " << item.get_identifier () << ";\n";
}
void
Dump::visit (Trait &trait)
{
for (auto &attr : trait.get_outer_attrs ())
{
visit (attr);
stream << "\n" << indentation;
}
visit (trait.get_visibility ());
stream << "trait " << trait.get_identifier ();
// Traits actually have an implicit Self thrown at the start, so we must
// expect the number of generic params to be > 1
if (trait.get_generic_params ().size () > 1)
{
stream << "<";
visit_items_joined_by_separator (trait.get_generic_params (), ", ", 1);
stream << ">";
}
visit_items_as_block (trait.get_trait_items (), "");
}
void
Dump::visit (InherentImpl &impl)
{
stream << "impl ";
// FIXME: Handle generics
visit (impl.get_type ());
if (impl.has_where_clause ())
visit (impl.get_where_clause ());
// FIXME: Handle inner attributes
visit_items_as_block (impl.get_impl_items (), "");
}
void
Dump::visit (TraitImpl &impl)
{
stream << "impl ";
visit (impl.get_trait_path ());
stream << " for ";
visit (impl.get_type ());
stream << " {\n";
indentation.increment ();
for (auto &item : impl.get_impl_items ())
{
stream << indentation;
visit (item);
}
indentation.decrement ();
stream << "\n}\n";
}
void
Dump::visit (ExternalStaticItem &)
{}
void
Dump::visit (ExternalFunctionItem &function)
{
visit (function.get_visibility ());
stream << "fn " << function.get_identifier () << '(';
visit_items_joined_by_separator (function.get_function_params ());
stream << ')';
if (function.has_return_type ())
{
stream << "-> ";
visit (function.get_return_type ());
}
}
void
Dump::visit (ExternBlock &block)
{
stream << "extern ";
if (block.has_abi ())
stream << "\"" << block.get_abi () << "\" ";
visit_items_as_block (block.get_extern_items (), ";");
}
static std::pair
get_delimiters (DelimType delim)
{
auto start_delim = '\0';
auto end_delim = '\0';
switch (delim)
{
case PARENS:
start_delim = '(';
end_delim = ')';
break;
case SQUARE:
start_delim = '[';
end_delim = ']';
break;
case CURLY:
start_delim = '{';
end_delim = '}';
break;
}
return {start_delim, end_delim};
}
void
Dump::visit (MacroMatchFragment &match)
{
stream << '$' << match.get_ident () << ':'
<< match.get_frag_spec ().as_string ();
}
void
Dump::visit (MacroMatchRepetition &repetition)
{
stream << "$(";
visit_items_joined_by_separator (repetition.get_matches (), " ");
auto op_char = '\0';
switch (repetition.get_op ())
{
case MacroMatchRepetition::ANY:
op_char = '*';
break;
case MacroMatchRepetition::ONE_OR_MORE:
op_char = '+';
break;
case MacroMatchRepetition::ZERO_OR_ONE:
op_char = '?';
break;
case MacroMatchRepetition::NONE:
break;
}
stream << ')';
if (repetition.has_sep ())
stream << repetition.get_sep ()->as_string ();
stream << op_char;
}
void
Dump::visit (MacroMatcher &matcher)
{
auto delimiters = get_delimiters (matcher.get_delim_type ());
stream << delimiters.first;
visit_items_joined_by_separator (matcher.get_matches (), " ");
stream << delimiters.second;
}
void
Dump::visit (MacroRule &rule)
{
visit (rule.get_matcher ());
stream << " => ";
visit (rule.get_transcriber ().get_token_tree ());
stream << ";";
}
void
Dump::visit (MacroRulesDefinition &rules_def)
{
for (auto &outer_attr : rules_def.get_outer_attrs ())
visit (outer_attr);
stream << "macro_rules! " << rules_def.get_rule_name ();
visit_items_as_block (rules_def.get_rules (), ";");
}
void
Dump::visit (MacroInvocation &)
{}
void
Dump::visit (MetaItemPath &)
{}
void
Dump::visit (MetaItemSeq &)
{}
void
Dump::visit (MetaWord &)
{}
void
Dump::visit (MetaNameValueStr &)
{}
void
Dump::visit (MetaListPaths &)
{}
void
Dump::visit (MetaListNameValueStr &)
{}
// rust-pattern.h
void
Dump::visit (LiteralPattern &)
{}
void
Dump::visit (IdentifierPattern &pattern)
{
stream << pattern.get_ident ();
}
void
Dump::visit (WildcardPattern &)
{}
// void Dump::visit(RangePatternBound& ){}
void
Dump::visit (RangePatternBoundLiteral &)
{}
void
Dump::visit (RangePatternBoundPath &)
{}
void
Dump::visit (RangePatternBoundQualPath &)
{}
void
Dump::visit (RangePattern &)
{}
void
Dump::visit (ReferencePattern &)
{}
// void Dump::visit(StructPatternField& ){}
void
Dump::visit (StructPatternFieldTuplePat &)
{}
void
Dump::visit (StructPatternFieldIdentPat &)
{}
void
Dump::visit (StructPatternFieldIdent &)
{}
void
Dump::visit (StructPattern &)
{}
// void Dump::visit(TupleStructItems& ){}
void
Dump::visit (TupleStructItemsNoRange &)
{}
void
Dump::visit (TupleStructItemsRange &)
{}
void
Dump::visit (TupleStructPattern &)
{}
// void Dump::visit(TuplePatternItems& ){}
void
Dump::visit (TuplePatternItemsMultiple &)
{}
void
Dump::visit (TuplePatternItemsRanged &)
{}
void
Dump::visit (TuplePattern &)
{}
void
Dump::visit (GroupedPattern &)
{}
void
Dump::visit (SlicePattern &)
{}
void
Dump::visit (AltPattern &)
{}
// rust-stmt.h
void
Dump::visit (EmptyStmt &)
{}
void
Dump::visit (LetStmt &stmt)
{
stream << "let ";
auto &pattern = stmt.get_pattern ();
if (pattern)
visit (pattern);
if (stmt.has_type ())
{
stream << ": ";
visit (stmt.get_type ());
}
if (stmt.has_init_expr ())
{
stream << " = ";
visit (stmt.get_init_expr ());
}
}
void
Dump::visit (ExprStmtWithoutBlock &stmt)
{
visit (stmt.get_expr ());
}
void
Dump::visit (ExprStmtWithBlock &stmt)
{
visit (stmt.get_expr ());
}
// rust-type.h
void
Dump::visit (TraitBound &bound)
{
// Syntax:
// ?? ForLifetimes? TypePath
// | ( ?? ForLifetimes? TypePath )
if (bound.has_opening_question_mark ())
stream << "? ";
if (bound.has_for_lifetimes ())
visit (bound.get_for_lifetimes ());
visit (bound.get_type_path ());
}
void
Dump::visit (ImplTraitType &type)
{
// Syntax:
// impl TypeParamBounds
// TypeParamBounds :
// TypeParamBound ( + TypeParamBound )* +?
stream << "impl ";
visit_items_joined_by_separator (type.get_type_param_bounds (), " + ");
}
void
Dump::visit (TraitObjectType &type)
{
// Syntax:
// dyn? TypeParamBounds
// TypeParamBounds :
// TypeParamBound ( + TypeParamBound )* +?
if (type.is_dyn ())
stream << "dyn ";
visit_items_joined_by_separator (type.get_type_param_bounds (), " + ");
}
void
Dump::visit (ParenthesisedType &type)
{
// Syntax:
// ( Type )
stream << "(";
visit (type.get_type_in_parens ());
stream << ")";
}
void
Dump::visit (ImplTraitTypeOneBound &type)
{
// Syntax:
// impl TraitBound
stream << "impl ";
visit (type.get_trait_bound ());
}
void
Dump::visit (TraitObjectTypeOneBound &type)
{
// Syntax:
// dyn? TraitBound
if (type.is_dyn ())
stream << "dyn ";
visit (type.get_trait_bound ());
}
void
Dump::visit (TupleType &type)
{
// Syntax:
// ( )
// | ( ( Type , )+ Type? )
stream << '(';
visit_items_joined_by_separator (type.get_elems (), ", ");
stream << ')';
}
void
Dump::visit (NeverType &)
{
// Syntax:
// !
stream << '!';
}
void
Dump::visit (RawPointerType &type)
{
// Syntax:
// * ( mut | const ) TypeNoBounds
if (type.get_pointer_type () == RawPointerType::MUT)
stream << "*mut ";
else /* RawPointerType::CONST */
stream << "*const ";
visit (type.get_type_pointed_to ());
}
void
Dump::visit (ReferenceType &type)
{
// Syntax:
// & Lifetime? mut? TypeNoBounds
stream << '&';
if (type.has_lifetime ())
{
visit (type.get_lifetime ());
stream << ' ';
}
if (type.get_has_mut ())
stream << "mut ";
visit (type.get_type_referenced ());
}
void
Dump::visit (ArrayType &type)
{
// Syntax:
// [ Type ; Expression ]
stream << '[';
visit (type.get_elem_type ());
stream << "; ";
visit (type.get_size_expr ());
stream << ']';
}
void
Dump::visit (SliceType &type)
{
// Syntax:
// [ Type ]
stream << '[';
visit (type.get_elem_type ());
stream << ']';
}
void
Dump::visit (InferredType &)
{
// Syntax:
// _
stream << "_";
}
void
Dump::visit (BareFunctionType &type)
{
// Syntax:
// ForLifetimes? FunctionTypeQualifiers fn
// ( FunctionParametersMaybeNamedVariadic? ) BareFunctionReturnType?
//
// BareFunctionReturnType:
// -> TypeNoBounds
//
// FunctionParametersMaybeNamedVariadic :
// MaybeNamedFunctionParameters | MaybeNamedFunctionParametersVariadic
//
// MaybeNamedFunctionParameters :
// MaybeNamedParam ( , MaybeNamedParam )* ,?
//
// MaybeNamedFunctionParametersVariadic :
// ( MaybeNamedParam , )* MaybeNamedParam , OuterAttribute* ...
if (type.has_for_lifetimes ())
visit (type.get_for_lifetimes ());
visit (type.get_function_qualifiers ());
stream << "fn (";
visit_items_joined_by_separator (type.get_function_params (), ", ");
if (type.is_variadic ())
{
stream << ", ";
visit_items_joined_by_separator (type.get_variadic_attr (), " ");
stream << "...";
}
stream << ')';
if (type.has_return_type ())
{
stream << " -> ";
visit (type.get_return_type ());
}
}
} // namespace AST
} // namespace Rust