Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/pretty.rs')
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs602
1 files changed, 547 insertions, 55 deletions
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index cc6d200051..e0eccb6cd0 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -1,25 +1,52 @@
//! A pretty-printer for HIR.
+#![allow(dead_code)]
-use std::fmt::{self, Write};
+use std::{
+ fmt::{self, Write},
+ mem,
+};
+use hir_expand::{Lookup, mod_path::PathKind};
use itertools::Itertools;
use span::Edition;
use crate::{
- hir::{Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement},
- pretty::{print_generic_args, print_path, print_type_ref},
+ DefWithBodyId, ItemTreeLoc, TypeParamId,
+ expr_store::path::{GenericArg, GenericArgs},
+ hir::{
+ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
+ generics::{GenericParams, WherePredicate, WherePredicateTypeTarget},
+ },
+ lang_item::LangItemTarget,
+ signatures::{FnFlags, FunctionSignature, StructSignature},
+ type_ref::{ConstRef, Mutability, TraitBoundModifier, TypeBound, UseArgRef},
};
use super::*;
+macro_rules! w {
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = write!($dst, $($arg)*); }
+ };
+}
+
+macro_rules! wln {
+ ($dst:expr) => {
+ { $dst.newline(); }
+ };
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = w!($dst, $($arg)*); $dst.newline(); }
+ };
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(super) enum LineFormat {
+pub(crate) enum LineFormat {
Oneline,
Newline,
Indentation,
}
-pub(super) fn print_body_hir(
+pub(crate) fn print_body_hir(
db: &dyn DefDatabase,
body: &Body,
owner: DefWithBodyId,
@@ -43,7 +70,6 @@ pub(super) fn print_body_hir(
}
)
}),
- DefWithBodyId::InTypeConstId(_) => "In type const = ".to_owned(),
DefWithBodyId::VariantId(it) => {
let loc = it.lookup(db);
let enum_loc = loc.parent.lookup(db);
@@ -63,22 +89,14 @@ pub(super) fn print_body_hir(
line_format: LineFormat::Newline,
edition,
};
- if let DefWithBodyId::FunctionId(it) = owner {
+ if let DefWithBodyId::FunctionId(_) = owner {
p.buf.push('(');
- let function_data = db.function_data(it);
- let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
if let Some(self_param) = body.self_param {
p.print_binding(self_param);
- p.buf.push_str(": ");
- if let Some(ty) = params.next() {
- p.print_type_ref(*ty, &function_data.types_map);
- p.buf.push_str(", ");
- }
+ p.buf.push_str(", ");
}
- body.params.iter().zip(params).for_each(|(&param, ty)| {
- p.print_pat(param);
- p.buf.push_str(": ");
- p.print_type_ref(*ty, &function_data.types_map);
+ body.params.iter().for_each(|param| {
+ p.print_pat(*param);
p.buf.push_str(", ");
});
// remove the last ", " in param list
@@ -86,9 +104,6 @@ pub(super) fn print_body_hir(
p.buf.truncate(p.buf.len() - 2);
}
p.buf.push(')');
- // return type
- p.buf.push_str(" -> ");
- p.print_type_ref(*ret_type, &function_data.types_map);
p.buf.push(' ');
}
p.print_expr(body.body_expr);
@@ -98,7 +113,240 @@ pub(super) fn print_body_hir(
p.buf
}
-pub(super) fn print_expr_hir(
+pub(crate) fn print_path(
+ db: &dyn DefDatabase,
+ store: &ExpressionStore,
+ path: &Path,
+ edition: Edition,
+) -> String {
+ let mut p = Printer {
+ db,
+ store,
+ buf: String::new(),
+ indent_level: 0,
+ line_format: LineFormat::Newline,
+ edition,
+ };
+ p.print_path(path);
+ p.buf
+}
+
+pub(crate) fn print_struct(
+ db: &dyn DefDatabase,
+ StructSignature { name, generic_params, store, flags, shape, repr }: &StructSignature,
+ edition: Edition,
+) -> String {
+ use crate::item_tree::FieldsShape;
+ use crate::signatures::StructFlags;
+
+ let mut p = Printer {
+ db,
+ store,
+ buf: String::new(),
+ indent_level: 0,
+ line_format: LineFormat::Newline,
+ edition,
+ };
+ if let Some(repr) = repr {
+ if repr.c() {
+ wln!(p, "#[repr(C)]");
+ }
+ if let Some(align) = repr.align {
+ wln!(p, "#[repr(align({}))]", align.bytes());
+ }
+ if let Some(pack) = repr.pack {
+ wln!(p, "#[repr(pack({}))]", pack.bytes());
+ }
+ }
+ if flags.contains(StructFlags::IS_FUNDAMENTAL) {
+ wln!(p, "#[fundamental]");
+ }
+ w!(p, "struct ");
+ w!(p, "{}", name.display(db.upcast(), edition));
+ print_generic_params(db, generic_params, &mut p);
+ match shape {
+ FieldsShape::Record => wln!(p, " {{...}}"),
+ FieldsShape::Tuple => wln!(p, "(...)"),
+ FieldsShape::Unit => (),
+ }
+
+ print_where_clauses(db, generic_params, &mut p);
+
+ match shape {
+ FieldsShape::Record => wln!(p),
+ FieldsShape::Tuple => wln!(p, ";"),
+ FieldsShape::Unit => wln!(p, ";"),
+ }
+
+ p.buf
+}
+
+pub(crate) fn print_function(
+ db: &dyn DefDatabase,
+ FunctionSignature {
+ name,
+ generic_params,
+ store,
+ params,
+ ret_type,
+ abi,
+ flags,
+ legacy_const_generics_indices,
+ }: &FunctionSignature,
+ edition: Edition,
+) -> String {
+ let mut p = Printer {
+ db,
+ store,
+ buf: String::new(),
+ indent_level: 0,
+ line_format: LineFormat::Newline,
+ edition,
+ };
+ if flags.contains(FnFlags::HAS_CONST_KW) {
+ w!(p, "const ");
+ }
+ if flags.contains(FnFlags::HAS_ASYNC_KW) {
+ w!(p, "async ");
+ }
+ if flags.contains(FnFlags::HAS_UNSAFE_KW) {
+ w!(p, "unsafe ");
+ }
+ if flags.contains(FnFlags::HAS_SAFE_KW) {
+ w!(p, "safe ");
+ }
+ if let Some(abi) = abi {
+ w!(p, "extern \"{}\" ", abi.as_str());
+ }
+ w!(p, "fn ");
+ w!(p, "{}", name.display(db.upcast(), edition));
+ print_generic_params(db, generic_params, &mut p);
+ w!(p, "(");
+ for (i, param) in params.iter().enumerate() {
+ if i != 0 {
+ w!(p, ", ");
+ }
+ if legacy_const_generics_indices.as_ref().is_some_and(|idx| idx.contains(&(i as u32))) {
+ w!(p, "const: ");
+ }
+ p.print_type_ref(*param);
+ }
+ w!(p, ")");
+ if let Some(ret_type) = ret_type {
+ w!(p, " -> ");
+ p.print_type_ref(*ret_type);
+ }
+
+ print_where_clauses(db, generic_params, &mut p);
+ wln!(p, " {{...}}");
+
+ p.buf
+}
+
+fn print_where_clauses(db: &dyn DefDatabase, generic_params: &GenericParams, p: &mut Printer<'_>) {
+ if !generic_params.where_predicates.is_empty() {
+ w!(p, "\nwhere\n");
+ p.indented(|p| {
+ for (i, pred) in generic_params.where_predicates.iter().enumerate() {
+ if i != 0 {
+ w!(p, ",\n");
+ }
+ match pred {
+ WherePredicate::TypeBound { target, bound } => match target {
+ &WherePredicateTypeTarget::TypeRef(idx) => {
+ p.print_type_ref(idx);
+ w!(p, ": ");
+ p.print_type_bounds(std::slice::from_ref(bound));
+ }
+ WherePredicateTypeTarget::TypeOrConstParam(idx) => {
+ match generic_params[*idx].name() {
+ Some(name) => w!(p, "{}", name.display(db.upcast(), p.edition)),
+ None => w!(p, "Param[{}]", idx.into_raw()),
+ }
+ w!(p, ": ");
+ p.print_type_bounds(std::slice::from_ref(bound));
+ }
+ },
+ WherePredicate::Lifetime { target, bound } => {
+ w!(
+ p,
+ "{}: {}",
+ target.name.display(db.upcast(), p.edition),
+ bound.name.display(db.upcast(), p.edition)
+ );
+ }
+ WherePredicate::ForLifetime { lifetimes, target, bound } => {
+ w!(p, "for<");
+ for (i, lifetime) in lifetimes.iter().enumerate() {
+ if i != 0 {
+ w!(p, ", ");
+ }
+ w!(p, "{}", lifetime.display(db.upcast(), p.edition));
+ }
+ w!(p, "> ");
+ match target {
+ WherePredicateTypeTarget::TypeRef(idx) => {
+ p.print_type_ref(*idx);
+ w!(p, ": ");
+ p.print_type_bounds(std::slice::from_ref(bound));
+ }
+ WherePredicateTypeTarget::TypeOrConstParam(idx) => {
+ match generic_params[*idx].name() {
+ Some(name) => w!(p, "{}", name.display(db.upcast(), p.edition)),
+ None => w!(p, "Param[{}]", idx.into_raw()),
+ }
+ w!(p, ": ");
+ p.print_type_bounds(std::slice::from_ref(bound));
+ }
+ }
+ }
+ }
+ }
+ });
+ wln!(p);
+ }
+}
+
+fn print_generic_params(db: &dyn DefDatabase, generic_params: &GenericParams, p: &mut Printer<'_>) {
+ if !generic_params.is_empty() {
+ w!(p, "<");
+ let mut first = true;
+ for (_i, param) in generic_params.iter_lt() {
+ if !first {
+ w!(p, ", ");
+ }
+ first = false;
+ w!(p, "{}", param.name.display(db.upcast(), p.edition));
+ }
+ for (i, param) in generic_params.iter_type_or_consts() {
+ if !first {
+ w!(p, ", ");
+ }
+ first = false;
+ if let Some(const_param) = param.const_param() {
+ w!(p, "const {}: ", const_param.name.display(db.upcast(), p.edition));
+ p.print_type_ref(const_param.ty);
+ if let Some(default) = const_param.default {
+ w!(p, " = ");
+ p.print_expr(default.expr);
+ }
+ }
+ if let Some(type_param) = param.type_param() {
+ match &type_param.name {
+ Some(name) => w!(p, "{}", name.display(db.upcast(), p.edition)),
+ None => w!(p, "Param[{}]", i.into_raw()),
+ }
+ if let Some(default) = type_param.default {
+ w!(p, " = ");
+ p.print_type_ref(default);
+ }
+ }
+ }
+ w!(p, ">");
+ }
+}
+
+pub(crate) fn print_expr_hir(
db: &dyn DefDatabase,
store: &ExpressionStore,
_owner: DefWithBodyId,
@@ -117,7 +365,7 @@ pub(super) fn print_expr_hir(
p.buf
}
-pub(super) fn print_pat_hir(
+pub(crate) fn print_pat_hir(
db: &dyn DefDatabase,
store: &ExpressionStore,
_owner: DefWithBodyId,
@@ -137,21 +385,6 @@ pub(super) fn print_pat_hir(
p.buf
}
-macro_rules! w {
- ($dst:expr, $($arg:tt)*) => {
- { let _ = write!($dst, $($arg)*); }
- };
-}
-
-macro_rules! wln {
- ($dst:expr) => {
- { $dst.newline(); }
- };
- ($dst:expr, $($arg:tt)*) => {
- { let _ = w!($dst, $($arg)*); $dst.newline(); }
- };
-}
-
struct Printer<'a> {
db: &'a dyn DefDatabase,
store: &'a ExpressionStore,
@@ -238,7 +471,7 @@ impl Printer<'_> {
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
Expr::OffsetOf(offset_of) => {
w!(self, "builtin#offset_of(");
- self.print_type_ref(offset_of.container, &self.store.types);
+ self.print_type_ref(offset_of.container);
let edition = self.edition;
w!(
self,
@@ -291,8 +524,7 @@ impl Printer<'_> {
w!(self, ".{}", method_name.display(self.db.upcast(), self.edition));
if let Some(args) = generic_args {
w!(self, "::<");
- let edition = self.edition;
- print_generic_args(self.db, args, &self.store.types, self, edition).unwrap();
+ self.print_generic_args(args);
w!(self, ">");
}
w!(self, "(");
@@ -401,7 +633,7 @@ impl Printer<'_> {
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
- self.print_type_ref(*type_ref, &self.store.types);
+ self.print_type_ref(*type_ref);
}
Expr::Ref { expr, rawness, mutability } => {
w!(self, "&");
@@ -489,13 +721,13 @@ impl Printer<'_> {
self.print_pat(*pat);
if let Some(ty) = ty {
w!(self, ": ");
- self.print_type_ref(*ty, &self.store.types);
+ self.print_type_ref(*ty);
}
}
w!(self, "|");
if let Some(ret_ty) = ret_type {
w!(self, " -> ");
- self.print_type_ref(*ret_ty, &self.store.types);
+ self.print_type_ref(*ret_ty);
}
self.whitespace();
self.print_expr(*body);
@@ -731,7 +963,7 @@ impl Printer<'_> {
self.print_pat(*pat);
if let Some(ty) = type_ref {
w!(self, ": ");
- self.print_type_ref(*ty, &self.store.types);
+ self.print_type_ref(*ty);
}
if let Some(init) = initializer {
w!(self, " = ");
@@ -782,16 +1014,6 @@ impl Printer<'_> {
}
}
- fn print_type_ref(&mut self, ty: TypeRefId, map: &TypesMap) {
- let edition = self.edition;
- print_type_ref(self.db, ty, map, self, edition).unwrap();
- }
-
- fn print_path(&mut self, path: &Path) {
- let edition = self.edition;
- print_path(self.db, path, &self.store.types, self, edition).unwrap();
- }
-
fn print_binding(&mut self, id: BindingId) {
let Binding { name, mode, .. } = &self.store.bindings[id];
let mode = match mode {
@@ -802,4 +1024,274 @@ impl Printer<'_> {
};
w!(self, "{}{}", mode, name.display(self.db.upcast(), self.edition));
}
+
+ fn print_path(&mut self, path: &Path) {
+ if let Path::LangItem(it, s) = path {
+ w!(self, "builtin#lang(");
+ macro_rules! write_name {
+ ($it:ident) => {{
+ let loc = $it.lookup(self.db);
+ let tree = loc.item_tree_id().item_tree(self.db);
+ let name = &tree[loc.id.value].name;
+ w!(self, "{}", name.display(self.db.upcast(), self.edition));
+ }};
+ }
+ match *it {
+ LangItemTarget::ImplDef(it) => w!(self, "{it:?}"),
+ LangItemTarget::EnumId(it) => write_name!(it),
+ LangItemTarget::Function(it) => write_name!(it),
+ LangItemTarget::Static(it) => write_name!(it),
+ LangItemTarget::Struct(it) => write_name!(it),
+ LangItemTarget::Union(it) => write_name!(it),
+ LangItemTarget::TypeAlias(it) => write_name!(it),
+ LangItemTarget::Trait(it) => write_name!(it),
+ LangItemTarget::EnumVariant(it) => write_name!(it),
+ }
+
+ if let Some(s) = s {
+ w!(self, "::{}", s.display(self.db.upcast(), self.edition));
+ }
+ return w!(self, ")");
+ }
+ match path.type_anchor() {
+ Some(anchor) => {
+ w!(self, "<");
+ self.print_type_ref(anchor);
+ w!(self, ">::");
+ }
+ None => match path.kind() {
+ PathKind::Plain => {}
+ &PathKind::SELF => w!(self, "self"),
+ PathKind::Super(n) => {
+ for i in 0..*n {
+ if i == 0 {
+ w!(self, "super");
+ } else {
+ w!(self, "::super");
+ }
+ }
+ }
+ PathKind::Crate => w!(self, "crate"),
+ PathKind::Abs => {}
+ PathKind::DollarCrate(krate) => w!(
+ self,
+ "{}",
+ krate
+ .extra_data(self.db)
+ .display_name
+ .as_ref()
+ .map(|it| it.crate_name().symbol().as_str())
+ .unwrap_or("$crate")
+ ),
+ },
+ }
+
+ for (i, segment) in path.segments().iter().enumerate() {
+ if i != 0 || !matches!(path.kind(), PathKind::Plain) {
+ w!(self, "::");
+ }
+
+ w!(self, "{}", segment.name.display(self.db.upcast(), self.edition));
+ if let Some(generics) = segment.args_and_bindings {
+ w!(self, "::<");
+ self.print_generic_args(generics);
+
+ w!(self, ">");
+ }
+ }
+ }
+
+ pub(crate) fn print_generic_args(&mut self, generics: &GenericArgs) {
+ let mut first = true;
+ let args = if generics.has_self_type {
+ let (self_ty, args) = generics.args.split_first().unwrap();
+ w!(self, "Self=");
+ self.print_generic_arg(self_ty);
+ first = false;
+ args
+ } else {
+ &generics.args
+ };
+ for arg in args {
+ if !first {
+ w!(self, ", ");
+ }
+ first = false;
+ self.print_generic_arg(arg);
+ }
+ for binding in generics.bindings.iter() {
+ if !first {
+ w!(self, ", ");
+ }
+ first = false;
+ w!(self, "{}", binding.name.display(self.db.upcast(), self.edition));
+ if !binding.bounds.is_empty() {
+ w!(self, ": ");
+ self.print_type_bounds(&binding.bounds);
+ }
+ if let Some(ty) = binding.type_ref {
+ w!(self, " = ");
+ self.print_type_ref(ty);
+ }
+ }
+ }
+
+ pub(crate) fn print_generic_arg(&mut self, arg: &GenericArg) {
+ match arg {
+ GenericArg::Type(ty) => self.print_type_ref(*ty),
+ GenericArg::Const(ConstRef { expr }) => self.print_expr(*expr),
+ GenericArg::Lifetime(lt) => {
+ w!(self, "{}", lt.name.display(self.db.upcast(), self.edition))
+ }
+ }
+ }
+
+ pub(crate) fn print_type_param(&mut self, param: TypeParamId) {
+ let generic_params = self.db.generic_params(param.parent());
+
+ match generic_params[param.local_id()].name() {
+ Some(name) => w!(self, "{}", name.display(self.db.upcast(), self.edition)),
+ None => w!(self, "Param[{}]", param.local_id().into_raw()),
+ }
+ }
+
+ pub(crate) fn print_type_ref(&mut self, type_ref: TypeRefId) {
+ // FIXME: deduplicate with `HirDisplay` impl
+ match &self.store[type_ref] {
+ TypeRef::Never => w!(self, "!"),
+ &TypeRef::TypeParam(p) => self.print_type_param(p),
+ 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(ref_) => {
+ let mtbl = match ref_.mutability {
+ Mutability::Shared => "",
+ Mutability::Mut => "mut ",
+ };
+ w!(self, "&");
+ if let Some(lt) = &ref_.lifetime {
+ w!(self, "{} ", lt.name.display(self.db.upcast(), self.edition));
+ }
+ w!(self, "{mtbl}");
+ self.print_type_ref(ref_.ty);
+ }
+ TypeRef::Array(array) => {
+ w!(self, "[");
+ self.print_type_ref(array.ty);
+ w!(self, "; ");
+ self.print_generic_arg(&GenericArg::Const(array.len));
+ w!(self, "]");
+ }
+ TypeRef::Slice(elem) => {
+ w!(self, "[");
+ self.print_type_ref(*elem);
+ w!(self, "]");
+ }
+ TypeRef::Fn(fn_) => {
+ let ((_, return_type), args) =
+ fn_.params.split_last().expect("TypeRef::Fn is missing return type");
+ if fn_.is_unsafe {
+ w!(self, "unsafe ");
+ }
+ if let Some(abi) = &fn_.abi {
+ w!(self, "extern ");
+ w!(self, "{}", abi.as_str());
+ w!(self, " ");
+ }
+ w!(self, "fn(");
+ for (i, (_, typeref)) in args.iter().enumerate() {
+ if i != 0 {
+ w!(self, ", ");
+ }
+ self.print_type_ref(*typeref);
+ }
+ if fn_.is_varargs {
+ if !args.is_empty() {
+ w!(self, ", ");
+ }
+ w!(self, "...");
+ }
+ w!(self, ") -> ");
+ self.print_type_ref(*return_type);
+ }
+ TypeRef::Error => w!(self, "{{error}}"),
+ TypeRef::ImplTrait(bounds) => {
+ w!(self, "impl ");
+ self.print_type_bounds(bounds);
+ }
+ TypeRef::DynTrait(bounds) => {
+ w!(self, "dyn ");
+ self.print_type_bounds(bounds);
+ }
+ }
+ }
+
+ pub(crate) 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, modifier) => {
+ match modifier {
+ TraitBoundModifier::None => (),
+ TraitBoundModifier::Maybe => w!(self, "?"),
+ }
+ self.print_path(&self.store[*path]);
+ }
+ TypeBound::ForLifetime(lifetimes, path) => {
+ w!(
+ self,
+ "for<{}> ",
+ lifetimes
+ .iter()
+ .map(|it| it.display(self.db.upcast(), self.edition))
+ .format(", ")
+ .to_string()
+ );
+ self.print_path(&self.store[*path]);
+ }
+ TypeBound::Lifetime(lt) => {
+ w!(self, "{}", lt.name.display(self.db.upcast(), self.edition))
+ }
+ TypeBound::Use(args) => {
+ w!(self, "use<");
+ let mut first = true;
+ for arg in args {
+ if !mem::take(&mut first) {
+ w!(self, ", ");
+ }
+ match arg {
+ UseArgRef::Name(it) => {
+ w!(self, "{}", it.display(self.db.upcast(), self.edition))
+ }
+ UseArgRef::Lifetime(it) => {
+ w!(self, "{}", it.name.display(self.db.upcast(), self.edition))
+ }
+ }
+ }
+ w!(self, ">")
+ }
+ TypeBound::Error => w!(self, "{{unknown}}"),
+ }
+ }
+ }
}