// 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