Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21528 from ChayimFriedman2/doclink-fixes
fix: Handle `Self::EnumVariant` and `Self` on traits in doclinks
| -rw-r--r-- | crates/hir/src/attrs.rs | 66 | ||||
| -rw-r--r-- | crates/ide/src/hover/tests.rs | 72 |
2 files changed, 120 insertions, 18 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index cba1b39e52..cfb95e07c3 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -3,7 +3,8 @@ use cfg::CfgExpr; use either::Either; use hir_def::{ - AssocItemId, AttrDefId, FieldId, LifetimeParamId, ModuleDefId, TypeOrConstParamId, + AssocItemId, AttrDefId, FieldId, GenericDefId, ItemContainerId, LifetimeParamId, ModuleDefId, + TraitId, TypeOrConstParamId, attrs::{AttrFlags, Docs, IsInnerDoc}, expr_store::path::Path, item_scope::ItemInNs, @@ -22,6 +23,7 @@ use hir_ty::{ next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt}, }; use intern::Symbol; +use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -357,13 +359,46 @@ fn resolve_assoc_or_field( ns: Option<Namespace>, ) -> Option<DocLinkDef> { let path = Path::from_known_path_with_no_generic(path); - // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the - // trait itself. let base_def = resolver.resolve_path_in_type_ns_fully(db, &path)?; + let handle_trait = |id: TraitId| { + // Doc paths in this context may only resolve to an item of this trait + // (i.e. no items of its supertraits), so we need to handle them here + // independently of others. + id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { + let def = match *assoc_id { + AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), + AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), + AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + }; + DocLinkDef::ModuleDef(def) + }) + }; let ty = match base_def { TypeNs::SelfType(id) => Impl::from(id).self_ty(db), - TypeNs::GenericParam(_) => { + TypeNs::GenericParam(param) => { + let generic_params = db.generic_params(param.parent()); + if generic_params[param.local_id()].is_trait_self() { + // `Self::assoc` in traits should refer to the trait itself. + let parent_trait = |container| match container { + ItemContainerId::TraitId(trait_) => handle_trait(trait_), + _ => { + never!("container {container:?} should be a trait"); + None + } + }; + return match param.parent() { + GenericDefId::TraitId(trait_) => handle_trait(trait_), + GenericDefId::ConstId(it) => parent_trait(it.loc(db).container), + GenericDefId::FunctionId(it) => parent_trait(it.loc(db).container), + GenericDefId::TypeAliasId(it) => parent_trait(it.loc(db).container), + _ => { + never!("type param {param:?} should belong to a trait"); + None + } + }; + } + // Even if this generic parameter has some trait bounds, rustdoc doesn't // resolve `name` to trait items. return None; @@ -384,19 +419,7 @@ fn resolve_assoc_or_field( alias.ty(db) } TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db), - TypeNs::TraitId(id) => { - // Doc paths in this context may only resolve to an item of this trait - // (i.e. no items of its supertraits), so we need to handle them here - // independently of others. - return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { - let def = match *assoc_id { - AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), - AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), - AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), - }; - DocLinkDef::ModuleDef(def) - }); - } + TypeNs::TraitId(id) => return handle_trait(id), TypeNs::ModuleId(_) => { return None; } @@ -414,7 +437,14 @@ fn resolve_assoc_or_field( let variant_def = match ty.as_adt()? { Adt::Struct(it) => it.into(), Adt::Union(it) => it.into(), - Adt::Enum(_) => return None, + Adt::Enum(enum_) => { + // Can happen on `Self::Variant` (otherwise would be fully resolved by the resolver). + return enum_ + .id + .enum_variants(db) + .variant(&name) + .map(|variant| DocLinkDef::ModuleDef(ModuleDef::Variant(variant.into()))); + } }; resolve_field(db, variant_def, name, ns) } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 0b518021e3..7900a0dc99 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -11239,3 +11239,75 @@ impl<T> Foo<i32, u64> for T { "#]], ); } + +#[test] +fn doc_link_enum_self_variant() { + check( + r#" +/// - [`VariantOne$0`](Self::One) +pub enum MyEnum { + One, + Two, +} + "#, + expect* + + ```rust + ra_test_fixture::MyEnum + ``` + + ```rust + One = 0 + ``` + "#]], + ); +} + +#[test] +fn doc_link_trait_self() { + check( + r#" +/// - [`do_something$0`](Self::do_something) +pub trait MyTrait { + fn do_something(&self); +} + "#, + expect* + + ```rust + ra_test_fixture::MyTrait + ``` + + ```rust + pub trait MyTrait + pub fn do_something(&self) + ``` + "#]], + ); + check( + r#" +pub trait MyTrait { + /// - [`do_something$0`](Self::do_something) + fn do_something(&self); +} + "#, + expect* + + ```rust + ra_test_fixture::MyTrait + ``` + + ```rust + pub trait MyTrait + pub fn do_something(&self) + ``` + + --- + + * [`do_something`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/trait.MyTrait.html#tymethod.do_something) + "#]], + ); +} |