//! `ItemTree` debug printer. use std::fmt::{self, Write}; use crate::{attr::RawAttrs, visibility::RawVisibility}; use super::*; pub(super) fn print_item_tree(tree: &ItemTree) -> String { let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { p.print_attrs(attrs, true); } p.blank(); for item in tree.top_level_items() { p.print_mod_item(*item); } let mut s = p.buf.trim_end_matches('\n').to_string(); s.push('\n'); s } macro_rules! w { ($dst:expr, $($arg:tt)*) => { drop(write!($dst, $($arg)*)) }; } macro_rules! wln { ($dst:expr) => { drop(writeln!($dst)) }; ($dst:expr, $($arg:tt)*) => { drop(writeln!($dst, $($arg)*)) }; } struct Printer<'a> { tree: &'a ItemTree, buf: String, indent_level: usize, needs_indent: bool, } impl<'a> Printer<'a> { fn indented(&mut self, f: impl FnOnce(&mut Self)) { self.indent_level += 1; wln!(self); f(self); self.indent_level -= 1; self.buf = self.buf.trim_end_matches('\n').to_string(); } /// Ensures that a blank line is output before the next text. fn blank(&mut self) { let mut iter = self.buf.chars().rev().fuse(); match (iter.next(), iter.next()) { (Some('\n'), Some('\n')) | (Some('\n'), None) | (None, None) => {} (Some('\n'), Some(_)) => { self.buf.push('\n'); } (Some(_), _) => { self.buf.push('\n'); self.buf.push('\n'); } (None, Some(_)) => unreachable!(), } } fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { let inner = if inner { "!" } else { "" }; for attr in &**attrs { wln!( self, "#{}[{}{}] // {:?}", inner, attr.path, attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), attr.id, ); } } fn print_attrs_of(&mut self, of: impl Into) { if let Some(attrs) = self.tree.attrs.get(&of.into()) { self.print_attrs(attrs, false); } } fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { RawVisibility::Module(path) => w!(self, "pub({}) ", path), RawVisibility::Public => w!(self, "pub "), }; } fn print_fields(&mut self, fields: &Fields) { match fields { Fields::Record(fields) => { w!(self, " {{"); self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); w!(this, "{}: ", name); this.print_type_ref(type_ref); wln!(this, ","); } }); w!(self, "}}"); } Fields::Tuple(fields) => { w!(self, "("); self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); w!(this, "{}: ", name); this.print_type_ref(type_ref); wln!(this, ","); } }); w!(self, ")"); } Fields::Unit => {} } } fn print_mod_item(&mut self, item: ModItem) { self.print_attrs_of(item); match item { ModItem::Import(it) => { let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "use {}", path); if *is_glob { w!(self, "::*"); } if let Some(alias) = alias { w!(self, " as {}", alias); } wln!(self, "; // {}", index); } ModItem::ExternCrate(it) => { let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "extern crate {}", name); if let Some(alias) = alias { w!(self, " as {}", alias); } wln!(self, ";"); } ModItem::ExternBlock(it) => { let ExternBlock { abi, ast_id: _, children } = &self.tree[it]; w!(self, "extern "); if let Some(abi) = abi { w!(self, "\"{}\" ", abi); } w!(self, "{{"); self.indented(|this| { for child in &**children { this.print_mod_item(*child); } }); wln!(self, "}}"); } ModItem::Function(it) => { let Function { name, visibility, generic_params: _, // FIXME print these somehow abi, params, ret_type, ast_id: _, flags, } = &self.tree[it]; if flags.bits != 0 { wln!(self, "// flags = 0x{:X}", flags.bits); } self.print_visibility(*visibility); if let Some(abi) = abi { w!(self, "extern \"{}\" ", abi); } w!(self, "fn {}(", name); if !params.is_empty() { self.indented(|this| { for param in params.clone() { this.print_attrs_of(param); match &this.tree[param] { Param::Normal(ty) => { w!(this, "_: "); this.print_type_ref(ty); wln!(this, ","); } Param::Varargs => { wln!(this, "..."); } }; } }); } w!(self, ") -> "); self.print_type_ref(ret_type); wln!(self, ";"); } ModItem::Struct(it) => { let Struct { visibility, name, fields, generic_params: _, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "struct {}", name); self.print_fields(fields); if matches!(fields, Fields::Record(_)) { wln!(self); } else { wln!(self, ";"); } } ModItem::Union(it) => { let Union { name, visibility, fields, generic_params: _, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "union {}", name); self.print_fields(fields); if matches!(fields, Fields::Record(_)) { wln!(self); } else { wln!(self, ";"); } } ModItem::Enum(it) => { let Enum { name, visibility, variants, generic_params: _, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "enum {} {{", name); self.indented(|this| { for variant in variants.clone() { let Variant { name, fields } = &this.tree[variant]; this.print_attrs_of(variant); w!(this, "{}", name); this.print_fields(fields); wln!(this, ","); } }); wln!(self, "}}"); } ModItem::Const(it) => { let Const { name, visibility, type_ref, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "const "); match name { Some(name) => w!(self, "{}", name), None => w!(self, "_"), } w!(self, ": "); self.print_type_ref(type_ref); wln!(self, " = _;"); } ModItem::Static(it) => { let Static { name, visibility, mutable, is_extern, type_ref, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "static "); if *mutable { w!(self, "mut "); } w!(self, "{}: ", name); self.print_type_ref(type_ref); w!(self, " = _;"); if *is_extern { w!(self, " // extern"); } wln!(self); } ModItem::Trait(it) => { let Trait { name, visibility, is_auto, is_unsafe, bounds, items, generic_params: _, ast_id: _, } = &self.tree[it]; self.print_visibility(*visibility); if *is_unsafe { w!(self, "unsafe "); } if *is_auto { w!(self, "auto "); } w!(self, "trait {}", name); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); } w!(self, " {{"); self.indented(|this| { for item in &**items { this.print_mod_item((*item).into()); } }); wln!(self, "}}"); } ModItem::Impl(it) => { let Impl { target_trait, self_ty, is_negative, items, generic_params: _, ast_id: _, } = &self.tree[it]; w!(self, "impl "); if *is_negative { w!(self, "!"); } if let Some(tr) = target_trait { self.print_path(&tr.path); w!(self, " for "); } self.print_type_ref(self_ty); w!(self, " {{"); self.indented(|this| { for item in &**items { this.print_mod_item((*item).into()); } }); wln!(self, "}}"); } ModItem::TypeAlias(it) => { let TypeAlias { name, visibility, bounds, type_ref, is_extern, generic_params: _, ast_id: _, } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "type {}", name); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); } if let Some(ty) = type_ref { w!(self, " = "); self.print_type_ref(ty); } w!(self, ";"); if *is_extern { w!(self, " // extern"); } wln!(self); } ModItem::Mod(it) => { let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); w!(self, "mod {}", name); match kind { ModKind::Inline { items } => { w!(self, " {{"); self.indented(|this| { for item in &**items { this.print_mod_item((*item).into()); } }); wln!(self, "}}"); } ModKind::Outline {} => { wln!(self, ";"); } } } ModItem::MacroCall(it) => { let MacroCall { path, ast_id: _, fragment: _ } = &self.tree[it]; wln!(self, "{}!(...);", path); } ModItem::MacroRules(it) => { let MacroRules { name, ast_id: _ } = &self.tree[it]; wln!(self, "macro_rules! {} {{ ... }}", name); } ModItem::MacroDef(it) => { let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); wln!(self, "macro {} {{ ... }}", name); } } self.blank(); } fn print_type_ref(&mut self, type_ref: &TypeRef) { // FIXME: deduplicate with `HirDisplay` impl match type_ref { TypeRef::Never => w!(self, "!"), TypeRef::Placeholder => w!(self, "_"), TypeRef::Tuple(fields) => { w!(self, "("); for (i, field) in fields.iter().enumerate() { if i != 0 { w!(self, ", "); } self.print_type_ref(field); } w!(self, ")"); } TypeRef::Path(path) => self.print_path(path), TypeRef::RawPtr(pointee, mtbl) => { let mtbl = match mtbl { Mutability::Shared => "*const", Mutability::Mut => "*mut", }; w!(self, "{} ", mtbl); self.print_type_ref(pointee); } TypeRef::Reference(pointee, lt, mtbl) => { let mtbl = match mtbl { Mutability::Shared => "", Mutability::Mut => "mut ", }; w!(self, "&"); if let Some(lt) = lt { w!(self, "{} ", lt.name); } w!(self, "{}", mtbl); self.print_type_ref(pointee); } TypeRef::Array(elem, len) => { w!(self, "["); self.print_type_ref(elem); w!(self, "; {}]", len); } TypeRef::Slice(elem) => { w!(self, "["); self.print_type_ref(elem); w!(self, "]"); } TypeRef::Fn(args_and_ret, varargs) => { let (ret, args) = args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); w!(self, "fn("); for (i, arg) in args.iter().enumerate() { if i != 0 { w!(self, ", "); } self.print_type_ref(arg); } if *varargs { if !args.is_empty() { w!(self, ", "); } w!(self, "..."); } w!(self, ") -> "); self.print_type_ref(ret); } TypeRef::Macro(_ast_id) => { w!(self, ""); } TypeRef::Error => drop(write!(self, "{{unknown}}")), TypeRef::ImplTrait(bounds) => { w!(self, "impl "); self.print_type_bounds(bounds); } TypeRef::DynTrait(bounds) => { w!(self, "dyn "); self.print_type_bounds(bounds); } } } fn print_type_bounds(&mut self, bounds: &[TypeBound]) { for (i, bound) in bounds.iter().enumerate() { if i != 0 { w!(self, " + "); } match bound { TypeBound::Path(path) => self.print_path(path), TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), TypeBound::Error => w!(self, "{{unknown}}"), } } } fn print_path(&mut self, path: &Path) { if path.type_anchor().is_none() && path.segments().iter().all(|seg| seg.args_and_bindings.is_none()) { w!(self, "{}", path.mod_path()); } else { // too complicated, just use `Debug` w!(self, "{:?}", path); } } } impl<'a> Write for Printer<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { if self.needs_indent { match self.buf.chars().last() { Some('\n') | None => {} _ => self.buf.push('\n'), } self.buf.push_str(&" ".repeat(self.indent_level)); self.needs_indent = false; } self.buf.push_str(line); self.needs_indent = line.ends_with('\n'); } Ok(()) } }