From e46c18768e3c0f046942b907d32b3c02c100b163 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 5 Jul 2020 21:20:31 -0400 Subject: [PATCH] Always resolve type@primitive as a primitive, not a module Previously, if there were a module in scope with the same name as the primitive, that would take precedence. Coupled with https://github.com/rust-lang/rust/issues/58699, this made it impossible to link to the primitive when that module was in scope. This approach could be extended so that `struct@foo` would no longer resolve to any type, etc. However, it could not be used for glob imports: ```rust pub mod foo { pub struct Bar; } pub enum Bar {} use foo::*; // This is expected to link to `inner::Bar`, but instead it will link to the enum. /// Link to [struct@Bar] pub struct MyDocs; ``` The reason for this is that this change does not affect the resolution algorithm of rustc_resolve at all. The only reason we could special-case primitives is because we have a list of all possible primitives ahead of time. --- .../passes/collect_intra_doc_links.rs | 33 ++++++++++++++++--- .../rustdoc/intra-link-prim-precedence.rs | 12 +++++++ 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/test/rustdoc/intra-link-prim-precedence.rs diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8da74f375d9ce..41a94dad0344d 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -164,6 +164,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { fn resolve( &self, path_str: &str, + disambiguator: Option<&str>, ns: Namespace, current_item: &Option, parent_id: Option, @@ -203,11 +204,22 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } return Ok((res, Some(path_str.to_owned()))); } - other => { - debug!( - "failed to resolve {} in namespace {:?} (got {:?})", - path_str, ns, other - ); + Res::Def(DefKind::Mod, _) => { + // This resolved to a module, but if we were passed `type@`, + // we want primitive types to take precedence instead. + if disambiguator == Some("type") { + if let Some(prim) = is_primitive(path_str, ns) { + if extra_fragment.is_some() { + return Err(ErrorKind::AnchorFailure( + "primitive types cannot be followed by anchors", + )); + } + return Ok((prim, Some(path_str.to_owned()))); + } + } + return Ok((res, extra_fragment.clone())); + } + _ => { return Ok((res, extra_fragment.clone())); } }; @@ -566,11 +578,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let mut path_str; let (res, fragment) = { let mut kind = None; + let mut disambiguator = None; path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"] .iter() .find(|p| link.starts_with(**p)) { kind = Some(TypeNS); + disambiguator = Some(&prefix[..prefix.len() - 1]); link.trim_start_matches(prefix) } else if let Some(prefix) = [ "const@", @@ -586,18 +600,23 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { .find(|p| link.starts_with(**p)) { kind = Some(ValueNS); + disambiguator = Some(&prefix[..prefix.len() - 1]); link.trim_start_matches(prefix) } else if link.ends_with("()") { kind = Some(ValueNS); + disambiguator = Some("fn"); link.trim_end_matches("()") } else if link.starts_with("macro@") { kind = Some(MacroNS); + disambiguator = Some("macro"); link.trim_start_matches("macro@") } else if link.starts_with("derive@") { kind = Some(MacroNS); + disambiguator = Some("derive"); link.trim_start_matches("derive@") } else if link.ends_with('!') { kind = Some(MacroNS); + disambiguator = Some("macro"); link.trim_end_matches('!') } else { &link[..] @@ -634,6 +653,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Some(ns @ ValueNS) => { match self.resolve( path_str, + disambiguator, ns, ¤t_item, base_node, @@ -657,6 +677,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Some(ns @ TypeNS) => { match self.resolve( path_str, + disambiguator, ns, ¤t_item, base_node, @@ -683,6 +704,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { .map(|res| (res, extra_fragment.clone())), type_ns: match self.resolve( path_str, + disambiguator, TypeNS, ¤t_item, base_node, @@ -697,6 +719,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { }, value_ns: match self.resolve( path_str, + disambiguator, ValueNS, ¤t_item, base_node, diff --git a/src/test/rustdoc/intra-link-prim-precedence.rs b/src/test/rustdoc/intra-link-prim-precedence.rs new file mode 100644 index 0000000000000..ca83d5e2281a7 --- /dev/null +++ b/src/test/rustdoc/intra-link-prim-precedence.rs @@ -0,0 +1,12 @@ +// ignore-tidy-linelength +#![deny(intra_doc_resolution_failure)] + +pub mod char {} + +/// See also [type@char] +// @has intra_link_prim_precedence/struct.MyString.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html' +pub struct MyString; + +/// See also [char] +// @has intra_link_prim_precedence/struct.MyString2.html '//a/@href' 'intra_link_prim_precedence/char/index.html' +pub struct MyString2;