Impl HirDisplay for function hover message
This commit is contained in:
parent
2bb8956a10
commit
ef416e0154
@ -1,9 +1,120 @@
|
||||
//! HirDisplay implementations for various hir types.
|
||||
use hir_ty::display::{
|
||||
write_bounds_like_dyn_trait_with_prefix, HirDisplay, HirDisplayError, HirFormatter,
|
||||
use hir_def::{
|
||||
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
GenericDefId,
|
||||
};
|
||||
use hir_ty::display::{
|
||||
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
|
||||
HirFormatter,
|
||||
};
|
||||
use syntax::ast::{self, NameOwner};
|
||||
|
||||
use crate::{Substs, Type, TypeParam};
|
||||
use crate::{Function, HasVisibility, Substs, Type, TypeParam};
|
||||
|
||||
impl HirDisplay for Function {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
let data = f.db.function_data(self.id);
|
||||
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
|
||||
let qual = &data.qualifier;
|
||||
if qual.is_default {
|
||||
write!(f, "default ")?;
|
||||
}
|
||||
if qual.is_const {
|
||||
write!(f, "const ")?;
|
||||
}
|
||||
if qual.is_async {
|
||||
write!(f, "async ")?;
|
||||
}
|
||||
if qual.is_unsafe {
|
||||
write!(f, "unsafe ")?;
|
||||
}
|
||||
if let Some(abi) = &qual.abi {
|
||||
// FIXME: String escape?
|
||||
write!(f, "extern \"{}\" ", abi)?;
|
||||
}
|
||||
write!(f, "fn {}", data.name)?;
|
||||
|
||||
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
|
||||
|
||||
write!(f, "(")?;
|
||||
|
||||
let write_self_param = |ty: &TypeRef, f: &mut HirFormatter| match ty {
|
||||
TypeRef::Path(p) if p.is_self_type() => write!(f, "self"),
|
||||
TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner,TypeRef::Path(p) if p.is_self_type()) =>
|
||||
{
|
||||
write!(f, "&")?;
|
||||
if let Some(lifetime) = lifetime {
|
||||
write!(f, "{} ", lifetime.name)?;
|
||||
}
|
||||
if let hir_def::type_ref::Mutability::Mut = mut_ {
|
||||
write!(f, "mut ")?;
|
||||
}
|
||||
write!(f, "self")
|
||||
}
|
||||
_ => {
|
||||
write!(f, "self: ")?;
|
||||
ty.hir_fmt(f)
|
||||
}
|
||||
};
|
||||
|
||||
let mut first = true;
|
||||
for (param, type_ref) in self.assoc_fn_params(f.db).into_iter().zip(&data.params) {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
if data.has_self_param {
|
||||
write_self_param(type_ref, f)?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
match param.pattern_source(f.db) {
|
||||
Some(ast::Pat::IdentPat(p)) if p.name().is_some() => {
|
||||
write!(f, "{}: ", p.name().unwrap())?
|
||||
}
|
||||
_ => write!(f, "_: ")?,
|
||||
}
|
||||
// FIXME: Use resolved `param.ty` or raw `type_ref`?
|
||||
// The former will ignore lifetime arguments currently.
|
||||
type_ref.hir_fmt(f)?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
|
||||
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
|
||||
// Use ugly pattern match to strip the Future trait.
|
||||
// Better way?
|
||||
let ret_type = if !qual.is_async {
|
||||
&data.ret_type
|
||||
} else {
|
||||
match &data.ret_type {
|
||||
TypeRef::ImplTrait(bounds) => match &bounds[0] {
|
||||
TypeBound::Path(path) => {
|
||||
path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
|
||||
[0]
|
||||
.type_ref
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
}
|
||||
_ => panic!("Async fn ret_type should be impl Future"),
|
||||
},
|
||||
_ => panic!("Async fn ret_type should be impl Future"),
|
||||
}
|
||||
};
|
||||
|
||||
match ret_type {
|
||||
TypeRef::Tuple(tup) if tup.is_empty() => {}
|
||||
ty => {
|
||||
write!(f, " -> ")?;
|
||||
ty.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
|
||||
write_where_clause(GenericDefId::FunctionId(self.id), f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Type {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
@ -23,3 +134,122 @@ impl HirDisplay for TypeParam {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_generic_params(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
let params = f.db.generic_params(def);
|
||||
if params.lifetimes.is_empty() && params.types.is_empty() && params.consts.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
write!(f, "<")?;
|
||||
|
||||
let mut first = true;
|
||||
let mut delim = |f: &mut HirFormatter| {
|
||||
if first {
|
||||
first = false;
|
||||
Ok(())
|
||||
} else {
|
||||
write!(f, ", ")
|
||||
}
|
||||
};
|
||||
for (_, lifetime) in params.lifetimes.iter() {
|
||||
delim(f)?;
|
||||
write!(f, "{}", lifetime.name)?;
|
||||
}
|
||||
for (_, ty) in params.types.iter() {
|
||||
if ty.provenance != TypeParamProvenance::TypeParamList {
|
||||
continue;
|
||||
}
|
||||
if let Some(name) = &ty.name {
|
||||
delim(f)?;
|
||||
write!(f, "{}", name)?;
|
||||
if let Some(default) = &ty.default {
|
||||
write!(f, " = ")?;
|
||||
default.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_, konst) in params.consts.iter() {
|
||||
delim(f)?;
|
||||
write!(f, "const {}: ", konst.name)?;
|
||||
konst.ty.hir_fmt(f)?;
|
||||
}
|
||||
|
||||
write!(f, ">")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_where_clause(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
let params = f.db.generic_params(def);
|
||||
if params.where_predicates.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter| match target {
|
||||
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
|
||||
WherePredicateTypeTarget::TypeParam(id) => match ¶ms.types[*id].name {
|
||||
Some(name) => write!(f, "{}", name),
|
||||
None => write!(f, "{{unnamed}}"),
|
||||
},
|
||||
};
|
||||
|
||||
write!(f, "\nwhere")?;
|
||||
|
||||
for (pred_idx, pred) in params.where_predicates.iter().enumerate() {
|
||||
let prev_pred =
|
||||
if pred_idx == 0 { None } else { Some(¶ms.where_predicates[pred_idx - 1]) };
|
||||
|
||||
let new_predicate = |f: &mut HirFormatter| {
|
||||
write!(f, "{}", if pred_idx == 0 { "\n " } else { ",\n " })
|
||||
};
|
||||
|
||||
match pred {
|
||||
WherePredicate::TypeBound { target, bound } => {
|
||||
if matches!(prev_pred, Some(WherePredicate::TypeBound { target: target_, .. }) if target_ == target)
|
||||
{
|
||||
write!(f, " + ")?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
write_target(target, f)?;
|
||||
write!(f, ": ")?;
|
||||
}
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target)
|
||||
{
|
||||
write!(f, " + {}", bound.name)?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
write!(f, "{}: {}", target.name, bound.name)?;
|
||||
}
|
||||
}
|
||||
WherePredicate::ForLifetime { lifetimes, target, bound } => {
|
||||
if matches!(
|
||||
prev_pred,
|
||||
Some(WherePredicate::ForLifetime { lifetimes: lifetimes_, target: target_, .. })
|
||||
if lifetimes_ == lifetimes && target_ == target,
|
||||
) {
|
||||
write!(f, " + ")?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
write!(f, "for<")?;
|
||||
for (idx, lifetime) in lifetimes.iter().enumerate() {
|
||||
if idx != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", lifetime)?;
|
||||
}
|
||||
write!(f, "> ")?;
|
||||
write_target(target, f)?;
|
||||
write!(f, ": ")?;
|
||||
}
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of final predicate. There must be at least one predicate here.
|
||||
write!(f, ",")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -822,7 +822,8 @@ impl Function {
|
||||
db.function_data(self.id)
|
||||
.params
|
||||
.iter()
|
||||
.map(|type_ref| {
|
||||
.enumerate()
|
||||
.map(|(idx, type_ref)| {
|
||||
let ty = Type {
|
||||
krate,
|
||||
ty: InEnvironment {
|
||||
@ -830,7 +831,7 @@ impl Function {
|
||||
environment: environment.clone(),
|
||||
},
|
||||
};
|
||||
Param { ty }
|
||||
Param { func: self, ty, idx }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -893,6 +894,9 @@ impl From<hir_ty::Mutability> for Access {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Param {
|
||||
func: Function,
|
||||
/// The index in parameter list, including self parameter.
|
||||
idx: usize,
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
@ -900,6 +904,15 @@ impl Param {
|
||||
pub fn ty(&self) -> &Type {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
|
||||
let params = self.func.source(db)?.value.param_list()?;
|
||||
if params.self_param().is_some() {
|
||||
params.params().nth(self.idx.checked_sub(1)?)?.pat()
|
||||
} else {
|
||||
params.params().nth(self.idx)?.pat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -9,7 +9,10 @@ use std::{
|
||||
|
||||
use crate::{body::LowerCtx, type_ref::LifetimeRef};
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{hygiene::Hygiene, name::Name};
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, Name},
|
||||
};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
@ -209,6 +212,12 @@ impl Path {
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn is_self_type(&self) -> bool {
|
||||
self.type_anchor.is_none()
|
||||
&& self.generic_args == &[None]
|
||||
&& self.mod_path.as_ident() == Some(&name!(Self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -5,7 +5,13 @@ use std::{borrow::Cow, fmt};
|
||||
use arrayvec::ArrayVec;
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs,
|
||||
db::DefDatabase,
|
||||
find_path,
|
||||
generics::TypeParamProvenance,
|
||||
item_scope::ItemInNs,
|
||||
path::{GenericArg, Path, PathKind},
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
visibility::Visibility,
|
||||
AssocContainerId, Lookup, ModuleId, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
@ -232,7 +238,7 @@ where
|
||||
|
||||
const TYPE_HINT_TRUNCATION: &str = "…";
|
||||
|
||||
impl HirDisplay for &Ty {
|
||||
impl<T: HirDisplay> HirDisplay for &'_ T {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
HirDisplay::hir_fmt(*self, f)
|
||||
}
|
||||
@ -761,12 +767,6 @@ impl HirDisplay for TraitRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for &GenericPredicate {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
HirDisplay::hir_fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for GenericPredicate {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
if f.should_truncate() {
|
||||
@ -825,3 +825,190 @@ impl HirDisplay for Obligation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_visibility(
|
||||
module_id: ModuleId,
|
||||
vis: Visibility,
|
||||
f: &mut HirFormatter,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
match vis {
|
||||
Visibility::Public => write!(f, "pub "),
|
||||
Visibility::Module(vis_id) => {
|
||||
let def_map = module_id.def_map(f.db.upcast());
|
||||
let root_module_id = def_map.module_id(def_map.root());
|
||||
if vis_id == module_id {
|
||||
// pub(self) or omitted
|
||||
Ok(())
|
||||
} else if root_module_id == vis_id {
|
||||
write!(f, "pub(crate) ")
|
||||
} else if module_id.containing_module(f.db.upcast()) == Some(vis_id) {
|
||||
write!(f, "pub(super) ")
|
||||
} else {
|
||||
write!(f, "pub(in ...) ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for TypeRef {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
match self {
|
||||
TypeRef::Never => write!(f, "!")?,
|
||||
TypeRef::Placeholder => write!(f, "_")?,
|
||||
TypeRef::Tuple(elems) => {
|
||||
write!(f, "(")?;
|
||||
f.write_joined(elems, ", ")?;
|
||||
if elems.len() == 1 {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
TypeRef::Path(path) => path.hir_fmt(f)?,
|
||||
TypeRef::RawPtr(inner, mutability) => {
|
||||
let mutability = match mutability {
|
||||
hir_def::type_ref::Mutability::Shared => "*const ",
|
||||
hir_def::type_ref::Mutability::Mut => "*mut ",
|
||||
};
|
||||
write!(f, "{}", mutability)?;
|
||||
inner.hir_fmt(f)?;
|
||||
}
|
||||
TypeRef::Reference(inner, lifetime, mutability) => {
|
||||
let mutability = match mutability {
|
||||
hir_def::type_ref::Mutability::Shared => "",
|
||||
hir_def::type_ref::Mutability::Mut => "mut ",
|
||||
};
|
||||
write!(f, "&")?;
|
||||
if let Some(lifetime) = lifetime {
|
||||
write!(f, "{} ", lifetime.name)?;
|
||||
}
|
||||
write!(f, "{}", mutability)?;
|
||||
inner.hir_fmt(f)?;
|
||||
}
|
||||
TypeRef::Array(inner) => {
|
||||
write!(f, "[")?;
|
||||
inner.hir_fmt(f)?;
|
||||
// FIXME: Array length?
|
||||
write!(f, "; _]")?;
|
||||
}
|
||||
TypeRef::Slice(inner) => {
|
||||
write!(f, "[")?;
|
||||
inner.hir_fmt(f)?;
|
||||
write!(f, "]")?;
|
||||
}
|
||||
TypeRef::Fn(tys, is_varargs) => {
|
||||
// FIXME: Function pointer qualifiers.
|
||||
write!(f, "fn(")?;
|
||||
f.write_joined(&tys[..tys.len() - 1], ", ")?;
|
||||
if *is_varargs {
|
||||
write!(f, "{}...", if tys.len() == 1 { "" } else { ", " })?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
let ret_ty = tys.last().unwrap();
|
||||
match ret_ty {
|
||||
TypeRef::Tuple(tup) if tup.is_empty() => {}
|
||||
_ => {
|
||||
write!(f, " -> ")?;
|
||||
ret_ty.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
write!(f, "impl ")?;
|
||||
f.write_joined(bounds, " + ")?;
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
write!(f, "dyn ")?;
|
||||
f.write_joined(bounds, " + ")?;
|
||||
}
|
||||
TypeRef::Error => write!(f, "{{error}}")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for TypeBound {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
match self {
|
||||
TypeBound::Path(path) => path.hir_fmt(f),
|
||||
TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
|
||||
TypeBound::Error => write!(f, "{{error}}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Path {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
match (self.type_anchor(), self.kind()) {
|
||||
(Some(anchor), _) => {
|
||||
write!(f, "<")?;
|
||||
anchor.hir_fmt(f)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
(_, PathKind::Plain) => {}
|
||||
(_, PathKind::Abs) => write!(f, "::")?,
|
||||
(_, PathKind::Crate) => write!(f, "crate")?,
|
||||
(_, PathKind::Super(0)) => write!(f, "self")?,
|
||||
(_, PathKind::Super(n)) => {
|
||||
write!(f, "super")?;
|
||||
for _ in 0..*n {
|
||||
write!(f, "::super")?;
|
||||
}
|
||||
}
|
||||
(_, PathKind::DollarCrate(_)) => write!(f, "{{extern_crate}}")?,
|
||||
}
|
||||
|
||||
for (seg_idx, segment) in self.segments().iter().enumerate() {
|
||||
if seg_idx != 0 {
|
||||
write!(f, "::")?;
|
||||
}
|
||||
write!(f, "{}", segment.name)?;
|
||||
if let Some(generic_args) = segment.args_and_bindings {
|
||||
// We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
|
||||
// Do we actually format expressions?
|
||||
write!(f, "<")?;
|
||||
let mut first = true;
|
||||
for arg in &generic_args.args {
|
||||
if first {
|
||||
first = false;
|
||||
if generic_args.has_self_type {
|
||||
// FIXME: Convert to `<Ty as Trait>` form.
|
||||
write!(f, "Self = ")?;
|
||||
}
|
||||
} else {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
arg.hir_fmt(f)?;
|
||||
}
|
||||
for binding in &generic_args.bindings {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", binding.name)?;
|
||||
match &binding.type_ref {
|
||||
Some(ty) => {
|
||||
write!(f, " = ")?;
|
||||
ty.hir_fmt(f)?
|
||||
}
|
||||
None => {
|
||||
write!(f, ": ")?;
|
||||
f.write_joined(&binding.bounds, " + ")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for GenericArg {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
match self {
|
||||
GenericArg::Type(ty) => ty.hir_fmt(f),
|
||||
GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ fn hover_for_definition(
|
||||
},
|
||||
mod_path,
|
||||
),
|
||||
ModuleDef::Function(it) => from_def_source(db, it, mod_path),
|
||||
ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path),
|
||||
ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path),
|
||||
ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path),
|
||||
ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path),
|
||||
@ -383,6 +383,14 @@ fn hover_for_definition(
|
||||
},
|
||||
};
|
||||
|
||||
fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
|
||||
where
|
||||
D: HasAttrs + HirDisplay,
|
||||
{
|
||||
let label = def.display(db).to_string();
|
||||
from_def_source_labeled(db, def, Some(label), mod_path)
|
||||
}
|
||||
|
||||
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
|
||||
where
|
||||
D: HasSource<Ast = A> + HasAttrs + Copy,
|
||||
|
Loading…
x
Reference in New Issue
Block a user