diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 05f3b598ecdf4..adb7fc3eb9cff 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -431,6 +431,43 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { look_for_tests(&cx, &dox, &item, true); + // find item's parent to resolve `Self` in item's docs below + let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { + let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); + let item_parent = self.cx.tcx.hir().find(parent_hir); + match item_parent { + Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl { + self_ty: + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { segments, .. }, + )), + .. + }, + .. + }, + .. + })) => segments.first().and_then(|seg| Some(seg.ident.to_string())), + Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Enum(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Struct(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Union(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Trait(..), .. + })) => Some(ident.to_string()), + _ => None, + } + }); + for (ori_link, link_range) in markdown_links(&dox) { // Bail early for real links. if ori_link.contains('/') { @@ -467,7 +504,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { }; let (res, fragment) = { let mut kind = None; - let path_str = if let Some(prefix) = + let mut path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"] .iter() .find(|p| link.starts_with(**p)) @@ -521,6 +558,15 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let base_node = if item.is_mod() && item.attrs.inner_docs { None } else { parent_node }; + let resolved_self; + // replace `Self` with suitable item's parent name + if path_str.starts_with("Self::") { + if let Some(ref name) = parent_name { + resolved_self = format!("{}::{}", name, &path_str[6..]); + path_str = &resolved_self; + } + } + match kind { Some(ns @ ValueNS) => { match self.resolve( @@ -529,7 +575,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { @@ -552,7 +598,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { @@ -577,7 +623,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ¤t_item, base_node, &extra_fragment, - None, + Some(&item), ) { Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); diff --git a/src/test/rustdoc/intra-link-self.rs b/src/test/rustdoc/intra-link-self.rs index acf975f5c738e..97752d5cfcb5c 100644 --- a/src/test/rustdoc/intra-link-self.rs +++ b/src/test/rustdoc/intra-link-self.rs @@ -1,5 +1,7 @@ #![crate_name = "foo"] +// ignore-tidy-linelength + // @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' // @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' @@ -27,3 +29,89 @@ impl Bar { unimplemented!() } } + +pub struct MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + + /// [`struct_field`] + /// + /// [`struct_field`]: Self::struct_field + pub struct_field: u8, +} + +pub enum MyEnum { + // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#EnumVariant.v' + + /// [`EnumVariant`] + /// + /// [`EnumVariant`]: Self::EnumVariant + EnumVariant, +} + +pub union MyUnion { + // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + + /// [`union_field`] + /// + /// [`union_field`]: Self::union_field + pub union_field: f32, +} + +pub trait MyTrait { + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 1; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() {} +} + +impl MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + + /// [`for_impl`] + /// + /// [`for_impl`]: Self::for_impl + pub fn for_impl() { + unimplemented!() + } +} + +impl MyTrait for MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType = u32; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 10; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() { + unimplemented!() + } +}