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