7900: show function params in completion detail r=matklad a=JoshMcguigan

This resolves #7842 by updating the detail for function completions from `-> T` to `fn(T, U) -> V`. I added an expicit unit test for this, `ide_completion::render::fn_detail_includes_args_and_return_type`, which passes.

Lots of other unit tests fail (~60 of them) due to this change, although I believe the failures are purely cosmetic (they were testing the exact format of this output). I'm happy to go update those tests, but before I do that I'd like to make sure this is in fact the format we want for the detail?

edit - I realized `UPDATE_EXPECT=1 cargo test` automatically updates `expect!` tests. Big 👍 to whoever worked on that! So I'll go ahead and update all these tests soon. But I still would like to confirm `fn(T, U) -> V` is the desired content in the `detail` field. 

8000: Use hir formatter for hover text r=matklad a=oxalica

Fix #2765 , (should) fix #4665

Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>
Co-authored-by: oxalica <oxalicc@pm.me>
This commit is contained in:
bors[bot] 2021-03-16 08:05:24 +00:00 committed by GitHub
commit 1a82af3527
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1058 additions and 212 deletions

441
crates/hir/src/display.rs Normal file
View file

@ -0,0 +1,441 @@
//! HirDisplay implementations for various hir types.
use hir_def::{
adt::VariantData,
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
use hir_ty::display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
HirFormatter,
};
use syntax::ast::{self, NameOwner};
use crate::{
Const, ConstParam, Enum, Field, Function, HasVisibility, Module, Static, Struct, Substs, Trait,
Type, TypeAlias, TypeParam, Union, Variant,
};
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 Struct {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
write!(f, "struct ")?;
write!(f, "{}", self.name(f.db))?;
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
Ok(())
}
}
impl HirDisplay for Enum {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
write!(f, "enum ")?;
write!(f, "{}", self.name(f.db))?;
let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
Ok(())
}
}
impl HirDisplay for Union {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
write!(f, "union ")?;
write!(f, "{}", self.name(f.db))?;
let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
Ok(())
}
}
impl HirDisplay for Field {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
write!(f, "{}: ", self.name(f.db))?;
self.signature_ty(f.db).hir_fmt(f)
}
}
impl HirDisplay for Variant {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write!(f, "{}", self.name(f.db))?;
let data = self.variant_data(f.db);
match &*data {
VariantData::Unit => {}
VariantData::Tuple(fields) => {
write!(f, "(")?;
let mut first = true;
for (_, field) in fields.iter() {
if first {
first = false;
} else {
write!(f, ", ")?;
}
// Enum variant fields must be pub.
field.type_ref.hir_fmt(f)?;
}
write!(f, ")")?;
}
VariantData::Record(fields) => {
write!(f, " {{")?;
let mut first = true;
for (_, field) in fields.iter() {
if first {
first = false;
write!(f, " ")?;
} else {
write!(f, ", ")?;
}
// Enum variant fields must be pub.
write!(f, "{}: ", field.name)?;
field.type_ref.hir_fmt(f)?;
}
write!(f, " }}")?;
}
}
Ok(())
}
}
impl HirDisplay for Type {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
self.ty.value.hir_fmt(f)
}
}
impl HirDisplay for TypeParam {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write!(f, "{}", self.name(f.db))?;
let bounds = f.db.generic_predicates_for_param(self.id);
let substs = Substs::type_params(f.db, self.id.parent);
let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
if !(predicates.is_empty() || f.omit_verbose_types()) {
write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
}
Ok(())
}
}
impl HirDisplay for ConstParam {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write!(f, "const {}: ", self.name(f.db))?;
self.ty(f.db).hir_fmt(f)
}
}
fn write_generic_params(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
let params = f.db.generic_params(def);
if params.lifetimes.is_empty()
&& params.consts.is_empty()
&& params
.types
.iter()
.all(|(_, param)| !matches!(param.provenance, TypeParamProvenance::TypeParamList))
{
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 &params.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(&params.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(())
}
impl HirDisplay for Const {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.const_data(self.id);
write!(f, "const ")?;
match &data.name {
Some(name) => write!(f, "{}: ", name)?,
None => write!(f, "_: ")?,
}
data.type_ref.hir_fmt(f)?;
Ok(())
}
}
impl HirDisplay for Static {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.static_data(self.id);
write!(f, "static ")?;
if data.mutable {
write!(f, "mut ")?;
}
match &data.name {
Some(name) => write!(f, "{}: ", name)?,
None => write!(f, "_: ")?,
}
data.type_ref.hir_fmt(f)?;
Ok(())
}
}
impl HirDisplay for Trait {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.trait_data(self.id);
if data.is_unsafe {
write!(f, "unsafe ")?;
}
if data.is_auto {
write!(f, "auto ")?;
}
write!(f, "trait {}", data.name)?;
let def_id = GenericDefId::TraitId(self.id);
write_generic_params(def_id, f)?;
if !data.bounds.is_empty() {
write!(f, ": ")?;
f.write_joined(&*data.bounds, " + ")?;
}
write_where_clause(def_id, f)?;
Ok(())
}
}
impl HirDisplay for TypeAlias {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.type_alias_data(self.id);
write!(f, "type {}", data.name)?;
if !data.bounds.is_empty() {
write!(f, ": ")?;
f.write_joined(&data.bounds, " + ")?;
}
if let Some(ty) = &data.type_ref {
write!(f, " = ")?;
ty.hir_fmt(f)?;
}
Ok(())
}
}
impl HirDisplay for Module {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
// FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) {
Some(name) => write!(f, "mod {}", name),
None if self.crate_root(f.db) == *self => match self.krate().display_name(f.db) {
Some(name) => write!(f, "extern crate {}", name),
None => write!(f, "extern crate {{unknown}}"),
},
None => write!(f, "mod {{unnamed}}"),
}
}
}

