Skip to content

Commit 7777a81

Browse files
committed
Auto merge of rust-lang#16374 - Veykril:hover-notable, r=Veykril
feat: Show notable trait impls on hover
2 parents e2df3f2 + 82e8355 commit 7777a81

File tree

7 files changed

+557
-375
lines changed

7 files changed

+557
-375
lines changed

crates/hir-def/src/db.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
200200
fn attrs(&self, def: AttrDefId) -> Attrs;
201201

202202
#[salsa::transparent]
203-
#[salsa::invoke(lang_item::lang_attr_query)]
203+
#[salsa::invoke(lang_item::lang_attr)]
204204
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
205205

206206
// endregion:attrs
@@ -228,6 +228,11 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
228228
#[salsa::invoke(LangItems::crate_lang_items_query)]
229229
fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
230230

231+
#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
232+
fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>;
233+
#[salsa::invoke(crate::lang_item::crate_notable_traits)]
234+
fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>;
235+
231236
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
232237
}
233238

crates/hir-def/src/lang_item.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,53 @@ impl LangItems {
184184
T: Into<AttrDefId> + Copy,
185185
{
186186
let _p = profile::span("collect_lang_item");
187-
if let Some(lang_item) = db.lang_attr(item.into()) {
187+
if let Some(lang_item) = lang_attr(db, item.into()) {
188188
self.items.entry(lang_item).or_insert_with(|| constructor(item));
189189
}
190190
}
191191
}
192192

193-
pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
193+
pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
194194
let attrs = db.attrs(item);
195195
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it))
196196
}
197197

198+
pub(crate) fn notable_traits_in_deps(
199+
db: &dyn DefDatabase,
200+
krate: CrateId,
201+
) -> Arc<[Arc<[TraitId]>]> {
202+
let _p = profile::span("notable_traits_in_deps").detail(|| format!("{krate:?}"));
203+
let crate_graph = db.crate_graph();
204+
205+
Arc::from_iter(
206+
crate_graph.transitive_deps(krate).filter_map(|krate| db.crate_notable_traits(krate)),
207+
)
208+
}
209+
210+
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option<Arc<[TraitId]>> {
211+
let _p = profile::span("crate_notable_traits").detail(|| format!("{krate:?}"));
212+
213+
let mut traits = Vec::new();
214+
215+
let crate_def_map = db.crate_def_map(krate);
216+
217+
for (_, module_data) in crate_def_map.modules() {
218+
for def in module_data.scope.declarations() {
219+
if let ModuleDefId::TraitId(trait_) = def {
220+
if db.attrs(trait_.into()).has_doc_notable_trait() {
221+
traits.push(trait_);
222+
}
223+
}
224+
}
225+
}
226+
227+
if traits.is_empty() {
228+
None
229+
} else {
230+
Some(traits.into_iter().collect())
231+
}
232+
}
233+
198234
pub enum GenericRequirement {
199235
None,
200236
Minimum(usize),

crates/hir/src/lib.rs

+24
Original file line numberDiff line numberDiff line change
@@ -2844,13 +2844,15 @@ impl AssocItem {
28442844
AssocItem::TypeAlias(it) => Some(it.name(db)),
28452845
}
28462846
}
2847+
28472848
pub fn module(self, db: &dyn HirDatabase) -> Module {
28482849
match self {
28492850
AssocItem::Function(f) => f.module(db),
28502851
AssocItem::Const(c) => c.module(db),
28512852
AssocItem::TypeAlias(t) => t.module(db),
28522853
}
28532854
}
2855+
28542856
pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
28552857
let container = match self {
28562858
AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
@@ -2886,6 +2888,27 @@ impl AssocItem {
28862888
AssocItemContainer::Impl(i) => i.trait_(db),
28872889
}
28882890
}
2891+
2892+
pub fn as_function(self) -> Option<Function> {
2893+
match self {
2894+
Self::Function(v) => Some(v),
2895+
_ => None,
2896+
}
2897+
}
2898+
2899+
pub fn as_const(self) -> Option<Const> {
2900+
match self {
2901+
Self::Const(v) => Some(v),
2902+
_ => None,
2903+
}
2904+
}
2905+
2906+
pub fn as_type_alias(self) -> Option<TypeAlias> {
2907+
match self {
2908+
Self::TypeAlias(v) => Some(v),
2909+
_ => None,
2910+
}
2911+
}
28892912
}
28902913

