| //! `ItemTree` debug printer. |
| |
| use std::fmt::{self, Write}; |
| |
| use crate::{ |
| generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, |
| pretty::{print_path, print_type_bounds, print_type_ref}, |
| 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)*) => { |
| { let _ = write!($dst, $($arg)*); } |
| }; |
| } |
| |
| macro_rules! wln { |
| ($dst:expr) => { |
| { let _ = writeln!($dst); } |
| }; |
| ($dst:expr, $($arg:tt)*) => { |
| { let _ = 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') | None) | (None, None) => {} |
| (Some('\n'), Some(_)) => { |
| self.buf.push('\n'); |
| } |
| (Some(_), _) => { |
| self.buf.push('\n'); |
| self.buf.push('\n'); |
| } |
| (None, Some(_)) => unreachable!(), |
| } |
| } |
| |
| fn whitespace(&mut self) { |
| match self.buf.chars().next_back() { |
| None | Some('\n' | ' ') => {} |
| _ => self.buf.push(' '), |
| } |
| } |
| |
| 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(), |
| ); |
| } |
| } |
| |
| fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) { |
| 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) => { |
| self.whitespace(); |
| w!(self, "{{"); |
| self.indented(|this| { |
| for field in fields.clone() { |
| let Field { visibility, name, type_ref, ast_id: _ } = &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, ast_id: _ } = &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_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericParams) { |
| match fields { |
| Fields::Record(_) => { |
| if self.print_where_clause(params) { |
| wln!(self); |
| } |
| self.print_fields(fields); |
| } |
| Fields::Unit => { |
| self.print_where_clause(params); |
| self.print_fields(fields); |
| } |
| Fields::Tuple(_) => { |
| self.print_fields(fields); |
| self.print_where_clause(params); |
| } |
| } |
| } |
| |
| fn print_use_tree(&mut self, use_tree: &UseTree) { |
| match &use_tree.kind { |
| UseTreeKind::Single { path, alias } => { |
| w!(self, "{}", path); |
| if let Some(alias) = alias { |
| w!(self, " as {}", alias); |
| } |
| } |
| UseTreeKind::Glob { path } => { |
| if let Some(path) = path { |
| w!(self, "{}::", path); |
| } |
| w!(self, "*"); |
| } |
| UseTreeKind::Prefixed { prefix, list } => { |
| if let Some(prefix) = prefix { |
| w!(self, "{}::", prefix); |
| } |
| w!(self, "{{"); |
| for (i, tree) in list.iter().enumerate() { |
| if i != 0 { |
| w!(self, ", "); |
| } |
| self.print_use_tree(tree); |
| } |
| w!(self, "}}"); |
| } |
| } |
| } |
| |
| fn print_mod_item(&mut self, item: ModItem) { |
| self.print_attrs_of(item); |
| |
| match item { |
| ModItem::Import(it) => { |
| let Import { visibility, use_tree, ast_id: _ } = &self.tree[it]; |
| self.print_visibility(*visibility); |
| w!(self, "use "); |
| self.print_use_tree(use_tree); |
| wln!(self, ";"); |
| } |
| 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, |
| explicit_generic_params, |
| abi, |
| params, |
| ret_type, |
| async_ret_type: _, |
| ast_id: _, |
| flags, |
| } = &self.tree[it]; |
| self.print_visibility(*visibility); |
| if flags.contains(FnFlags::HAS_DEFAULT_KW) { |
| w!(self, "default "); |
| } |
| if flags.contains(FnFlags::HAS_CONST_KW) { |
| w!(self, "const "); |
| } |
| if flags.contains(FnFlags::HAS_ASYNC_KW) { |
| w!(self, "async "); |
| } |
| if flags.contains(FnFlags::HAS_UNSAFE_KW) { |
| w!(self, "unsafe "); |
| } |
| if let Some(abi) = abi { |
| w!(self, "extern \"{}\" ", abi); |
| } |
| w!(self, "fn {}", name); |
| self.print_generic_params(explicit_generic_params); |
| w!(self, "("); |
| if !params.is_empty() { |
| self.indented(|this| { |
| for (i, param) in params.clone().enumerate() { |
| this.print_attrs_of(param); |
| match &this.tree[param] { |
| Param::Normal(name, ty) => { |
| match name { |
| Some(name) => w!(this, "{}: ", name), |
| None => w!(this, "_: "), |
| } |
| this.print_type_ref(ty); |
| w!(this, ","); |
| if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 { |
| wln!(this, " // self"); |
| } else { |
| wln!(this); |
| } |
| } |
| Param::Varargs => { |
| wln!(this, "..."); |
| } |
| }; |
| } |
| }); |
| } |
| w!(self, ") -> "); |
| self.print_type_ref(ret_type); |
| self.print_where_clause(explicit_generic_params); |
| if flags.contains(FnFlags::HAS_BODY) { |
| wln!(self, " {{ ... }}"); |
| } else { |
| 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_generic_params(generic_params); |
| self.print_fields_and_where_clause(fields, generic_params); |
| 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_generic_params(generic_params); |
| self.print_fields_and_where_clause(fields, generic_params); |
| 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.print_generic_params(generic_params); |
| self.print_where_clause_and_opening_brace(generic_params); |
| self.indented(|this| { |
| for variant in variants.clone() { |
| let Variant { name, fields, ast_id: _ } = &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, 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, " = _;"); |
| wln!(self); |
| } |
| ModItem::Trait(it) => { |
| let Trait { |
| name, |
| visibility, |
| is_auto, |
| is_unsafe, |
| 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); |
| self.print_generic_params(generic_params); |
| match items { |
| Some(items) => { |
| self.print_where_clause_and_opening_brace(generic_params); |
| self.indented(|this| { |
| for item in &**items { |
| this.print_mod_item((*item).into()); |
| } |
| }); |
| } |
| None => { |
| w!(self, " = "); |
| // FIXME: Print the aliased traits |
| self.print_where_clause_and_opening_brace(generic_params); |
| } |
| } |
| wln!(self, "}}"); |
| } |
| ModItem::Impl(it) => { |
| let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } = |
| &self.tree[it]; |
| w!(self, "impl"); |
| self.print_generic_params(generic_params); |
| w!(self, " "); |
| 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); |
| self.print_where_clause_and_opening_brace(generic_params); |
| 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, generic_params, ast_id: _ } = |
| &self.tree[it]; |
| self.print_visibility(*visibility); |
| w!(self, "type {}", name); |
| self.print_generic_params(generic_params); |
| if !bounds.is_empty() { |
| w!(self, ": "); |
| self.print_type_bounds(bounds); |
| } |
| if let Some(ty) = type_ref { |
| w!(self, " = "); |
| self.print_type_ref(ty); |
| } |
| self.print_where_clause(generic_params); |
| w!(self, ";"); |
| 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); |
| } |
| }); |
| wln!(self, "}}"); |
| } |
| ModKind::Outline => { |
| wln!(self, ";"); |
| } |
| } |
| } |
| ModItem::MacroCall(it) => { |
| let MacroCall { path, ast_id: _, expand_to: _ } = &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) { |
| print_type_ref(type_ref, self).unwrap(); |
| } |
| |
| fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) { |
| print_type_bounds(bounds, self).unwrap(); |
| } |
| |
| fn print_path(&mut self, path: &Path) { |
| print_path(path, self).unwrap(); |
| } |
| |
| fn print_generic_params(&mut self, params: &GenericParams) { |
| if params.type_or_consts.is_empty() && params.lifetimes.is_empty() { |
| return; |
| } |
| |
| w!(self, "<"); |
| let mut first = true; |
| for (_, lt) in params.lifetimes.iter() { |
| if !first { |
| w!(self, ", "); |
| } |
| first = false; |
| w!(self, "{}", lt.name); |
| } |
| for (idx, x) in params.type_or_consts.iter() { |
| if !first { |
| w!(self, ", "); |
| } |
| first = false; |
| match x { |
| TypeOrConstParamData::TypeParamData(ty) => match &ty.name { |
| Some(name) => w!(self, "{}", name), |
| None => w!(self, "_anon_{}", idx.into_raw()), |
| }, |
| TypeOrConstParamData::ConstParamData(konst) => { |
| w!(self, "const {}: ", konst.name); |
| self.print_type_ref(&konst.ty); |
| } |
| } |
| } |
| w!(self, ">"); |
| } |
| |
| fn print_where_clause_and_opening_brace(&mut self, params: &GenericParams) { |
| if self.print_where_clause(params) { |
| w!(self, "\n{{"); |
| } else { |
| self.whitespace(); |
| w!(self, "{{"); |
| } |
| } |
| |
| fn print_where_clause(&mut self, params: &GenericParams) -> bool { |
| if params.where_predicates.is_empty() { |
| return false; |
| } |
| |
| w!(self, "\nwhere"); |
| self.indented(|this| { |
| for (i, pred) in params.where_predicates.iter().enumerate() { |
| if i != 0 { |
| wln!(this, ","); |
| } |
| |
| let (target, bound) = match pred { |
| WherePredicate::TypeBound { target, bound } => (target, bound), |
| WherePredicate::Lifetime { target, bound } => { |
| wln!(this, "{}: {},", target.name, bound.name); |
| continue; |
| } |
| WherePredicate::ForLifetime { lifetimes, target, bound } => { |
| w!(this, "for<"); |
| for (i, lt) in lifetimes.iter().enumerate() { |
| if i != 0 { |
| w!(this, ", "); |
| } |
| w!(this, "{}", lt); |
| } |
| w!(this, "> "); |
| (target, bound) |
| } |
| }; |
| |
| match target { |
| WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), |
| WherePredicateTypeTarget::TypeOrConstParam(id) => { |
| match ¶ms.type_or_consts[*id].name() { |
| Some(name) => w!(this, "{}", name), |
| None => w!(this, "_anon_{}", id.into_raw()), |
| } |
| } |
| } |
| w!(this, ": "); |
| this.print_type_bounds(std::slice::from_ref(bound)); |
| } |
| }); |
| true |
| } |
| } |
| |
| 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(()) |
| } |
| } |