blob: 11d58a6ba09ca776be964d47264a16d09adca11c [file] [log] [blame]
//! Display and pretty printing routines.
use std::fmt::{self, Write};
use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
use intern::Interned;
use itertools::Itertools;
use crate::{
path::{GenericArg, GenericArgs, Path},
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
if let Path::LangItem(it) = path {
return write!(buf, "$lang_item::{it:?}");
}
match path.type_anchor() {
Some(anchor) => {
write!(buf, "<")?;
print_type_ref(db, anchor, buf)?;
write!(buf, ">::")?;
}
None => match path.kind() {
PathKind::Plain => {}
PathKind::Super(0) => write!(buf, "self")?,
PathKind::Super(n) => {
for i in 0..*n {
if i == 0 {
buf.write_str("super")?;
} else {
buf.write_str("::super")?;
}
}
}
PathKind::Crate => write!(buf, "crate")?,
PathKind::Abs => {}
PathKind::DollarCrate(_) => write!(buf, "$crate")?,
},
}
for (i, segment) in path.segments().iter().enumerate() {
if i != 0 || !matches!(path.kind(), PathKind::Plain) {
write!(buf, "::")?;
}
write!(buf, "{}", segment.name.display(db))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
print_generic_args(db, generics, buf)?;
write!(buf, ">")?;
}
}
Ok(())
}
pub(crate) fn print_generic_args(
db: &dyn ExpandDatabase,
generics: &GenericArgs,
buf: &mut dyn Write,
) -> fmt::Result {
let mut first = true;
let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?;
print_generic_arg(db, self_ty, buf)?;
first = false;
args
} else {
&generics.args
};
for arg in args {
if !first {
write!(buf, ", ")?;
}
first = false;
print_generic_arg(db, arg, buf)?;
}
for binding in generics.bindings.iter() {
if !first {
write!(buf, ", ")?;
}
first = false;
write!(buf, "{}", binding.name.display(db))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
print_type_bounds(db, &binding.bounds, buf)?;
}
if let Some(ty) = &binding.type_ref {
write!(buf, " = ")?;
print_type_ref(db, ty, buf)?;
}
}
Ok(())
}
pub(crate) fn print_generic_arg(
db: &dyn ExpandDatabase,
arg: &GenericArg,
buf: &mut dyn Write,
) -> fmt::Result {
match arg {
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
}
}
pub(crate) fn print_type_ref(
db: &dyn ExpandDatabase,
type_ref: &TypeRef,
buf: &mut dyn Write,
) -> fmt::Result {
// FIXME: deduplicate with `HirDisplay` impl
match type_ref {
TypeRef::Never => write!(buf, "!")?,
TypeRef::Placeholder => write!(buf, "_")?,
TypeRef::Tuple(fields) => {
write!(buf, "(")?;
for (i, field) in fields.iter().enumerate() {
if i != 0 {
write!(buf, ", ")?;
}
print_type_ref(db, field, buf)?;
}
write!(buf, ")")?;
}
TypeRef::Path(path) => print_path(db, path, buf)?,
TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "*const",
Mutability::Mut => "*mut",
};
write!(buf, "{mtbl} ")?;
print_type_ref(db, pointee, buf)?;
}
TypeRef::Reference(pointee, lt, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "",
Mutability::Mut => "mut ",
};
write!(buf, "&")?;
if let Some(lt) = lt {
write!(buf, "{} ", lt.name.display(db))?;
}
write!(buf, "{mtbl}")?;
print_type_ref(db, pointee, buf)?;
}
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
print_type_ref(db, elem, buf)?;
write!(buf, "; {}]", len.display(db))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
print_type_ref(db, elem, buf)?;
write!(buf, "]")?;
}
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
let ((_, return_type), args) =
args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
if *is_unsafe {
write!(buf, "unsafe ")?;
}
write!(buf, "fn(")?;
for (i, (_, typeref)) in args.iter().enumerate() {
if i != 0 {
write!(buf, ", ")?;
}
print_type_ref(db, typeref, buf)?;
}
if *varargs {
if !args.is_empty() {
write!(buf, ", ")?;
}
write!(buf, "...")?;
}
write!(buf, ") -> ")?;
print_type_ref(db, return_type, buf)?;
}
TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?;
}
TypeRef::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?;
print_type_bounds(db, bounds, buf)?;
}
TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?;
print_type_bounds(db, bounds, buf)?;
}
}
Ok(())
}
pub(crate) fn print_type_bounds(
db: &dyn ExpandDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
for (i, bound) in bounds.iter().enumerate() {
if i != 0 {
write!(buf, " + ")?;
}
match bound.as_ref() {
TypeBound::Path(path, modifier) => {
match modifier {
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
print_path(db, path, buf)?;
}
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
Ok(())
}