View file

@ -29,6 +29,8 @@ mod has_source;
pub mod diagnostics;
pub mod db;
mod display;
use std::{iter, sync::Arc};
use arrayvec::ArrayVec;
@ -50,7 +52,6 @@ use hir_def::{
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
use hir_ty::{
autoderef,
display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
method_resolution::{self, TyFingerprint},
primitive::UintTy,
to_assoc_type_id,
@ -572,6 +573,12 @@ impl Struct {
}
}
impl HasVisibility for Struct {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
db.struct_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Union {
pub(crate) id: UnionId,
@ -604,6 +611,12 @@ impl Union {
}
}
impl HasVisibility for Union {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
db.union_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Enum {
pub(crate) id: EnumId,
@ -631,6 +644,12 @@ impl Enum {
}
}
impl HasVisibility for Enum {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
db.enum_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Variant {
pub(crate) parent: Enum,
@ -822,7 +841,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 +850,7 @@ impl Function {
environment: environment.clone(),
},
};
Param { ty }
Param { func: self, ty, idx }
})
.collect()
}
@ -844,7 +864,7 @@ impl Function {
}
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).is_unsafe
db.function_data(self.id).qualifier.is_unsafe
}
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
@ -893,6 +913,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 +923,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)]
@ -922,6 +954,14 @@ impl SelfParam {
})
.unwrap_or(Access::Owned)
}
pub fn display(self, db: &dyn HirDatabase) -> &'static str {
match self.access(db) {
Access::Shared => "&self",
Access::Exclusive => "&mut self",
Access::Owned => "self",
}
}
}
impl HasVisibility for Function {
@ -949,6 +989,10 @@ impl Const {
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
db.const_data(self.id).name.clone()
}
pub fn type_ref(self, db: &dyn HirDatabase) -> TypeRef {
db.const_data(self.id).type_ref.clone()
}
}
impl HasVisibility for Const {
@ -982,6 +1026,12 @@ impl Static {
}
}
impl HasVisibility for Static {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
db.static_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Trait {
pub(crate) id: TraitId,
@ -1001,7 +1051,13 @@ impl Trait {
}
pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
db.trait_data(self.id).auto
db.trait_data(self.id).is_auto
}
}
impl HasVisibility for Trait {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
db.trait_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
}
}
@ -1413,19 +1469,6 @@ impl TypeParam {
}
}
impl HirDisplay for TypeParam {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write!(f, "{}", self.name(f.db))?;
let bounds = f.db.generic_predicates_for_param(self.id);
let substs = Substs::type_params(f.db, self.id.parent);
let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
if !(predicates.is_empty() || f.omit_verbose_types()) {
write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LifetimeParam {
pub(crate) id: LifetimeParamId,
@ -2059,12 +2102,6 @@ impl Type {
}
}
impl HirDisplay for Type {
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
self.ty.value.hir_fmt(f)
}
}
// FIXME: closures
#[derive(Debug)]
pub struct Callable {

View file

@ -31,12 +31,14 @@ pub struct StructData {
pub name: Name,
pub variant_data: Arc<VariantData>,
pub repr: Option<ReprKind>,
pub visibility: RawVisibility,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
pub name: Name,
pub variants: Arena<EnumVariantData>,
pub visibility: RawVisibility,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -102,6 +104,7 @@ impl StructData {
name: strukt.name.clone(),
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[strukt.visibility].clone(),
})
}
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
@ -118,6 +121,7 @@ impl StructData {
name: union.name.clone(),
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[union.visibility].clone(),
})
}
}
@ -150,7 +154,11 @@ impl EnumData {
}
}
Arc::new(EnumData { name: enum_.name.clone(), variants })
Arc::new(EnumData {
name: enum_.name.clone(),
variants,
visibility: item_tree[enum_.visibility].clone(),
})
}
pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {

View file

@ -9,7 +9,7 @@ use crate::{
attr::Attrs,
body::Expander,
db::DefDatabase,
item_tree::{AssocItem, ItemTreeId, ModItem},
item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem},
type_ref::{TypeBound, TypeRef},
visibility::RawVisibility,
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@ -26,9 +26,9 @@ pub struct FunctionData {
/// can be called as a method.
pub has_self_param: bool,
pub has_body: bool,
pub is_unsafe: bool,
pub qualifier: FunctionQualifier,
pub is_in_extern_block: bool,
pub is_varargs: bool,
pub is_extern: bool,
pub visibility: RawVisibility,
}
@ -46,9 +46,9 @@ impl FunctionData {
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
has_self_param: func.has_self_param,
has_body: func.has_body,
is_unsafe: func.is_unsafe,
qualifier: func.qualifier.clone(),
is_in_extern_block: func.is_in_extern_block,
is_varargs: func.is_varargs,
is_extern: func.is_extern,
visibility: item_tree[func.visibility].clone(),
})
}
@ -87,7 +87,10 @@ impl TypeAliasData {
pub struct TraitData {
pub name: Name,
pub items: Vec<(Name, AssocItemId)>,
pub auto: bool,
pub is_auto: bool,
pub is_unsafe: bool,
pub visibility: RawVisibility,
pub bounds: Box<[TypeBound]>,
}
impl TraitData {
@ -96,10 +99,13 @@ impl TraitData {
let item_tree = db.item_tree(tr_loc.id.file_id);
let tr_def = &item_tree[tr_loc.id.value];
let name = tr_def.name.clone();
let auto = tr_def.auto;
let is_auto = tr_def.is_auto;
let is_unsafe = tr_def.is_unsafe;
let module_id = tr_loc.container;
let container = AssocContainerId::TraitId(tr);
let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
let visibility = item_tree[tr_def.visibility].clone();
let bounds = tr_def.bounds.clone();
let items = collect_items(
db,
@ -111,7 +117,7 @@ impl TraitData {
100,
);
Arc::new(TraitData { name, items, auto })
Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility, bounds })
}
pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {

View file

@ -24,7 +24,7 @@ use la_arena::{Arena, Idx, RawIdx};
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{ast, match_ast, SyntaxKind};
use syntax::{ast, match_ast, SmolStr, SyntaxKind};
use crate::{
attr::{Attrs, RawAttrs},
@ -556,16 +556,25 @@ pub struct Function {
pub generic_params: GenericParamsId,
pub has_self_param: bool,
pub has_body: bool,
pub is_unsafe: bool,
pub qualifier: FunctionQualifier,
/// Whether the function is located in an `extern` block (*not* whether it is an
/// `extern "abi" fn`).
pub is_extern: bool,
pub is_in_extern_block: bool,
pub params: Box<[Idx<TypeRef>]>,
pub is_varargs: bool,
pub ret_type: Idx<TypeRef>,
pub ast_id: FileAstId<ast::Fn>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionQualifier {
pub is_default: bool,
pub is_const: bool,
pub is_async: bool,
pub is_unsafe: bool,
pub abi: Option<SmolStr>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Struct {
pub name: Name,
@ -629,7 +638,9 @@ pub struct Trait {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub auto: bool,
pub is_auto: bool,
pub is_unsafe: bool,
pub bounds: Box<[TypeBound]>,
pub items: Box<[AssocItem]>,
pub ast_id: FileAstId<ast::Trait>,
}

View file

@ -391,14 +391,33 @@ impl Ctx {
let has_body = func.body().is_some();
let ast_id = self.source_ast_id_map.ast_id(func);
let qualifier = FunctionQualifier {
is_default: func.default_token().is_some(),
is_const: func.const_token().is_some(),
is_async: func.async_token().is_some(),
is_unsafe: func.unsafe_token().is_some(),
abi: func.abi().map(|abi| {
// FIXME: Abi::abi() -> Option<SyntaxToken>?
match abi.syntax().last_token() {
Some(tok) if tok.kind() == SyntaxKind::STRING => {
// FIXME: Better way to unescape?
tok.text().trim_matches('"').into()
}
_ => {
// `extern` default to be `extern "C"`.
"C".into()
}
}
}),
};
let mut res = Function {
name,
visibility,
generic_params: GenericParamsId::EMPTY,
has_self_param,
has_body,
is_unsafe: func.unsafe_token().is_some(),
is_extern: false,
qualifier,
is_in_extern_block: false,
params,
is_varargs,
ret_type,
@ -481,7 +500,9 @@ impl Ctx {
let visibility = self.lower_visibility(trait_def);
let generic_params =
self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
let auto = trait_def.auto_token().is_some();
let is_auto = trait_def.auto_token().is_some();
let is_unsafe = trait_def.unsafe_token().is_some();
let bounds = self.lower_type_bounds(trait_def);
let items = trait_def.assoc_item_list().map(|list| {
self.with_inherited_visibility(visibility, |this| {
list.assoc_items()
@ -501,7 +522,9 @@ impl Ctx {
name,
visibility,
generic_params,
auto,
is_auto,
is_unsafe,
bounds: bounds.into(),
items: items.unwrap_or_default(),
ast_id,
};
@ -608,8 +631,8 @@ impl Ctx {
ast::ExternItem::Fn(ast) => {
let func_id = self.lower_function(&ast)?;
let func = &mut self.data().functions[func_id.index];
func.is_unsafe = is_intrinsic_fn_unsafe(&func.name);
func.is_extern = true;
func.qualifier.is_unsafe = is_intrinsic_fn_unsafe(&func.name);
func.is_in_extern_block = true;
func_id.into()
}
ast::ExternItem::Static(ast) => {

View file

@ -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)]

View file

@ -91,7 +91,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
fn validate_func(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
if data.is_extern {
if data.is_in_extern_block {
cov_mark::hit!(extern_func_incorrect_case_ignored);
return;
}

View file

@ -32,7 +32,7 @@ impl<'a, 'b> UnsafeValidator<'a, 'b> {
let def = self.owner.into();
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
let is_unsafe = match self.owner {
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe,
DefWithBodyId::FunctionId(it) => db.function_data(it).qualifier.is_unsafe,
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
};
if is_unsafe
@ -86,7 +86,7 @@ fn walk_unsafe(
match expr {
&Expr::Call { callee, .. } => {
if let Some(func) = infer[callee].as_fn_def(db) {
if db.function_data(func).is_unsafe {
if db.function_data(func).qualifier.is_unsafe {
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
}
}
@ -103,7 +103,7 @@ fn walk_unsafe(
Expr::MethodCall { .. } => {
if infer
.method_resolution(current)
.map(|func| db.function_data(func).is_unsafe)
.map(|func| db.function_data(func).qualifier.is_unsafe)
.unwrap_or(false)
{
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });

View file

@ -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),
}
}
}

View file

@ -429,7 +429,7 @@ pub(crate) fn trait_datum_query(
let generic_params = generics(db.upcast(), trait_.into());
let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
let flags = rust_ir::TraitFlags {
auto: trait_data.auto,
auto: trait_data.is_auto,
upstream: trait_.lookup(db.upcast()).container.krate() != krate,
non_enumerable: true,
coinductive: false, // only relevant for Chalk testing

View file

@ -5,6 +5,5 @@ pub(crate) mod navigation_target;
mod short_label;
pub(crate) use navigation_target::{ToNav, TryToNav};
pub(crate) use short_label::ShortLabel;
pub(crate) use syntax::display::{function_declaration, macro_label};

View file

@ -3,7 +3,9 @@
use std::fmt;
use either::Either;
use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
use hir::{
AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, InFile, ModuleSource,
};
use ide_db::{
base_db::{FileId, FileRange, SourceDatabase},
symbol_index::FileSymbolKind,
@ -98,7 +100,7 @@ impl NavigationTarget {
SymbolKind::Module,
);
res.docs = module.attrs(db).docs();
res.description = src.value.short_label();
res.description = Some(module.display(db).to_string());
return res;
}
module.to_nav(db)
@ -251,8 +253,8 @@ impl ToNavFromAst for hir::Trait {
impl<D> TryToNav for D
where
D: HasSource + ToNavFromAst + Copy + HasAttrs,
D::Ast: ast::NameOwner + ShortLabel,
D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay,
D::Ast: ast::NameOwner,
{
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
let src = self.source(db)?;
@ -262,7 +264,7 @@ where
D::KIND,
);
res.docs = self.docs(db);
res.description = src.value.short_label();
res.description = Some(self.display(db).to_string());
Some(res)
}
}
@ -317,7 +319,7 @@ impl TryToNav for hir::Field {
let mut res =
NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
res.docs = self.docs(db);
res.description = it.short_label();
res.description = Some(self.display(db).to_string());
res
}
FieldSource::Pos(it) => {

View file

@ -1,7 +1,7 @@
use either::Either;
use hir::{
Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
Adt, AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, Module,
ModuleDef, Semantics,
};
use ide_db::{
base_db::SourceDatabase,
@ -14,7 +14,7 @@ use stdx::format_to;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
use crate::{
display::{macro_label, ShortLabel, TryToNav},
display::{macro_label, TryToNav},
doc_links::{remove_links, rewrite_links},
markdown_remove::remove_markdown,
markup::Markup,
@ -335,34 +335,18 @@ fn hover_for_definition(
let label = macro_label(&it.source(db)?.value);
from_def_source_labeled(db, it, Some(label), mod_path)
}
Definition::Field(def) => {
let src = def.source(db)?.value;
if let FieldSource::Named(it) = src {
from_def_source_labeled(db, def, it.short_label(), mod_path)
} else {
None
}
}
Definition::Field(def) => from_hir_fmt(db, def, mod_path),
Definition::ModuleDef(it) => match it {
ModuleDef::Module(it) => from_def_source_labeled(
db,
it,
match it.definition_source(db).value {
ModuleSource::Module(it) => it.short_label(),
ModuleSource::SourceFile(it) => it.short_label(),
ModuleSource::BlockExpr(it) => it.short_label(),
},
mod_path,
),
ModuleDef::Function(it) => from_def_source(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),
ModuleDef::Variant(it) => from_def_source(db, it, mod_path),
ModuleDef::Const(it) => from_def_source(db, it, mod_path),
ModuleDef::Static(it) => from_def_source(db, it, mod_path),
ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
ModuleDef::Module(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::Adt(Adt::Struct(it)) => from_hir_fmt(db, it, mod_path),
ModuleDef::Adt(Adt::Union(it)) => from_hir_fmt(db, it, mod_path),
ModuleDef::Adt(Adt::Enum(it)) => from_hir_fmt(db, it, mod_path),
ModuleDef::Variant(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::Const(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::Static(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::Trait(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::TypeAlias(it) => from_hir_fmt(db, it, mod_path),
ModuleDef::BuiltinType(it) => famous_defs
.and_then(|fd| hover_for_builtin(fd, it))
.or_else(|| Some(Markup::fenced_block(&it.name()))),
@ -370,26 +354,25 @@ fn hover_for_definition(
Definition::Local(it) => hover_for_local(it, db),
Definition::SelfType(impl_def) => {
impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
Adt::Struct(it) => from_def_source(db, it, mod_path),
Adt::Union(it) => from_def_source(db, it, mod_path),
Adt::Enum(it) => from_def_source(db, it, mod_path),
Adt::Struct(it) => from_hir_fmt(db, it, mod_path),
Adt::Union(it) => from_hir_fmt(db, it, mod_path),
Adt::Enum(it) => from_hir_fmt(db, it, mod_path),
})
}
Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
Definition::GenericParam(it) => match it {
GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
GenericParam::ConstParam(it) => from_def_source(db, it, None),
GenericParam::ConstParam(it) => Some(Markup::fenced_block(&it.display(db))),
},
};
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
where
D: HasSource<Ast = A> + HasAttrs + Copy,
A: ShortLabel,
D: HasAttrs + HirDisplay,
{
let short_label = def.source(db)?.value.short_label();
from_def_source_labeled(db, def, short_label, mod_path)
let label = def.display(db).to_string();
from_def_source_labeled(db, def, Some(label), mod_path)
}
fn from_def_source_labeled<D>(
@ -670,7 +653,9 @@ fn main() { let foo_test = fo$0o(); }
```
```rust
pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str
pub fn foo<'a, T>(b: &'a T) -> &'a str
where
T: AsRef<str>,
```
"#]],
);
@ -878,7 +863,7 @@ fn main() { So$0me(12); }
```
```rust
Some
Some(T)
```
"#]],
);
@ -944,7 +929,7 @@ fn main() {
```
```rust
Some
Some(T)
```
---
@ -1441,13 +1426,14 @@ fn bar() { fo$0o(); }
```
"#]],
);
// Top level `pub(crate)` will be displayed as no visibility.
check(
r#"pub(crate) async unsafe extern "C" fn foo$0() {}"#,
r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
expect![[r#"
*foo*
```rust
test
test::m
```
```rust
@ -1489,11 +1475,18 @@ extern crate st$0d;
//! abc123
"#,
expect![[r#"
*std*
Standard library for this test
*std*
Printed?
abc123
```rust
extern crate std
```
---
Standard library for this test
Printed?
abc123
"#]],
);
check(
@ -1507,11 +1500,18 @@ extern crate std as ab$0c;
//! abc123
"#,
expect![[r#"
*abc*
Standard library for this test
*abc*
Printed?
abc123
```rust
extern crate std
```
---
Standard library for this test
Printed?
abc123
"#]],
);
}
@ -2021,7 +2021,7 @@ enum E {
```
```rust
V
V { field: i32 }
```
---
@ -2417,7 +2417,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
focus_range: 24..25,
name: "S",
kind: Struct,
description: "struct S",
description: "struct S<T>",
},
},
HoverGotoTypeData {
@ -2463,7 +2463,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
focus_range: 24..25,
name: "S",
kind: Struct,
description: "struct S",
description: "struct S<T>",
},
},
HoverGotoTypeData {
@ -2605,7 +2605,7 @@ fn main() { let s$0t = foo(); }
focus_range: 6..9,
name: "Foo",
kind: Trait,
description: "trait Foo",
description: "trait Foo<T>",
},
},
HoverGotoTypeData {
@ -2702,7 +2702,7 @@ fn main() { let s$0t = foo(); }
focus_range: 6..9,
name: "Foo",
kind: Trait,
description: "trait Foo",
description: "trait Foo<T>",
},
},
HoverGotoTypeData {
@ -2715,7 +2715,7 @@ fn main() { let s$0t = foo(); }
focus_range: 22..25,
name: "Bar",
kind: Trait,
description: "trait Bar",
description: "trait Bar<T>",
},
},
HoverGotoTypeData {
@ -2819,7 +2819,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
focus_range: 19..22,
name: "Bar",
kind: Trait,
description: "trait Bar",
description: "trait Bar<T>",
},
},
HoverGotoTypeData {
@ -2916,7 +2916,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
focus_range: 6..9,
name: "Foo",
kind: Trait,
description: "trait Foo",
description: "trait Foo<T>",
},
},
HoverGotoTypeData {
@ -2966,7 +2966,7 @@ fn main() { let s$0t = foo(); }
focus_range: 49..50,
name: "B",
kind: Struct,
description: "struct B",
description: "struct B<T>",
},
},
HoverGotoTypeData {
@ -3042,7 +3042,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
focus_range: 6..9,
name: "Foo",
kind: Trait,
description: "trait Foo",
description: "trait Foo<T>",
},
},
HoverGotoTypeData {
@ -3090,7 +3090,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
focus_range: 6..15,
name: "ImplTrait",
kind: Trait,
description: "trait ImplTrait",
description: "trait ImplTrait<T>",
},
},
HoverGotoTypeData {
@ -3103,7 +3103,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
focus_range: 50..51,
name: "B",
kind: Struct,
description: "struct B",
description: "struct B<T>",
},
},
HoverGotoTypeData {
@ -3116,7 +3116,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
focus_range: 28..36,
name: "DynTrait",
kind: Trait,
description: "trait DynTrait",
description: "trait DynTrait<T>",
},
},
HoverGotoTypeData {
@ -3582,6 +3582,17 @@ mod foo$0;
"#,
expect![[r#"
*foo*
```rust
test
```
```rust
mod foo
```
---
For the horde!
"#]],
);
@ -3606,7 +3617,7 @@ use foo::bar::{self$0};
```
```rust
pub mod bar
mod bar
```
---
@ -3657,4 +3668,43 @@ cosnt _: &str$0 = ""; }"#;
"#]],
);
}
#[test]
fn hover_macro_expanded_function() {
check(
r#"
struct S<'a, T>(&'a T);
trait Clone {}
macro_rules! foo {
() => {
fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
't: 't + 't,
for<'a> T: Clone + 'a
{ 0 as _ }
};
}
foo!();
fn main() {
bar$0;
}
"#,
expect![[r#"
*bar*
```rust
test
```
```rust
fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
where
T: Clone + 't,
't: 't + 't,
for<'a> T: Clone + 'a,
```
"#]],
)
}
}

View file

@ -81,7 +81,7 @@ fn foo(s: S) { s.$0 }
"#,
expect![[r#"
fd foo u32
me bar() -> ()
me bar() fn(&self)
"#]],
);
}
@ -97,7 +97,7 @@ impl S {
"#,
expect![[r#"
fd the_field (u32,)
me foo() -> ()
me foo() fn(self)
"#]],
)
}
@ -113,7 +113,7 @@ impl A {
"#,
expect![[r#"
fd the_field (u32, i32)
me foo() -> ()
me foo() fn(&self)
"#]],
)
}
@ -163,7 +163,7 @@ mod m {
fn foo(a: A) { a.$0 }
"#,
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
);
}
@ -196,7 +196,7 @@ impl A<i32> {
fn foo(a: A<u32>) { a.$0 }
"#,
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
)
}
@ -211,7 +211,7 @@ impl Trait for A {}
fn foo(a: A) { a.$0 }
"#,
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
);
}
@ -226,7 +226,7 @@ impl<T> Trait for T {}
fn foo(a: &A) { a.$0 }
",
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
);
}
@ -244,7 +244,7 @@ impl Trait for A {}
fn foo(a: A) { a.$0 }
",
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
);
}
@ -298,7 +298,7 @@ impl T {
}
"#,
expect![[r#"
me blah() -> ()
me blah() fn(&self)
"#]],
);
}
@ -407,7 +407,7 @@ fn foo() {
}
"#,
expect![[r#"
me the_method() -> ()
me the_method() fn(&self)
"#]],
);
}
@ -422,7 +422,7 @@ macro_rules! make_s { () => { S }; }
fn main() { make_s!().f$0; }
"#,
expect![[r#"
me foo() -> ()
me foo() fn(&self)
"#]],
)
}
@ -450,7 +450,7 @@ mod foo {
}
"#,
expect![[r#"
me private() -> ()
me private() fn(&self)
"#]],
);
}

View file

@ -402,7 +402,7 @@ fn main() {
check(
fixture,
expect![[r#"
fn weird_function() (dep::test_mod::TestTrait) -> ()
fn weird_function() (dep::test_mod::TestTrait) fn()
"#]],
);
@ -495,7 +495,7 @@ fn main() {
check(
fixture,
expect![[r#"
me random_method() (dep::test_mod::TestTrait) -> ()
me random_method() (dep::test_mod::TestTrait) fn(&self)
"#]],
);
@ -665,7 +665,7 @@ fn main() {
}
"#,
expect![[r#"
me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
me random_method() (dep::test_mod::TestTrait) fn(&self) DEPRECATED
"#]],
);
@ -696,7 +696,7 @@ fn main() {
"#,
expect![[r#"
ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
fn weird_function() (dep::test_mod::TestTrait) fn() DEPRECATED
"#]],
);
}

View file

@ -359,8 +359,8 @@ impl S {
fn foo() { let _ = S::$0 }
"#,
expect![[r#"
fn a() -> ()
me b() -> ()
fn a() fn()
me b() fn(&self)
ct C const C: i32 = 42;
ta T type T = i32;
"#]],
@ -387,7 +387,7 @@ mod m {
fn foo() { let _ = S::$0 }
"#,
expect![[r#"
fn public_method() -> ()
fn public_method() fn()
ct PUBLIC_CONST pub(crate) const PUBLIC_CONST: u32 = 1;
ta PublicType pub(crate) type PublicType = u32;
"#]],
@ -404,7 +404,7 @@ impl E { fn m() { } }
fn foo() { let _ = E::$0 }
"#,
expect![[r#"
fn m() -> ()
fn m() fn()
"#]],
);
}
@ -419,7 +419,7 @@ impl U { fn m() { } }
fn foo() { let _ = U::$0 }
"#,
expect![[r#"
fn m() -> ()
fn m() fn()
"#]],
);
}
@ -449,7 +449,7 @@ trait Trait { fn m(); }
fn foo() { let _ = Trait::$0 }
"#,
expect![[r#"
fn m() -> ()
fn m() fn()
"#]],
);
}
@ -466,7 +466,7 @@ impl Trait for S {}
fn foo() { let _ = S::$0 }
"#,
expect![[r#"
fn m() -> ()
fn m() fn()
"#]],
);
}
@ -483,7 +483,7 @@ impl Trait for S {}
fn foo() { let _ = <S as Trait>::$0 }
"#,
expect![[r#"
fn m() -> ()
fn m() fn()
"#]],
);
}
@ -512,11 +512,11 @@ fn foo<T: Sub>() { T::$0 }
ta SubTy type SubTy;
ta Ty type Ty;
ct C2 const C2: ();
fn subfunc() -> ()
me submethod() -> ()
fn subfunc() fn()
me submethod() fn(&self)
ct CONST const CONST: u8;
fn func() -> ()
me method() -> ()
fn func() fn()
me method() fn(&self)
"#]],
);
}
@ -552,11 +552,11 @@ impl<T> Sub for Wrap<T> {
ta SubTy type SubTy;
ta Ty type Ty;
ct CONST const CONST: u8 = 0;
fn func() -> ()
me method() -> ()
fn func() fn()
me method() fn(&self)
ct C2 const C2: () = ();
fn subfunc() -> ()
me submethod() -> ()
fn subfunc() fn()
me submethod() fn(&self)
"#]],
);
}
@ -573,8 +573,8 @@ impl T { fn bar() {} }
fn main() { T::$0; }
"#,
expect![[r#"
fn foo() -> ()
fn bar() -> ()
fn foo() fn()
fn bar() fn()
"#]],
);
}
@ -589,7 +589,7 @@ macro_rules! foo { () => {} }
fn main() { let _ = crate::$0 }
"#,
expect![[r##"
fn main() -> ()
fn main() fn()
ma foo!() #[macro_export] macro_rules! foo
"##]],
);
@ -633,7 +633,7 @@ mod p {
"#,
expect![[r#"
ct RIGHT_CONST
fn right_fn() -> ()
fn right_fn() fn()
st RightType
"#]],
);
@ -680,8 +680,8 @@ fn main() { m!(self::f$0); }
fn foo() {}
"#,
expect![[r#"
fn main() -> ()
fn foo() -> ()
fn main() fn()
fn foo() fn()
"#]],
);
}
@ -699,7 +699,7 @@ mod m {
"#,
expect![[r#"
md z
fn z() -> ()
fn z() fn()
"#]],
);
}
@ -719,7 +719,7 @@ fn foo() {
}
"#,
expect![[r#"
fn new() -> HashMap<K, V, RandomState>
fn new() fn() -> HashMap<K, V, RandomState>
"#]],
);
}
@ -752,8 +752,8 @@ fn main() {
}
"#,
expect![[r#"
fn main() -> ()
fn foo() -> ()
fn main() fn()
fn foo() fn(i32, i32)
"#]],
);
}
@ -776,7 +776,7 @@ impl Foo {
expect![[r#"
ev Bar ()
ev Baz ()
me foo() -> ()
me foo() fn(self)
"#]],
);
}
@ -800,7 +800,7 @@ impl u8 {
"#,
expect![[r#"
ct MAX pub const MAX: Self = 255;
me func() -> ()
me func() fn(self)
"#]],
);
}

View file

@ -135,7 +135,7 @@ fn quux(x: i32) {
expect![[r#"
lc y i32
lc x i32
fn quux() -> ()
fn quux() fn(i32)
"#]],
);
}
@ -157,7 +157,7 @@ fn quux() {
expect![[r#"
lc b i32
lc a
fn quux() -> ()
fn quux() fn()
"#]],
);
}
@ -172,7 +172,7 @@ fn quux() {
"#,
expect![[r#"
lc x
fn quux() -> ()
fn quux() fn()
"#]],
);
}
@ -203,14 +203,14 @@ fn main() {
r#"fn quux<T>() { $0 }"#,
expect![[r#"
tp T
fn quux() -> ()
fn quux() fn()
"#]],
);
check(
r#"fn quux<const C: usize>() { $0 }"#,
expect![[r#"
cp C
fn quux() -> ()
fn quux() fn()
"#]],
);
}
@ -221,7 +221,7 @@ fn main() {
check(
r#"fn quux<'a>() { $0 }"#,
expect![[r#"
fn quux() -> ()
fn quux() fn()
"#]],
);
}
@ -259,7 +259,7 @@ fn quux() { $0 }
"#,
expect![[r#"
st S
fn quux() -> ()
fn quux() fn()
en E
"#]],
);
@ -312,7 +312,7 @@ mod m {
}
"#,
expect![[r#"
fn quux() -> ()
fn quux() fn()
st Bar
"#]],
);
@ -327,7 +327,7 @@ fn x() -> $0
"#,
expect![[r#"
st Foo
fn x() -> ()
fn x() fn()
"#]],
);
}
@ -348,7 +348,7 @@ fn foo() {
expect![[r#"
lc bar i32
lc bar i32
fn foo() -> ()
fn foo() fn()
"#]],
);
}
@ -378,7 +378,7 @@ use prelude::*;
mod prelude { struct Option; }
"#,
expect![[r#"
fn foo() -> ()
fn foo() fn()
md std
st Option
"#]],
@ -408,7 +408,7 @@ mod macros {
}
"#,
expect![[r##"
fn f() -> ()
fn f() fn()
ma concat!() #[macro_export] macro_rules! concat
md std
"##]],
@ -435,7 +435,7 @@ use prelude::*;
mod prelude { struct String; }
"#,
expect![[r#"
fn foo() -> ()
fn foo() fn()
md std
md core
st String
@ -466,7 +466,7 @@ fn main() { let v = $0 }
expect![[r##"
md m1
ma baz!() #[macro_export] macro_rules! baz
fn main() -> ()
fn main() fn()
md m2
ma bar!() macro_rules! bar
ma foo!() macro_rules! foo
@ -482,7 +482,7 @@ macro_rules! foo { () => {} }
fn foo() { $0 }
"#,
expect![[r#"
fn foo() -> ()
fn foo() fn()
ma foo!() macro_rules! foo
"#]],
);
@ -496,7 +496,7 @@ macro_rules! foo { () => {} }
fn main() { let x: $0 }
"#,
expect![[r#"
fn main() -> ()
fn main() fn()
ma foo!() macro_rules! foo
"#]],
);
@ -510,7 +510,7 @@ macro_rules! foo { () => {} }
fn main() { $0 }
"#,
expect![[r#"
fn main() -> ()
fn main() fn()
ma foo!() macro_rules! foo
"#]],
);
@ -526,8 +526,8 @@ fn main() {
}
"#,
expect![[r#"
fn frobnicate() -> ()
fn main() -> ()
fn frobnicate() fn()
fn main() fn()
"#]],
);
}
@ -545,7 +545,7 @@ fn quux(x: i32) {
expect![[r#"
lc y i32
lc x i32
fn quux() -> ()
fn quux() fn(i32)
ma m!() macro_rules! m
"#]],
);
@ -564,7 +564,7 @@ fn quux(x: i32) {
expect![[r#"
lc y i32
lc x i32
fn quux() -> ()
fn quux() fn(i32)
ma m!() macro_rules! m
"#]],
);
@ -583,7 +583,7 @@ fn quux(x: i32) {
expect![[r#"
lc y i32
lc x i32
fn quux() -> ()
fn quux() fn(i32)
ma m!() macro_rules! m
"#]],
);
@ -598,7 +598,7 @@ use spam::Quux;
fn main() { $0 }
"#,
expect![[r#"
fn main() -> ()
fn main() fn()
?? Quux
"#]],
);
@ -616,7 +616,7 @@ fn main() { let foo: Foo = Q$0 }
ev Foo::Baz ()
ev Foo::Quux ()
en Foo
fn main() -> ()
fn main() fn()
"#]],
)
}
@ -631,7 +631,7 @@ fn f() -> m::E { V$0 }
expect![[r#"
ev m::E::V ()
md m
fn f() -> E
fn f() fn() -> E
"#]],
)
}

View file

@ -230,7 +230,7 @@ fn foo() {
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "-> ()", documentation: "Do the foo" },
DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
);
}
@ -255,7 +255,7 @@ fn foo() {
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "-> ()", documentation: " Do the foo" },
DetailAndDocumentation { detail: "fn(&self)", documentation: " Do the foo" },
);
}
@ -273,7 +273,7 @@ fn bar() {
for c in fo$0
}
"#,
DetailAndDocumentation { detail: "-> &str", documentation: "Do the foo" },
DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" },
);
}
}

View file

@ -451,6 +451,44 @@ fn main() { Foo::Fo$0 }
);
}
#[test]
fn fn_detail_includes_args_and_return_type() {
check(
r#"
fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) }
fn main() { fo$0 }
"#,
expect![[r#"
[
CompletionItem {
label: "foo(…)",
source_range: 68..70,
delete: 68..70,
insert: "foo(${1:a}, ${2:b}, ${3:t})$0",
kind: SymbolKind(
Function,
),
lookup: "foo",
detail: "fn(u32, u32, T) -> (u32, T)",
trigger_call_info: true,
},
CompletionItem {
label: "main()",
source_range: 68..70,
delete: 68..70,
insert: "main()$0",
kind: SymbolKind(
Function,
),
lookup: "main",
detail: "fn()",
},
]
"#]],
);
}
#[test]
fn enum_detail_just_parentheses_for_unit() {
check(
@ -538,7 +576,7 @@ fn main() { let _: m::Spam = S$0 }
Function,
),
lookup: "main",
detail: "-> ()",
detail: "fn()",
},
]
"#]],
@ -567,7 +605,7 @@ fn main() { som$0 }
Function,
),
lookup: "main",
detail: "-> ()",
detail: "fn()",
},
CompletionItem {
label: "something_deprecated()",
@ -578,7 +616,7 @@ fn main() { som$0 }
Function,
),
lookup: "something_deprecated",
detail: "-> ()",
detail: "fn()",
deprecated: true,
},
CompletionItem {
@ -590,7 +628,7 @@ fn main() { som$0 }
Function,
),
lookup: "something_else_deprecated",
detail: "-> ()",
detail: "fn()",
deprecated: true,
},
]
@ -641,7 +679,7 @@ impl S {
insert: "bar()$0",
kind: Method,
lookup: "bar",
detail: "-> ()",
detail: "fn(self)",
documentation: Documentation(
"Method docs",
),
@ -741,7 +779,7 @@ fn foo(s: S) { s.$0 }
insert: "the_method()$0",
kind: Method,
lookup: "the_method",
detail: "-> ()",
detail: "fn(&self)",
},
]
"#]],
@ -1049,7 +1087,7 @@ fn main() {
Function,
),
lookup: "foo",
detail: "-> ()",
detail: "fn(&mut S)",
trigger_call_info: true,
},
CompletionItem {
@ -1061,7 +1099,7 @@ fn main() {
Function,
),
lookup: "main",
detail: "-> ()",
detail: "fn()",
},
CompletionItem {
label: "s",

View file

@ -2,6 +2,7 @@
use hir::{HasSource, HirDisplay, Type};
use ide_db::SymbolKind;
use itertools::Itertools;
use syntax::ast::Fn;
use crate::{
@ -73,8 +74,42 @@ impl<'a> FunctionRender<'a> {
}
fn detail(&self) -> String {
let ty = self.func.ret_type(self.ctx.db());
format!("-> {}", ty.display(self.ctx.db()))
let ret_ty = self.func.ret_type(self.ctx.db());
let ret = if ret_ty.is_unit() {
// Omit the return type if it is the unit type
String::new()
} else {
format!(" {}", self.ty_display())
};
format!("fn({}){}", self.params_display(), ret)
}
fn params_display(&self) -> String {
if let Some(self_param) = self.func.self_param(self.ctx.db()) {
let params = self
.func
.assoc_fn_params(self.ctx.db())
.into_iter()
.skip(1) // skip the self param because we are manually handling that
.map(|p| p.ty().display(self.ctx.db()).to_string());
std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ")
} else {
let params = self
.func
.assoc_fn_params(self.ctx.db())
.into_iter()
.map(|p| p.ty().display(self.ctx.db()).to_string())
.join(", ");
params
}
}
fn ty_display(&self) -> String {
let ret_ty = self.func.ret_type(self.ctx.db());
format!("-> {}", ret_ty.display(self.ctx.db()))
}
fn add_arg(&self, arg: &str, ty: &Type) -> String {