28912914
impl HasVisibility for AssocItem {
@@ -3024,6 +3047,7 @@ impl LocalSource {
30243047

30253048
impl Local {
30263049
pub fn is_param(self, db: &dyn HirDatabase) -> bool {
3050+
// FIXME: This parses!
30273051
let src = self.primary_source(db);
30283052
match src.source.value {
30293053
Either::Left(pat) => pat

crates/ide-db/src/defs.rs

+8-16
Original file line numberDiff line numberDiff line change
@@ -230,23 +230,15 @@ impl Definition {
230230
Definition::BuiltinType(it) => it.name().display(db).to_string(),
231231
Definition::Local(it) => {
232232
let ty = it.ty(db);
233-
let ty = ty.display_truncated(db, None);
233+
let ty_display = ty.display_truncated(db, None);
234234
let is_mut = if it.is_mut(db) { "mut " } else { "" };
235-
let desc = match it.primary_source(db).into_ident_pat() {
236-
Some(ident) => {
237-
let name = it.name(db);
238-
let let_kw = if ident.syntax().parent().map_or(false, |p| {
239-
p.kind() == SyntaxKind::LET_STMT || p.kind() == SyntaxKind::LET_EXPR
240-
}) {
241-
"let "
242-
} else {
243-
""
244-
};
245-
format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
246-
}
247-
None => format!("{is_mut}self: {ty}"),
248-
};
249-
desc
235+
if it.is_self(db) {
236+
format!("{is_mut}self: {ty_display}")
237+
} else {
238+
let name = it.name(db);
239+
let let_kw = if it.is_param(db) { "" } else { "let " };
240+
format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db))
241+
}
250242
}
251243
Definition::SelfType(impl_def) => {
252244
impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?

crates/ide/src/hover/render.rs

+71-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Logic for rendering the different hover messages
2+
use std::{mem, ops::Not};
3+
24
use either::Either;
35
use hir::{
4-
Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Semantics, TypeInfo,
6+
db::DefDatabase, Adt, AsAssocItem, AssocItem, CaptureKind, HasCrate, HasSource, HirDisplay,
7+
Layout, LayoutError, Semantics, TypeInfo,
58
};
69
use ide_db::{
710
base_db::SourceDatabase,
@@ -390,7 +393,6 @@ pub(super) fn definition(
390393
let mod_path = definition_mod_path(db, &def);
391394
let label = def.label(db)?;
392395
let docs = def.docs(db, famous_defs);
393-
394396
let value = match def {
395397
Definition::Variant(it) => {
396398
if !it.parent_enum(db).is_data_carrying(db) {
@@ -462,14 +464,75 @@ pub(super) fn definition(
462464
_ => None,
463465
};
464466

465-
let label = match (value, layout_info) {
466-
(Some(value), Some(layout_info)) => format!("{layout_info}\n{label} = {value}"),
467-
(Some(value), None) => format!("{label} = {value}"),
468-
(None, Some(layout_info)) => format!("{layout_info}\n{label}"),
469-
(None, None) => label,
467+
let def_ty = match def {
468+
Definition::Local(it) => Some(it.ty(db)),
469+
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
470+
Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
471+
Definition::Field(field) => Some(field.ty(db)),
472+
Definition::TupleField(it) => Some(it.ty(db)),
473+
Definition::Function(it) => Some(it.ty(db)),
474+
Definition::Adt(it) => Some(it.ty(db)),
475+
Definition::Const(it) => Some(it.ty(db)),
476+
Definition::Static(it) => Some(it.ty(db)),
477+
Definition::TypeAlias(it) => Some(it.ty(db)),
478+
Definition::BuiltinType(it) => Some(it.ty(db)),
479+
_ => None,
470480
};
481+
let notable_traits = def_ty.and_then(|ty| {
482+
let mut desc = String::new();
483+
let mut needs_impl_header = true;
484+
for &trait_ in db.notable_traits_in_deps(ty.krate(db).into()).iter().flat_map(|it| &**it) {
485+
let trait_ = trait_.into();
486+
if ty.impls_trait(db, trait_, &[]) {
487+
let aliases: Vec<_> = trait_
488+
.items(db)
489+
.into_iter()
490+
.filter_map(AssocItem::as_type_alias)
491+
.map(|alias| (ty.normalize_trait_assoc_type(db, &[], alias), alias.name(db)))
492+
.collect();
493+
desc.push_str(if mem::take(&mut needs_impl_header) {
494+
" // notable traits impls: "
495+
} else {
496+
", "
497+
});
498+
format_to!(desc, "{}", trait_.name(db).display(db),);
499+
if !aliases.is_empty() {
500+
desc.push('<');
501+
format_to!(
502+
desc,
503+
"{}",
504+
aliases.into_iter().format_with(", ", |(ty, name), f| {
505+
f(&name.display(db))?;
506+
f(&" = ")?;
507+
match ty {
508+
Some(ty) => f(&ty.display(db)),
509+
None => f(&"?"),
510+
}
511+
})
512+
);
513+
desc.push('>');
514+
}
515+
}
516+
}
517+
desc.is_empty().not().then(|| desc)
518+
});
519+
520+
let mut desc = String::new();
521+
if let Some(notable_traits) = notable_traits {
522+
desc.push_str(&notable_traits);
523+
desc.push('\n');
524+
}
525+
if let Some(layout_info) = layout_info {
526+
desc.push_str(&layout_info);
527+
desc.push('\n');
528+
}
529+
desc.push_str(&label);
530+
if let Some(value) = value {
531+
desc.push_str(" = ");
532+
desc.push_str(&value);
533+
}
471534

472-
markup(docs.map(Into::into), label, mod_path)
535+
markup(docs.map(Into::into), desc, mod_path)
473536
}
474537

475538
fn type_info(

0 commit comments

Comments
 (0)