From ef416e0154767619fcbfa0d1682b28bd338a8ce9 Mon Sep 17 00:00:00 2001 From: oxalica Date: Sun, 14 Mar 2021 20:03:39 +0800 Subject: Impl HirDisplay for function hover message --- crates/hir/src/display.rs | 234 ++++++++++++++++++++++++++++++++++++++++++- crates/hir/src/lib.rs | 17 +++- crates/hir_def/src/path.rs | 11 +- crates/hir_ty/src/display.rs | 203 +++++++++++++++++++++++++++++++++++-- crates/ide/src/hover.rs | 10 +- 5 files changed, 461 insertions(+), 14 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 86f48256e..ae08e2584 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -1,9 +1,120 @@ //! HirDisplay implementations for various hir types. +use hir_def::{ + generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, + type_ref::{TypeBound, TypeRef}, + GenericDefId, +}; use hir_ty::display::{ - write_bounds_like_dyn_trait_with_prefix, HirDisplay, HirDisplayError, HirFormatter, + write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, + HirFormatter, }; +use syntax::ast::{self, NameOwner}; + +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, "(")?; -use crate::{Substs, Type, TypeParam}; + 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` 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(()) +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0d0e757fc..476fdb132 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -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 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 { + 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)] diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 0e60dc2b6..8c923bb7b 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -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)] diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index c1062387e..c572bb114 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -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 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` instead of `Foo::`. + // 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 `` 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), + } + } +} diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index f872b68cf..1e4c247c0 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -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(db: &RootDatabase, def: D, mod_path: Option) -> Option + 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(db: &RootDatabase, def: D, mod_path: Option) -> Option where D: HasSource + HasAttrs + Copy, -- cgit v1.2.3