Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #16606 - Veykril:hover-extern, r=Veykril
internal: Render assoc item owner in hover for items other than functions Closes https://github.com/rust-lang/rust-analyzer/issues/16603
bors 2024-02-20
parent 26a16c4 · parent 85203d9 · commit c888724
-rw-r--r--crates/hir/src/lib.rs88
-rw-r--r--crates/ide-db/src/defs.rs34
-rw-r--r--crates/ide/src/hover.rs37
-rw-r--r--crates/ide/src/hover/render.rs36
-rw-r--r--crates/ide/src/hover/tests.rs131
-rw-r--r--crates/ide/src/static_index.rs4
6 files changed, 267 insertions, 63 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index beaa6dd4d6..2d8811cf5e 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2653,6 +2653,37 @@ impl ItemInNs {
}
}
+/// Invariant: `inner.as_extern_assoc_item(db).is_some()`
+/// We do not actively enforce this invariant.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ExternAssocItem {
+ Function(Function),
+ Static(Static),
+ TypeAlias(TypeAlias),
+}
+
+pub trait AsExternAssocItem {
+ fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem>;
+}
+
+impl AsExternAssocItem for Function {
+ fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem> {
+ as_extern_assoc_item(db, ExternAssocItem::Function, self.id)
+ }
+}
+
+impl AsExternAssocItem for Static {
+ fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem> {
+ as_extern_assoc_item(db, ExternAssocItem::Static, self.id)
+ }
+}
+
+impl AsExternAssocItem for TypeAlias {
+ fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem> {
+ as_extern_assoc_item(db, ExternAssocItem::TypeAlias, self.id)
+ }
+}
+
/// Invariant: `inner.as_assoc_item(db).is_some()`
/// We do not actively enforce this invariant.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -2727,6 +2758,63 @@ where
}
}
+fn as_extern_assoc_item<'db, ID, DEF, LOC>(
+ db: &(dyn HirDatabase + 'db),
+ ctor: impl FnOnce(DEF) -> ExternAssocItem,
+ id: ID,
+) -> Option<ExternAssocItem>
+where
+ ID: Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<LOC>>,
+ DEF: From<ID>,
+ LOC: ItemTreeNode,
+{
+ match id.lookup(db.upcast()).container {
+ ItemContainerId::ExternBlockId(_) => Some(ctor(DEF::from(id))),
+ ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) | ItemContainerId::ModuleId(_) => {
+ None
+ }
+ }
+}
+
+impl ExternAssocItem {
+ pub fn name(self, db: &dyn HirDatabase) -> Name {
+ match self {
+ Self::Function(it) => it.name(db),
+ Self::Static(it) => it.name(db),
+ Self::TypeAlias(it) => it.name(db),
+ }
+ }
+
+ pub fn module(self, db: &dyn HirDatabase) -> Module {
+ match self {
+ Self::Function(f) => f.module(db),
+ Self::Static(c) => c.module(db),
+ Self::TypeAlias(t) => t.module(db),
+ }
+ }
+
+ pub fn as_function(self) -> Option<Function> {
+ match self {
+ Self::Function(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ pub fn as_static(self) -> Option<Static> {
+ match self {
+ Self::Static(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ pub fn as_type_alias(self) -> Option<TypeAlias> {
+ match self {
+ Self::TypeAlias(v) => Some(v),
+ _ => None,
+ }
+ }
+}
+
impl AssocItem {
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
match self {
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index d95d94ec72..1b6ff8bad5 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -8,11 +8,11 @@
use arrayvec::ArrayVec;
use either::Either;
use hir::{
- Adt, AsAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate,
- DefWithBody, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam,
- HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution,
- Semantics, Static, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef,
- Visibility,
+ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
+ Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
+ Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module,
+ ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TupleField,
+ TypeAlias, Variant, VariantDef, Visibility,
};
use stdx::{format_to, impl_from};
use syntax::{
@@ -213,8 +213,8 @@ impl Definition {
})
}
- pub fn label(&self, db: &RootDatabase) -> Option<String> {
- let label = match *self {
+ pub fn label(&self, db: &RootDatabase) -> String {
+ match *self {
Definition::Macro(it) => it.display(db).to_string(),
Definition::Field(it) => it.display(db).to_string(),
Definition::TupleField(it) => it.display(db).to_string(),
@@ -241,7 +241,11 @@ impl Definition {
}
}
Definition::SelfType(impl_def) => {
- impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?
+ let self_ty = &impl_def.self_ty(db);
+ match self_ty.as_adt() {
+ Some(it) => it.display(db).to_string(),
+ None => self_ty.display(db).to_string(),
+ }
}
Definition::GenericParam(it) => it.display(db).to_string(),
Definition::Label(it) => it.name(db).display(db).to_string(),
@@ -249,8 +253,7 @@ impl Definition {
Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db)),
Definition::ToolModule(it) => it.name(db).to_string(),
Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)),
- };
- Some(label)
+ }
}
}
@@ -739,6 +742,17 @@ impl AsAssocItem for Definition {
}
}
+impl AsExternAssocItem for Definition {
+ fn as_extern_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<ExternAssocItem> {
+ match self {
+ Definition::Function(it) => it.as_extern_assoc_item(db),
+ Definition::Static(it) => it.as_extern_assoc_item(db),
+ Definition::TypeAlias(it) => it.as_extern_assoc_item(db),
+ _ => None,
+ }
+ }
+}
+
impl From<AssocItem> for Definition {
fn from(assoc_item: AssocItem) -> Self {
match assoc_item {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 19b181ae3b..4a7350feb3 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -147,7 +147,7 @@ fn hover_simple(
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
cov_mark::hit!(no_highlight_on_comment_hover);
return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
- let res = hover_for_definition(sema, file_id, def, &node, config)?;
+ let res = hover_for_definition(sema, file_id, def, &node, config);
Some(RangeInfo::new(range, res))
});
}
@@ -161,7 +161,7 @@ fn hover_simple(
Definition::from(resolution?),
&original_token.parent()?,
config,
- )?;
+ );
return Some(RangeInfo::new(range, res));
}
@@ -215,7 +215,7 @@ fn hover_simple(
})
.flatten()
.unique_by(|&(def, _)| def)
- .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
+ .map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
.reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
acc.actions.extend(actions);
acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
@@ -373,9 +373,9 @@ pub(crate) fn hover_for_definition(
def: Definition,
scope_node: &SyntaxNode,
config: &HoverConfig,
-) -> Option<HoverResult> {
+) -> HoverResult {
let famous_defs = match &def {
- Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(scope_node)?.krate())),
+ Definition::BuiltinType(_) => sema.scope(scope_node).map(|it| FamousDefs(sema, it.krate())),
_ => None,
};
@@ -396,20 +396,19 @@ pub(crate) fn hover_for_definition(
};
let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
- render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, config).map(|markup| {
- HoverResult {
- markup: render::process_markup(sema.db, def, &markup, config),
- actions: [
- show_implementations_action(sema.db, def),
- show_fn_references_action(sema.db, def),
- runnable_action(sema, def, file_id),
- goto_type_action_for_def(sema.db, def, &notable_traits),
- ]
- .into_iter()
- .flatten()
- .collect(),
- }
- })
+ let markup = render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, config);
+ HoverResult {
+ markup: render::process_markup(sema.db, def, &markup, config),
+ actions: [
+ show_implementations_action(sema.db, def),
+ show_fn_references_action(sema.db, def),
+ runnable_action(sema, def, file_id),
+ goto_type_action_for_def(sema.db, def, &notable_traits),
+ ]
+ .into_iter()
+ .flatten()
+ .collect(),
+ }
}
fn notable_traits(
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index eff055c959..563e78253a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -3,8 +3,8 @@ use std::{mem, ops::Not};
use either::Either;
use hir::{
- Adt, AsAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout, LayoutError, Name,
- Semantics, Trait, Type, TypeInfo,
+ Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout,
+ LayoutError, Name, Semantics, Trait, Type, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
@@ -264,7 +264,7 @@ pub(super) fn keyword(
let markup = process_markup(
sema.db,
Definition::Module(doc_owner),
- &markup(Some(docs.into()), description, None)?,
+ &markup(Some(docs.into()), description, None),
config,
);
Some(HoverResult { markup, actions })
@@ -369,12 +369,20 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
match def {
Definition::Field(f) => Some(f.parent_def(db).name(db)),
Definition::Local(l) => l.parent(db).name(db),
- Definition::Function(f) => match f.as_assoc_item(db)?.container(db) {
- hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
- hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
- },
Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
- _ => None,
+
+ d => {
+ if let Some(assoc_item) = d.as_assoc_item(db) {
+ match assoc_item.container(db) {
+ hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+ hir::AssocItemContainer::Impl(i) => {
+ i.self_ty(db).as_adt().map(|adt| adt.name(db))
+ }
+ }
+ } else {
+ return d.as_extern_assoc_item(db).map(|_| "<extern>".to_owned());
+ }
+ }
}
.map(|name| name.display(db).to_string())
}
@@ -396,11 +404,11 @@ pub(super) fn definition(
famous_defs: Option<&FamousDefs<'_, '_>>,
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
config: &HoverConfig,
-) -> Option<Markup> {
+) -> Markup {
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 value = match def {
+ let value = (|| match def {
Definition::Variant(it) => {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
@@ -436,7 +444,7 @@ pub(super) fn definition(
Some(body.to_string())
}
_ => None,
- };
+ })();
let layout_info = match def {
Definition::Field(it) => render_memory_layout(
@@ -683,7 +691,7 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
}
-fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
+fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Markup {
let mut buf = String::new();
if let Some(mod_path) = mod_path {
@@ -696,7 +704,7 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
if let Some(doc) = docs {
format_to!(buf, "\n___\n\n{}", doc);
}
- Some(buf.into())
+ buf.into()
}
fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 69ddc1e45e..ead4f91595 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1202,7 +1202,7 @@ fn main() {
*C*
```rust
- test
+ test::X
```
```rust
@@ -1279,11 +1279,11 @@ impl Thing {
);
check(
r#"
- enum Thing { A }
- impl Thing {
- pub fn thing(a: Self$0) {}
- }
- "#,
+enum Thing { A }
+impl Thing {
+ pub fn thing(a: Self$0) {}
+}
+"#,
expect![[r#"
*Self*
@@ -1298,6 +1298,42 @@ impl Thing {
```
"#]],
);
+ check(
+ r#"
+impl usize {
+ pub fn thing(a: Self$0) {}
+}
+"#,
+ expect![[r#"
+ *Self*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ usize
+ ```
+ "#]],
+ );
+ check(
+ r#"
+impl fn() -> usize {
+ pub fn thing(a: Self$0) {}
+}
+"#,
+ expect![[r#"
+ *Self*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn() -> usize
+ ```
+ "#]],
+ );
}
#[test]
@@ -2241,7 +2277,7 @@ fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } }
*foo*
```rust
- test
+ test::<extern>
```
```rust
@@ -4230,7 +4266,7 @@ fn main() {
*B*
```rust
- test
+ test::T
```
```rust
@@ -4259,7 +4295,7 @@ fn main() {
*B*
```rust
- test
+ test::T
```
```rust
@@ -4291,7 +4327,7 @@ fn main() {
*B*
```rust
- test
+ test::T
```
```rust
@@ -4883,7 +4919,7 @@ fn test() {
*FOO*
```rust
- test
+ test::S
```
```rust
@@ -5248,7 +5284,7 @@ impl T1 for Foo {
*Bar*
```rust
- test::t2
+ test::t2::T2
```
```rust
@@ -5270,7 +5306,7 @@ trait A {
*Assoc*
```rust
- test
+ test::A
```
```rust
@@ -5291,7 +5327,7 @@ trait A {
*Assoc*
```rust
- test
+ test::A
```
```rust
@@ -5310,7 +5346,7 @@ trait A where
*Assoc*
```rust
- test
+ test::A
```
```rust
@@ -6596,7 +6632,7 @@ fn test() {
*A*
```rust
- test
+ test::S
```
```rust
@@ -6625,7 +6661,7 @@ fn test() {
*A*
```rust
- test
+ test::S
```
```rust
@@ -6655,7 +6691,7 @@ mod m {
*A*
```rust
- test
+ test::S
```
```rust
@@ -7202,6 +7238,65 @@ impl Iterator for S {
}
#[test]
+fn extern_items() {
+ check(
+ r#"
+extern "C" {
+ static STATIC$0: ();
+}
+"#,
+ expect![[r#"
+ *STATIC*
+
+ ```rust
+ test::<extern>
+ ```
+
+ ```rust
+ static STATIC: ()
+ ```
+ "#]],
+ );
+ check(
+ r#"
+extern "C" {
+ fn fun$0();
+}
+"#,
+ expect![[r#"
+ *fun*
+
+ ```rust
+ test::<extern>
+ ```
+
+ ```rust
+ unsafe fn fun()
+ ```
+ "#]],
+ );
+ check(
+ r#"
+extern "C" {
+ type Ty$0;
+}
+"#,
+ expect![[r#"
+ *Ty*
+
+ ```rust
+ test::<extern>
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ type Ty
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn notable_ranged() {
check_hover_range(
r#"
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 5feaf21aa9..2929a7522e 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -186,7 +186,7 @@ impl StaticIndex<'_> {
} else {
let it = self.tokens.insert(TokenStaticData {
documentation: documentation_for_definition(&sema, def, &node),
- hover: hover_for_definition(&sema, file_id, def, &node, &hover_config),
+ hover: Some(hover_for_definition(&sema, file_id, def, &node, &hover_config)),
definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| {
FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
}),
@@ -196,7 +196,7 @@ impl StaticIndex<'_> {
enclosing_moniker: current_crate
.zip(def.enclosing_definition(self.db))
.and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)),
- signature: def.label(self.db),
+ signature: Some(def.label(self.db)),
kind: def_to_kind(self.db, def),
});
self.def_map.insert(def, it);