about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Nelson <jyn514@gmail.com>2020-07-05 21:20:31 -0400
committerJoshua Nelson <jyn514@gmail.com>2020-07-05 21:40:02 -0400
commite46c18768e3c0f046942b907d32b3c02c100b163 (patch)
treec94021760b3e8a5539233c589f7b806250479aa6
parentf844ea1e561475e6023282ef167e76bc973773ef (diff)
downloadrust-e46c18768e3c0f046942b907d32b3c02c100b163.tar.gz
rust-e46c18768e3c0f046942b907d32b3c02c100b163.zip
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.
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs33
-rw-r--r--src/test/rustdoc/intra-link-prim-precedence.rs12
2 files changed, 40 insertions, 5 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 8da74f375d9..41a94dad034 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<String>,
         parent_id: Option<hir::HirId>,
@@ -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,
                             &current_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,
                             &current_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,
                                 &current_item,
                                 base_node,
@@ -697,6 +719,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                             },
                             value_ns: match self.resolve(
                                 path_str,
+                                disambiguator,
                                 ValueNS,
                                 &current_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 00000000000..ca83d5e2281
--- /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;