mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Auto merge of #16374 - Veykril:hover-notable, r=Veykril
feat: Show notable trait impls on hover
This commit is contained in:
commit
7777a81b69
7 changed files with 556 additions and 374 deletions
|
@ -200,7 +200,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||||
fn attrs(&self, def: AttrDefId) -> Attrs;
|
fn attrs(&self, def: AttrDefId) -> Attrs;
|
||||||
|
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
#[salsa::invoke(lang_item::lang_attr_query)]
|
#[salsa::invoke(lang_item::lang_attr)]
|
||||||
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
|
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
|
||||||
|
|
||||||
// endregion:attrs
|
// endregion:attrs
|
||||||
|
@ -228,6 +228,11 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||||
#[salsa::invoke(LangItems::crate_lang_items_query)]
|
#[salsa::invoke(LangItems::crate_lang_items_query)]
|
||||||
fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
|
fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
|
||||||
|
fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>;
|
||||||
|
#[salsa::invoke(crate::lang_item::crate_notable_traits)]
|
||||||
|
fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>;
|
||||||
|
|
||||||
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,17 +184,53 @@ impl LangItems {
|
||||||
T: Into<AttrDefId> + Copy,
|
T: Into<AttrDefId> + Copy,
|
||||||
{
|
{
|
||||||
let _p = profile::span("collect_lang_item");
|
let _p = profile::span("collect_lang_item");
|
||||||
if let Some(lang_item) = db.lang_attr(item.into()) {
|
if let Some(lang_item) = lang_attr(db, item.into()) {
|
||||||
self.items.entry(lang_item).or_insert_with(|| constructor(item));
|
self.items.entry(lang_item).or_insert_with(|| constructor(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
|
pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
|
||||||
let attrs = db.attrs(item);
|
let attrs = db.attrs(item);
|
||||||
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it))
|
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notable_traits_in_deps(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
) -> Arc<[Arc<[TraitId]>]> {
|
||||||
|
let _p = profile::span("notable_traits_in_deps").detail(|| format!("{krate:?}"));
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
|
||||||
|
Arc::from_iter(
|
||||||
|
crate_graph.transitive_deps(krate).filter_map(|krate| db.crate_notable_traits(krate)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option<Arc<[TraitId]>> {
|
||||||
|
let _p = profile::span("crate_notable_traits").detail(|| format!("{krate:?}"));
|
||||||
|
|
||||||
|
let mut traits = Vec::new();
|
||||||
|
|
||||||
|
let crate_def_map = db.crate_def_map(krate);
|
||||||
|
|
||||||
|
for (_, module_data) in crate_def_map.modules() {
|
||||||
|
for def in module_data.scope.declarations() {
|
||||||
|
if let ModuleDefId::TraitId(trait_) = def {
|
||||||
|
if db.attrs(trait_.into()).has_doc_notable_trait() {
|
||||||
|
traits.push(trait_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if traits.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(traits.into_iter().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum GenericRequirement {
|
pub enum GenericRequirement {
|
||||||
None,
|
None,
|
||||||
Minimum(usize),
|
Minimum(usize),
|
||||||
|
|
|
@ -2844,6 +2844,7 @@ impl AssocItem {
|
||||||
AssocItem::TypeAlias(it) => Some(it.name(db)),
|
AssocItem::TypeAlias(it) => Some(it.name(db)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||||
match self {
|
match self {
|
||||||
AssocItem::Function(f) => f.module(db),
|
AssocItem::Function(f) => f.module(db),
|
||||||
|
@ -2851,6 +2852,7 @@ impl AssocItem {
|
||||||
AssocItem::TypeAlias(t) => t.module(db),
|
AssocItem::TypeAlias(t) => t.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
|
pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
|
||||||
let container = match self {
|
let container = match self {
|
||||||
AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
|
AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
|
||||||
|
@ -2886,6 +2888,27 @@ impl AssocItem {
|
||||||
AssocItemContainer::Impl(i) => i.trait_(db),
|
AssocItemContainer::Impl(i) => i.trait_(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_function(self) -> Option<Function> {
|
||||||
|
match self {
|
||||||
|
Self::Function(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_const(self) -> Option<Const> {
|
||||||
|
match self {
|
||||||
|
Self::Const(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_type_alias(self) -> Option<TypeAlias> {
|
||||||
|
match self {
|
||||||
|
Self::TypeAlias(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for AssocItem {
|
impl HasVisibility for AssocItem {
|
||||||
|
@ -3024,6 +3047,7 @@ impl LocalSource {
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
pub fn is_param(self, db: &dyn HirDatabase) -> bool {
|
pub fn is_param(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
// FIXME: This parses!
|
||||||
let src = self.primary_source(db);
|
let src = self.primary_source(db);
|
||||||
match src.source.value {
|
match src.source.value {
|
||||||
Either::Left(pat) => pat
|
Either::Left(pat) => pat
|
||||||
|
|
|
@ -230,23 +230,15 @@ impl Definition {
|
||||||
Definition::BuiltinType(it) => it.name().display(db).to_string(),
|
Definition::BuiltinType(it) => it.name().display(db).to_string(),
|
||||||
Definition::Local(it) => {
|
Definition::Local(it) => {
|
||||||
let ty = it.ty(db);
|
let ty = it.ty(db);
|
||||||
let ty = ty.display_truncated(db, None);
|
let ty_display = ty.display_truncated(db, None);
|
||||||
let is_mut = if it.is_mut(db) { "mut " } else { "" };
|
let is_mut = if it.is_mut(db) { "mut " } else { "" };
|
||||||
let desc = match it.primary_source(db).into_ident_pat() {
|
if it.is_self(db) {
|
||||||
Some(ident) => {
|
format!("{is_mut}self: {ty_display}")
|
||||||
let name = it.name(db);
|
|
||||||
let let_kw = if ident.syntax().parent().map_or(false, |p| {
|
|
||||||
p.kind() == SyntaxKind::LET_STMT || p.kind() == SyntaxKind::LET_EXPR
|
|
||||||
}) {
|
|
||||||
"let "
|
|
||||||
} else {
|
} else {
|
||||||
""
|
let name = it.name(db);
|
||||||
};
|
let let_kw = if it.is_param(db) { "" } else { "let " };
|
||||||
format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
|
format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db))
|
||||||
}
|
}
|
||||||
None => format!("{is_mut}self: {ty}"),
|
|
||||||
};
|
|
||||||
desc
|
|
||||||
}
|
}
|
||||||
Definition::SelfType(impl_def) => {
|
Definition::SelfType(impl_def) => {
|
||||||
impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?
|
impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
//! Logic for rendering the different hover messages
|
//! Logic for rendering the different hover messages
|
||||||
|
use std::{mem, ops::Not};
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Semantics, TypeInfo,
|
db::DefDatabase, Adt, AsAssocItem, AssocItem, CaptureKind, HasCrate, HasSource, HirDisplay,
|
||||||
|
Layout, LayoutError, Semantics, TypeInfo,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::SourceDatabase,
|
base_db::SourceDatabase,
|
||||||
|
@ -390,7 +393,6 @@ pub(super) fn definition(
|
||||||
let mod_path = definition_mod_path(db, &def);
|
let mod_path = definition_mod_path(db, &def);
|
||||||
let label = def.label(db)?;
|
let label = def.label(db)?;
|
||||||
let docs = def.docs(db, famous_defs);
|
let docs = def.docs(db, famous_defs);
|
||||||
|
|
||||||
let value = match def {
|
let value = match def {
|
||||||
Definition::Variant(it) => {
|
Definition::Variant(it) => {
|
||||||
if !it.parent_enum(db).is_data_carrying(db) {
|
if !it.parent_enum(db).is_data_carrying(db) {
|
||||||
|
@ -462,14 +464,75 @@ pub(super) fn definition(
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let label = match (value, layout_info) {
|
let def_ty = match def {
|
||||||
(Some(value), Some(layout_info)) => format!("{layout_info}\n{label} = {value}"),
|
Definition::Local(it) => Some(it.ty(db)),
|
||||||
(Some(value), None) => format!("{label} = {value}"),
|
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
|
||||||
(None, Some(layout_info)) => format!("{layout_info}\n{label}"),
|
Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
|
||||||
(None, None) => label,
|
Definition::Field(field) => Some(field.ty(db)),
|
||||||
|
Definition::TupleField(it) => Some(it.ty(db)),
|
||||||
|
Definition::Function(it) => Some(it.ty(db)),
|
||||||
|
Definition::Adt(it) => Some(it.ty(db)),
|
||||||
|
Definition::Const(it) => Some(it.ty(db)),
|
||||||
|
Definition::Static(it) => Some(it.ty(db)),
|
||||||
|
Definition::TypeAlias(it) => Some(it.ty(db)),
|
||||||
|
Definition::BuiltinType(it) => Some(it.ty(db)),
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let notable_traits = def_ty.and_then(|ty| {
|
||||||
|
let mut desc = String::new();
|
||||||
|
let mut needs_impl_header = true;
|
||||||
|
for &trait_ in db.notable_traits_in_deps(ty.krate(db).into()).iter().flat_map(|it| &**it) {
|
||||||
|
let trait_ = trait_.into();
|
||||||
|
if ty.impls_trait(db, trait_, &[]) {
|
||||||
|
let aliases: Vec<_> = trait_
|
||||||
|
.items(db)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(AssocItem::as_type_alias)
|
||||||
|
.map(|alias| (ty.normalize_trait_assoc_type(db, &[], alias), alias.name(db)))
|
||||||
|
.collect();
|
||||||
|
desc.push_str(if mem::take(&mut needs_impl_header) {
|
||||||
|
" // notable traits impls: "
|
||||||
|
} else {
|
||||||
|
", "
|
||||||
|
});
|
||||||
|
format_to!(desc, "{}", trait_.name(db).display(db),);
|
||||||
|
if !aliases.is_empty() {
|
||||||
|
desc.push('<');
|
||||||
|
format_to!(
|
||||||
|
desc,
|
||||||
|
"{}",
|
||||||
|
aliases.into_iter().format_with(", ", |(ty, name), f| {
|
||||||
|
f(&name.display(db))?;
|
||||||
|
f(&" = ")?;
|
||||||
|
match ty {
|
||||||
|
Some(ty) => f(&ty.display(db)),
|
||||||
|
None => f(&"?"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
desc.push('>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
desc.is_empty().not().then(|| desc)
|
||||||
|
});
|
||||||
|
|
||||||
markup(docs.map(Into::into), label, mod_path)
|
let mut desc = String::new();
|
||||||
|
if let Some(notable_traits) = notable_traits {
|
||||||
|
desc.push_str(¬able_traits);
|
||||||
|
desc.push('\n');
|
||||||
|
}
|
||||||
|
if let Some(layout_info) = layout_info {
|
||||||
|
desc.push_str(&layout_info);
|
||||||
|
desc.push('\n');
|
||||||
|
}
|
||||||
|
desc.push_str(&label);
|
||||||
|
if let Some(value) = value {
|
||||||
|
desc.push_str(" = ");
|
||||||
|
desc.push_str(&value);
|
||||||
|
}
|
||||||
|
|
||||||
|
markup(docs.map(Into::into), desc, mod_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_info(
|
fn type_info(
|
||||||
|
|
|
@ -610,7 +610,7 @@ pub fn foo$0(_: &Path) {}
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
*foo*
|
*foo*
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -630,7 +630,7 @@ fn main() { }
|
||||||
#
|
#
|
||||||
foo(Path::new("hello, world!"))
|
foo(Path::new("hello, world!"))
|
||||||
```
|
```
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,7 +643,7 @@ pub fn foo$0(_: &Path) {}
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
"##,
|
"##,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
*foo*
|
*foo*
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -657,7 +657,7 @@ fn main() { }
|
||||||
---
|
---
|
||||||
|
|
||||||
Raw string doc attr
|
Raw string doc attr
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3327,7 +3327,7 @@ fn main() {
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// size = 8, align = 8, niches = 1
|
// size = 8, align = 8, niches = 1
|
||||||
f: &i32
|
let f: &i32
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -5164,16 +5164,16 @@ impl T1 for Foo {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*Bar*
|
*Bar*
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
test::t2
|
test::t2
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub type Bar
|
pub type Bar
|
||||||
```
|
```
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -7059,3 +7059,63 @@ fn main() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn notable_local() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
#[doc(notable_trait)]
|
||||||
|
trait Notable {
|
||||||
|
type Assoc;
|
||||||
|
type Assoc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Notable for u32 {
|
||||||
|
type Assoc = &str;
|
||||||
|
type Assoc2 = char;
|
||||||
|
}
|
||||||
|
fn main(notable$0: u32) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*notable*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// notable traits impls: Notable<Assoc = &str, Assoc2 = char>
|
||||||
|
// size = 4, align = 4
|
||||||
|
notable: u32
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn notable_foreign() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: future, iterator
|
||||||
|
struct S;
|
||||||
|
#[doc(notable_trait)]
|
||||||
|
trait Notable {}
|
||||||
|
impl Notable for S$0 {}
|
||||||
|
impl core::future::Future for S {
|
||||||
|
type Output = u32;
|
||||||
|
}
|
||||||
|
impl Iterator for S {
|
||||||
|
type Item = S;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*S*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// notable traits impls: Notable, Future<Output = u32>, Iterator<Item = S>
|
||||||
|
// size = 0, align = 1
|
||||||
|
struct S
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1166,6 +1166,7 @@ pub mod future {
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[doc(notable_trait)]
|
||||||
#[lang = "future_trait"]
|
#[lang = "future_trait"]
|
||||||
pub trait Future {
|
pub trait Future {
|
||||||
type Output;
|
type Output;
|
||||||
|
@ -1264,6 +1265,7 @@ pub mod iter {
|
||||||
|
|
||||||
mod traits {
|
mod traits {
|
||||||
mod iterator {
|
mod iterator {
|
||||||
|
#[doc(notable_trait)]
|
||||||
pub trait Iterator {
|
pub trait Iterator {
|
||||||
type Item;
|
type Item;
|
||||||
#[lang = "next"]
|
#[lang = "next"]
|
||||||
|
|
Loading…
Reference in a new issue