diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index beaa6dd4d67..2d8811cf5eb 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; +} + +impl AsExternAssocItem for Function { + fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option { + as_extern_assoc_item(db, ExternAssocItem::Function, self.id) + } +} + +impl AsExternAssocItem for Static { + fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option { + as_extern_assoc_item(db, ExternAssocItem::Static, self.id) + } +} + +impl AsExternAssocItem for TypeAlias { + fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option { + 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 +where + ID: Lookup = dyn DefDatabase + 'db, Data = AssocItemLoc>, + DEF: From, + 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 { + match self { + Self::Function(v) => Some(v), + _ => None, + } + } + + pub fn as_static(self) -> Option { + match self { + Self::Static(v) => Some(v), + _ => None, + } + } + + pub fn as_type_alias(self) -> Option { + match self { + Self::TypeAlias(v) => Some(v), + _ => None, + } + } +} + impl AssocItem { pub fn name(self, db: &dyn HirDatabase) -> Option { match self { diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index d95d94ec72e..1b6ff8bad53 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 { - 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 { + 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 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 19b181ae3b6..4a7350feb38 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 { 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(), ¬able_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, ¬able_traits), - ] - .into_iter() - .flatten() - .collect(), - } - }) + let markup = render::definition(sema.db, def, famous_defs.as_ref(), ¬able_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, ¬able_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 eff055c9599..563e78253a8 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 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(|_| "".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, Name)>)], config: &HoverConfig, -) -> Option { +) -> 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 { def.module(db).map(|module| path(db, module, definition_owner_name(db, def))) } -fn markup(docs: Option, desc: String, mod_path: Option) -> Option { +fn markup(docs: Option, desc: String, mod_path: Option) -> Markup { let mut buf = String::new(); if let Some(mod_path) = mod_path { @@ -696,7 +704,7 @@ fn markup(docs: Option, desc: String, mod_path: Option) -> 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 { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 69ddc1e45ef..ead4f91595f 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:: ``` ```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 @@ -7201,6 +7237,65 @@ impl Iterator for S { ); } +#[test] +fn extern_items() { + check( + r#" +extern "C" { + static STATIC$0: (); +} +"#, + expect![[r#" + *STATIC* + + ```rust + test:: + ``` + + ```rust + static STATIC: () + ``` + "#]], + ); + check( + r#" +extern "C" { + fn fun$0(); +} +"#, + expect![[r#" + *fun* + + ```rust + test:: + ``` + + ```rust + unsafe fn fun() + ``` + "#]], + ); + check( + r#" +extern "C" { + type Ty$0; +} +"#, + expect![[r#" + *Ty* + + ```rust + test:: + ``` + + ```rust + // size = 0, align = 1 + type Ty + ``` + "#]], + ); +} + #[test] fn notable_ranged() { check_hover_range( diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 5feaf21aa97..2929a7522e5 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);