// Copyright (C) 2020-2025 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-hir-dump.h" #include "rust-abi.h" #include "rust-hir-item.h" #include "rust-hir-path.h" #include "rust-hir-type.h" #include "rust-hir.h" #include #include "rust-attribute-values.h" #include "tree/rust-hir-expr.h" namespace Rust { namespace HIR { // Dump Format for HIR // // SomeHIRNode [ // field: ... // field: ... // field: ... // ] // // When a field is a collection of other HIR objects: // field { // SomeHIRNode [ ... ] // SomeOtherHIRNode [ ... ] // } // // If a field is an empty collection: // field: empty // // If a field is optional and is currently not holding anything: // field: none std::string Dump::delims[2][2] = { {std::string ("{"), std::string ("}")}, {std::string ("["), std::string ("]")}, }; static std::string BoundPolarityString (BoundPolarity polarity) { switch (polarity) { case RegularBound: return "regular"; case NegativeBound: return "negative"; case AntiBound: return "anti"; } return "unknown"; } /** * Static member used to dump HIR from the debugger to stderr. * * @param v The HIR node to dump */ void Dump::debug (FullVisitable &v) { Dump dump (std::cerr); v.accept_vis (dump); } void Dump::go (HIR::Crate &e) { begin ("Crate"); do_inner_attrs (e); do_mappings (e.get_mappings ()); visit_collection ("items", e.get_items ()); end ("Crate"); } Dump::Dump (std::ostream &stream) : stream (stream) {} /** * Writes TEXT with a final newline if ENDLINE is true. * If TEXT is starting the current line, its first line is indented. * If TEXT is multiline, all followning lines are also indented. * * @param text Text to emit * @param endline If true, newline is emitted after text */ void Dump::put (std::string text, bool endline) { if (beg_of_line) { stream << indentation; beg_of_line = false; } // keep multiline string indented std::string::size_type pos = 0; std::string::size_type prev = 0; auto first = true; while ((pos = text.find ('\n', prev)) != std::string::npos) { if (!first) stream << std::endl << indentation; first = false; stream << text.substr (prev, pos - prev); prev = pos + 1; } if (first) stream << text; if (endline) { stream << std::endl; beg_of_line = endline; } } /** * Called when starting to emit info for an HIR node. * Emits NAME and an opening delimiter denoted by D. * * @param name Name of HIR node * @param d Delimiter */ void Dump::begin (std::string name, enum delim d) { if (!beg_of_line) put (""); put (name + " " + delims[d][0], true); indentation.increment (); } /** * Called when ending the dump info for an HIR node. Emits a C++-style * comment with NAME and a closing delimiter denoted by D. * * @param name Name of HIR node * @param d Delimiter */ void Dump::end (std::string name, enum delim d) { indentation.decrement (); if (!beg_of_line) stream << std::endl; put (delims[d][1] + " // " + name); } /** * Called when starting to emit info for a field within an HIR node. * Emits NAME and an opening curly brace * * @param name HIR field name */ void Dump::begin_field (std::string name) { begin (name, CURLY); } /** * Called when ending the dump info for a field within an HIR node. * Emits a C++-style comment with NAME and a closing curly brace * * @param name HIR field name */ void Dump::end_field (std::string name) { end (name, CURLY); } /** * Emits a single field/value pair denoted by NAME and TEXT. * * @param name Field name * @param text Field value */ void Dump::put_field (std::string name, std::string text) { put (name + ": ", false); indentation.increment (); put (text); indentation.decrement (); } /** * Recursively visits an HIR field NAME with value possibly pointed to by * PTR, if PTR is not null. If PTR is null, simply emits FIELD_NAME/NULL pair. * * @param name Field name * @param ptr Pointer to field's value */ template void Dump::visit_field (std::string name, std::unique_ptr &ptr) { if (ptr) visit_field (name, *ptr); else put_field (name, "NULL"); } /** * Recursively visits an HIR field NAME with value V. * * @param name Field name * @param v Field value */ void Dump::visit_field (std::string name, FullVisitable &v) { put (name + ": ", false); indentation.increment (); v.accept_vis (*this); indentation.decrement (); } /** * Recursively visits a collection VEC of HIR node for field NAME. * If VEC is empty, simply emits the NAME/empty pair. * * @param name Field name * @param vec Field value as a vector */ template void Dump::visit_collection (std::string name, std::vector> &vec) { if (vec.empty ()) { put_field (name, "empty"); return; } begin_field (name); for (const auto &elt : vec) elt->accept_vis (*this); end_field (name); } /** * Recursively visits a collection VEC of HIR node for field NAME. * If VEC is empty, simply emits the NAME/empty pair. * * @param name Field name * @param vec Field value as a vector */ template void Dump::visit_collection (std::string name, std::vector &vec) { if (vec.empty ()) { put_field (name, "empty"); return; } begin_field (name); for (auto &elt : vec) elt.accept_vis (*this); end_field (name); } void Dump::do_traititem (TraitItem &e) { do_mappings (e.get_mappings ()); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); } void Dump::do_vis_item (VisItem &e) { do_item (e); std::string str = "none"; if (e.has_visibility ()) str = e.get_visibility ().as_string (); put_field ("visibility", str); } void Dump::do_functionparam (FunctionParam &e) { begin ("FunctionParam"); do_mappings (e.get_mappings ()); visit_field ("param_name", e.get_param_name ()); visit_field ("type", e.get_type ()); end ("FunctionParam"); } void Dump::do_pathpattern (PathPattern &e) { std::string str = ""; for (const auto &segment : e.get_segments ()) str += segment.as_string () + ", "; put_field ("segments", str); } void Dump::do_structexprstruct (StructExprStruct &e) { do_expr (e); // StructExpr visit_field ("struct_name", e.get_struct_name ()); // StructExprStruct do_mappings (e.get_mappings ()); do_inner_attrs (e); } void Dump::do_ifexpr (IfExpr &e) { do_expr (e); visit_field ("condition", e.get_if_condition ()); visit_field ("if_block", e.get_if_block ()); } void Dump::do_expr (Expr &e) { do_mappings (e.get_mappings ()); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); } void Dump::do_matcharm (MatchArm &e) { begin ("MatchArm"); // FIXME Can't remember how to handle that. Let's see later. // do_outer_attrs(e); visit_collection ("match_arm_patterns", e.get_patterns ()); visit_field ("guard_expr", e.get_guard_expr ()); end ("MatchArm"); } void Dump::do_matchcase (HIR::MatchCase &e) { begin ("MatchCase"); begin_field ("arm"); do_matcharm (e.get_arm ()); end_field ("arm"); visit_field ("expr", e.get_expr ()); end ("MatchCase"); } void Dump::do_pathexpr (PathExpr &e) { do_expr (e); } void Dump::do_typepathsegment (TypePathSegment &e) { do_mappings (e.get_mappings ()); put_field ("ident_segment", e.get_ident_segment ().as_string ()); } void Dump::do_typepathfunction (TypePathFunction &e) { visit_collection ("params", e.get_params ()); visit_field ("return_type", e.get_return_type ()); } void Dump::do_qualifiedpathtype (QualifiedPathType &e) { do_mappings (e.get_mappings ()); visit_field ("type", e.get_type ()); visit_field ("trait", e.get_trait ()); } void Dump::do_operatorexpr (OperatorExpr &e) { visit_field ("main_or_left_expr", e.get_expr ()); } void Dump::do_mappings (const Analysis::NodeMapping &mappings) { put ("mapping: ", false); put (mappings.as_string ()); } void Dump::do_inner_attrs (WithInnerAttrs &e) { auto attrs = e.get_inner_attrs (); if (attrs.empty ()) { put_field ("inner_attrs", "empty"); return; } begin_field ("inner_attrs"); for (auto &elt : attrs) visit (elt); end_field ("inner_attrs"); } void Dump::do_outer_attrs (std::vector &attrs) { if (attrs.empty ()) put_field ("outer_attributes", "empty"); else { begin_field ("outer_attributes"); for (const auto &attr : attrs) put (attr.as_string ()); end_field ("outer_attributes"); } } void Dump::do_baseloopexpr (BaseLoopExpr &e) { do_expr (e); if (!e.has_loop_label ()) put_field ("label", "none"); else put_field ("label", e.get_loop_label ().as_string ()); visit_field ("loop_block", e.get_loop_block ()); } void Dump::do_struct (Struct &e) { do_vis_item (e); put_field ("struct_name", e.get_identifier ().as_string ()); visit_collection ("generic_params", e.get_generic_params ()); if (!e.has_where_clause ()) put_field ("where_clause", "none"); else put_field ("where clause", e.get_where_clause ().as_string ()); } void Dump::do_enumitem (EnumItem &e) { do_item (e); put_field ("variant_name", e.get_identifier ().as_string ()); std::string str; switch (e.get_enum_item_kind ()) { case EnumItem::EnumItemKind::Named: str = "[Named variant]"; break; case EnumItem::EnumItemKind::Tuple: str = "[Tuple variant]"; break; case EnumItem::EnumItemKind::Struct: str = "[Struct variant]"; break; case EnumItem::EnumItemKind::Discriminant: str = "[Discriminant variant]"; break; } put_field ("item_kind", str); } void Dump::do_traitfunctiondecl (TraitFunctionDecl &e) { begin ("TraitFunctionDecl"); put_field ("qualifiers", e.get_qualifiers ().as_string ()); put_field ("function_name", e.get_function_name ().as_string ()); visit_collection ("generic_params", e.get_generic_params ()); if (!e.get_function_params ().empty ()) { begin_field ("function_params"); for (auto &item : e.get_function_params ()) do_functionparam (item); end_field ("function_params"); } else put_field ("function_params", "empty"); visit_field ("return_type", e.get_return_type ()); if (e.has_where_clause ()) put_field ("where_clause", e.get_where_clause ().as_string ()); else put_field ("where_clause", "none"); put_field ("self", e.get_self ().as_string ()); end ("TraitFunctionDecl"); } void Dump::do_externalitem (ExternalItem &e) { do_mappings (e.get_mappings ()); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); std::string str = "none"; if (e.has_visibility ()) str = e.get_visibility ().as_string (); put_field ("visibility", str); put_field ("item_name", e.get_item_name ().as_string ()); } void Dump::do_namefunctionparam (NamedFunctionParam &e) { begin ("NamedFunctionParam"); do_mappings (e.get_mappings ()); put_field ("name", e.get_param_name ().as_string ()); visit_field ("type", e.get_type ()); end ("NamedFunctionParam"); } void Dump::do_stmt (Stmt &e) { do_mappings (e.get_mappings ()); } void Dump::do_type (Type &e) { do_mappings (e.get_mappings ()); } void Dump::do_item (Item &e) { do_stmt (e); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); } void Dump::do_tuplefield (TupleField &e) { do_mappings (e.get_mappings ()); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); std::string str = "none"; if (e.has_visibility ()) str = e.get_visibility ().as_string (); put_field ("visibility", str); visit_field ("field_type", e.get_field_type ()); } void Dump::do_structfield (StructField &e) { do_mappings (e.get_mappings ()); auto oa = e.get_outer_attrs (); do_outer_attrs (oa); std::string str = "none"; if (e.has_visibility ()) str = e.get_visibility ().as_string (); put_field ("visibility", str); put_field ("field_name", e.get_field_name ().as_string ()); visit_field ("field_type", e.get_field_type ()); } void Dump::do_genericargs (GenericArgs &e) { visit_collection ("lifetime_args", e.get_lifetime_args ()); visit_collection ("type_args", e.get_type_args ()); if (e.get_const_args ().empty ()) { put_field ("binding_args", "empty"); } else { begin_field ("const_args"); for (auto &arg : e.get_const_args ()) { begin ("ConstGenericArg"); visit_field ("expression", arg.get_expression ()); end ("ConstGenericArg"); } end_field ("const_args"); } if (e.get_binding_args ().empty ()) { put_field ("binding_args", "empty"); } else { begin_field ("binding_args"); for (auto &arg : e.get_binding_args ()) { begin ("GenericArgsBinding"); put_field ("identfier", arg.get_identifier ().as_string ()); visit_field ("type", arg.get_type ()); end ("GenericArgsBinding"); } end_field ("binding_args"); } } void Dump::do_maybenamedparam (MaybeNamedParam &e) { visit_field ("param_type", e.get_type ()); put_field ("param_kind", enum_to_str (e.get_param_kind ())); put_field ("name", e.get_name ().as_string ()); } // All visit methods void Dump::visit (AST::Attribute &attribute) { // Special, no begin/end as this is called by do_inner_attrs. put_field (Values::Attributes::PATH, attribute.get_path ().as_string ()); std::string str = "none"; if (attribute.has_attr_input ()) str = attribute.get_attr_input ().as_string (); put_field ("attr_input", str); } void Dump::visit (Lifetime &e) { do_mappings (e.get_mappings ()); std::string type; std::string name = e.get_name (); switch (e.get_lifetime_type ()) { case AST::Lifetime::LifetimeType::NAMED: type = "[NAMED]"; break; case AST::Lifetime::LifetimeType::STATIC: type = "[STATIC]"; name += " (not applicable for type)"; break; case AST::Lifetime::LifetimeType::WILDCARD: type = "[WILDCARD]"; name += " (not applicable for type)"; break; default: rust_assert (false); break; } put_field ("lifetime_type", type); put_field ("lifetime_name", name); } void Dump::visit (LifetimeParam &lifetimeparam) { begin ("Lifetimeparam"); put (lifetimeparam.as_string ()); end ("Lifetimeparam"); } void Dump::visit (PathInExpression &e) { begin ("PathInExpression"); do_pathpattern (e); do_pathexpr (e); put_field ("has_opening_scope_resolution", std::to_string (e.opening_scope_resolution ())); end ("PathInExpression"); } void Dump::visit (TypePathSegment &e) { begin ("TypePathSegment"); do_typepathsegment (e); end ("TypePathSegment"); } void Dump::visit (TypePathSegmentGeneric &e) { begin ("TypePathSegmentGeneric"); do_typepathsegment (e); if (e.has_generic_args ()) { begin_field ("generic_args"); begin ("GenericArgs"); do_genericargs (e.get_generic_args ()); end ("GenericArgs"); end_field ("generic_args"); } else { put_field ("generic_args", "empty"); } end ("TypePathSegmentGeneric"); } void Dump::visit (TypePathSegmentFunction &e) { begin ("TypePathSegmentFunction"); do_typepathsegment (e); begin ("function_path"); do_typepathfunction (e.get_function_path ()); end ("function_path"); end ("TypePathSegmentFunction"); } void Dump::visit (TypePath &e) { begin ("TypePath"); put_field ("has_opening_scope_resolution", std::to_string (e.has_opening_scope_resolution_op ())); visit_collection ("segments", e.get_segments ()); end ("TypePath"); } void Dump::visit (QualifiedPathInExpression &e) { begin ("QualifiedPathInExpression"); do_pathpattern (e); do_expr (e); begin_field ("path_type"); begin ("QualifiedPathType"); do_qualifiedpathtype (e.get_path_type ()); end ("QualifiedPathType"); end_field ("path_type"); end ("QualifiedPathInExpression"); } void Dump::visit (QualifiedPathInType &e) { begin ("QualifiedPathInType"); begin_field ("path_type"); do_qualifiedpathtype (e.get_path_type ()); end_field ("path_type"); begin_field ("associated_segment"); do_typepathsegment (e.get_associated_segment ()); end_field ("associated_segment"); visit_collection ("segments", e.get_segments ()); end ("QualifiedPathInType"); } void Dump::visit (LiteralExpr &e) { begin ("LiteralExpr"); do_expr (e); put_field ("literal", e.get_literal ().as_string ()); end ("LiteralExpr"); } void Dump::visit (BorrowExpr &e) { begin ("BorrowExpr"); do_operatorexpr (e); put_field ("mut", enum_to_str (e.get_mut ())); end ("BorrowExpr"); } void Dump::visit (DereferenceExpr &e) { begin ("DereferenceExpr"); do_operatorexpr (e); end ("DereferenceExpr"); } void Dump::visit (ErrorPropagationExpr &e) { begin ("ErrorPropagationExpr"); do_operatorexpr (e); end ("ErrorPropagationExpr"); } void Dump::visit (NegationExpr &e) { begin ("NegationExpr"); do_operatorexpr (e); std::string str; switch (e.get_expr_type ()) { case NegationOperator::NEGATE: str = "[NEGATE]"; break; case NegationOperator::NOT: str = "[NOT]"; break; default: rust_assert (false); } put_field ("expr_type", str); end ("NegationExpr"); } void Dump::visit (ArithmeticOrLogicalExpr &e) { begin ("ArithmeticOrLogicalExpr"); std::string str; // which operator switch (e.get_expr_type ()) { case ArithmeticOrLogicalOperator::ADD: str = "[ADD]"; break; case ArithmeticOrLogicalOperator::SUBTRACT: str = "SUBTRACT"; break; case ArithmeticOrLogicalOperator::MULTIPLY: str = "MULTIPLY"; break; case ArithmeticOrLogicalOperator::DIVIDE: str = "DIVIDE"; break; case ArithmeticOrLogicalOperator::MODULUS: str = "MODULUS"; break; case ArithmeticOrLogicalOperator::BITWISE_AND: str = "BITWISE"; break; case ArithmeticOrLogicalOperator::BITWISE_OR: str = "BITWISE"; break; case ArithmeticOrLogicalOperator::BITWISE_XOR: str = "BITWISE"; break; case ArithmeticOrLogicalOperator::LEFT_SHIFT: str = "