diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 2e3c46fdd4..796490abd7 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -6,33 +6,22 @@ use hir_def::{ path::{ModPath, Path}, per_ns::Namespace, resolver::{HasResolver, Resolver, TypeNs}, - AssocItemId, AttrDefId, GenericParamId, ModuleDefId, + AssocItemId, AttrDefId, ModuleDefId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use hir_ty::db::HirDatabase; use syntax::{ast, AstNode}; use crate::{ - Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field, - Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, - TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, + Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, + Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, + Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner; - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - ) -> Option; -} - -/// Subset of `ide_db::Definition` that doc links can resolve to. -pub enum DocLinkDef { - ModuleDef(ModuleDef), - Field(Field), - SelfType(Trait), + #[doc(hidden)] + fn attr_id(self) -> AttrDefId; } macro_rules! impl_has_attrs { @@ -42,14 +31,8 @@ macro_rules! impl_has_attrs { let def = AttrDefId::$def_id(self.into()); db.attrs_with_owner(def) } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option - ) -> Option { - let def = AttrDefId::$def_id(self.into()); - resolve_doc_path(db, def, link, ns) + fn attr_id(self) -> AttrDefId { + AttrDefId::$def_id(self.into()) } } )*}; @@ -69,6 +52,7 @@ impl_has_attrs![ (Module, ModuleId), (GenericParam, GenericParamId), (Impl, ImplId), + (ExternCrateDecl, ExternCrateId), ]; macro_rules! impl_has_attrs_enum { @@ -77,13 +61,8 @@ macro_rules! impl_has_attrs_enum { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { $enum::$variant(self).attrs(db) } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option - ) -> Option { - $enum::$variant(self).resolve_doc_path(db, link, ns) + fn attr_id(self) -> AttrDefId { + $enum::$variant(self).attr_id() } } )*}; @@ -100,45 +79,35 @@ impl HasAttrs for AssocItem { AssocItem::TypeAlias(it) => it.attrs(db), } } - - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - ) -> Option { + fn attr_id(self) -> AttrDefId { match self { - AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), - AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), - AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + AssocItem::Function(it) => it.attr_id(), + AssocItem::Const(it) => it.attr_id(), + AssocItem::TypeAlias(it) => it.attr_id(), } } } -impl HasAttrs for ExternCrateDecl { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { - let def = AttrDefId::ExternCrateId(self.into()); - db.attrs_with_owner(def) - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - ) -> Option { - let def = AttrDefId::ExternCrateId(self.into()); - resolve_doc_path(db, def, link, ns) - } -} - /// Resolves the item `link` points to in the scope of `def`. -fn resolve_doc_path( +pub fn resolve_doc_path_on( db: &dyn HirDatabase, - def: AttrDefId, + def: impl HasAttrs, link: &str, ns: Option, ) -> Option { - let resolver = match def { + // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), + // AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()), + + resolve_doc_path_on_(db, link, def.attr_id(), ns) +} + +fn resolve_doc_path_on_( + db: &dyn HirDatabase, + link: &str, + attr_id: AttrDefId, + ns: Option, +) -> Option { + let resolver = match attr_id { AttrDefId::ModuleId(it) => it.resolver(db.upcast()), AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), AttrDefId::AdtId(it) => it.resolver(db.upcast()), @@ -154,12 +123,7 @@ fn resolve_doc_path( AttrDefId::UseId(it) => it.resolver(db.upcast()), AttrDefId::MacroId(it) => it.resolver(db.upcast()), AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()), - AttrDefId::GenericParamId(it) => match it { - GenericParamId::TypeParamId(it) => it.parent(), - GenericParamId::ConstParamId(it) => it.parent(), - GenericParamId::LifetimeParamId(it) => it.parent, - } - .resolver(db.upcast()), + AttrDefId::GenericParamId(_) => return None, }; let mut modpath = modpath_from_str(db, link)?; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4123b235a7..91443238c6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -88,7 +88,7 @@ use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ - attrs::{DocLinkDef, HasAttrs}, + attrs::{resolve_doc_path_on, HasAttrs}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, @@ -4839,3 +4839,10 @@ pub enum ItemContainer { ExternBlock(), Crate(CrateId), } + +/// Subset of `ide_db::Definition` that doc links can resolve to. +pub enum DocLinkDef { + ModuleDef(ModuleDef), + Field(Field), + SelfType(Trait), +} diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs index 3e6b1cbc36..aa8a374218 100644 --- a/crates/ide-db/src/documentation.rs +++ b/crates/ide-db/src/documentation.rs @@ -1,7 +1,7 @@ use either::Either; use hir::{ db::{DefDatabase, HirDatabase}, - AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile, + resolve_doc_path_on, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile, }; use itertools::Itertools; use syntax::{ @@ -32,77 +32,13 @@ impl From for String { pub trait HasDocs: HasAttrs { fn docs(self, db: &dyn HirDatabase) -> Option; + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option, + ) -> Option; } - -macro_rules! impl_has_docs { - ($($def:ident,)*) => {$( - impl HasDocs for hir::$def { - fn docs(self, db: &dyn HirDatabase) -> Option { - docs_from_attrs(&self.attrs(db)).map(Documentation) - } - } - )*}; -} - -impl_has_docs![ - Field, - Variant, - Static, - Const, - Trait, - TraitAlias, - TypeAlias, - Macro, - Function, - Adt, - Module, - GenericParam, - Impl, -]; - -macro_rules! impl_has_docs_enum { - ($($variant:ident),* for $enum:ident) => {$( - impl HasDocs for hir::$variant { - fn docs(self, db: &dyn HirDatabase) -> Option { - hir::$enum::$variant(self).docs(db) - } - } - )*}; -} - -impl_has_docs_enum![Struct, Union, Enum for Adt]; -impl_has_docs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam]; - -impl HasDocs for hir::AssocItem { - fn docs(self, db: &dyn HirDatabase) -> Option { - match self { - hir::AssocItem::Function(it) => it.docs(db), - hir::AssocItem::Const(it) => it.docs(db), - hir::AssocItem::TypeAlias(it) => it.docs(db), - } - } -} - -impl HasDocs for hir::ExternCrateDecl { - fn docs(self, db: &dyn HirDatabase) -> Option { - let crate_docs = - docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from); - let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from); - match (decl_docs, crate_docs) { - (None, None) => None, - (Some(decl_docs), None) => Some(decl_docs), - (None, Some(crate_docs)) => Some(crate_docs), - (Some(mut decl_docs), Some(crate_docs)) => { - decl_docs.push('\n'); - decl_docs.push('\n'); - decl_docs += &crate_docs; - Some(decl_docs) - } - } - .map(Documentation::new) - } -} - /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. #[derive(Debug)] pub struct DocsRangeMap { @@ -150,22 +86,6 @@ impl DocsRangeMap { } } -fn get_doc_string_in_attr(it: &ast::Attr) -> Option { - match it.expr() { - // #[doc = lit] - Some(ast::Expr::Literal(lit)) => match lit.kind() { - ast::LiteralKind::String(it) => Some(it), - _ => None, - }, - // #[cfg_attr(..., doc = "", ...)] - None => { - // FIXME: See highlight injection for what to do here - None - } - _ => None, - } -} - pub fn docs_with_rangemap( db: &dyn DefDatabase, attrs: &AttrsWithOwner, @@ -237,6 +157,116 @@ pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option { } } +macro_rules! impl_has_docs { + ($($def:ident,)*) => {$( + impl HasDocs for hir::$def { + fn docs(self, db: &dyn HirDatabase) -> Option { + docs_from_attrs(&self.attrs(db)).map(Documentation) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option + ) -> Option { + resolve_doc_path_on(db, self, link, ns) + } + } + )*}; +} + +impl_has_docs![ + Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module, + Impl, +]; + +macro_rules! impl_has_docs_enum { + ($($variant:ident),* for $enum:ident) => {$( + impl HasDocs for hir::$variant { + fn docs(self, db: &dyn HirDatabase) -> Option { + hir::$enum::$variant(self).docs(db) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option + ) -> Option { + hir::$enum::$variant(self).resolve_doc_path(db, link, ns) + } + } + )*}; +} + +impl_has_docs_enum![Struct, Union, Enum for Adt]; + +impl HasDocs for hir::AssocItem { + fn docs(self, db: &dyn HirDatabase) -> Option { + match self { + hir::AssocItem::Function(it) => it.docs(db), + hir::AssocItem::Const(it) => it.docs(db), + hir::AssocItem::TypeAlias(it) => it.docs(db), + } + } + + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option, + ) -> Option { + match self { + hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + } + } +} + +impl HasDocs for hir::ExternCrateDecl { + fn docs(self, db: &dyn HirDatabase) -> Option { + let crate_docs = + docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from); + let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(decl_docs), None) => Some(decl_docs), + (None, Some(crate_docs)) => Some(crate_docs), + (Some(mut decl_docs), Some(crate_docs)) => { + decl_docs.push('\n'); + decl_docs.push('\n'); + decl_docs += &crate_docs; + Some(decl_docs) + } + } + .map(Documentation::new) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option, + ) -> Option { + resolve_doc_path_on(db, self, link, ns) + } +} + +fn get_doc_string_in_attr(it: &ast::Attr) -> Option { + match it.expr() { + // #[doc = lit] + Some(ast::Expr::Literal(lit)) => match lit.kind() { + ast::LiteralKind::String(it) => Some(it), + _ => None, + }, + // #[cfg_attr(..., doc = "", ...)] + None => { + // FIXME: See highlight injection for what to do here + None + } + _ => None, + } +} + fn doc_indent(attrs: &hir::Attrs) -> usize { attrs .by_key("doc") diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index f714bb7d1d..37a1776221 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -16,7 +16,7 @@ use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasA use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, defs::{Definition, NameClass, NameRefClass}, - documentation::{docs_with_rangemap, Documentation}, + documentation::{docs_with_rangemap, Documentation, HasDocs}, helpers::pick_best_token, RootDatabase, }; diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 59423396ce..f72ce37d1d 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -471,7 +471,7 @@ pub(super) fn definition( Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } - Definition::GenericParam(it) => label_and_docs(db, it), + Definition::GenericParam(it) => (it.display(db).to_string(), None), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), Definition::ExternCrateDecl(it) => label_and_docs(db, it), // FIXME: We should be able to show more info about these