about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs45
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs53
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr127
3 files changed, 223 insertions, 2 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index bf7a43236e0..3d8daf4e9a5 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -574,9 +574,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             };
             let resolved_self;
             let mut path_str;
+            let mut disambiguator = None;
             let (res, fragment) = {
                 let mut kind = None;
-                let mut disambiguator = None;
                 path_str = if let Some(prefix) =
                     ["struct@", "enum@", "type@", "trait@", "union@", "module@", "mod@"]
                         .iter()
@@ -595,6 +595,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                     link.trim_start_matches(prefix)
                 } else if link.ends_with("!()") {
                     kind = Some(MacroNS);
+                    disambiguator = Some("bang");
                     link.trim_end_matches("!()")
                 } else if link.ends_with("()") {
                     kind = Some(ValueNS);
@@ -610,7 +611,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                     link.trim_start_matches("derive@")
                 } else if link.ends_with('!') {
                     kind = Some(MacroNS);
-                    disambiguator = Some("macro");
+                    disambiguator = Some("bang");
                     link.trim_end_matches('!')
                 } else {
                     &link[..]
@@ -789,6 +790,46 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             } else {
                 debug!("intra-doc link to {} resolved to {:?}", path_str, res);
 
+                // Disallow e.g. linking to enums with `struct@`
+                if let Res::Def(kind, id) = res {
+                    debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
+                    // NOTE: this relies on the fact that `''` is never parsed as a disambiguator
+                    // NOTE: this needs to be kept in sync with the disambiguator parsing
+                    match (kind, disambiguator.unwrap_or_default().trim_end_matches("@")) {
+                        | (DefKind::Struct, "struct")
+                        | (DefKind::Enum, "enum")
+                        | (DefKind::Trait, "trait")
+                        | (DefKind::Union, "union")
+                        | (DefKind::Mod, "mod" | "module")
+                        | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, "const")
+                        | (DefKind::Static, "static")
+                        // NOTE: this allows 'method' to mean both normal functions and associated functions
+                        // This can't cause ambiguity because both are in the same namespace.
+                        | (DefKind::Fn | DefKind::AssocFn, "fn" | "function" | "method")
+                        | (DefKind::Macro(MacroKind::Bang), "bang")
+                        | (DefKind::Macro(MacroKind::Derive), "derive")
+                        // These are namespaces; allow anything in the namespace to match
+                        | (_, "type" | "macro" | "value")
+                        // If no disambiguator given, allow anything
+                        | (_, "")
+                        // All of these are valid, so do nothing
+                        => {}
+                        (_, disambiguator) => {
+                            // The resolved item did not match the disambiguator; give a better error than 'not found'
+                            let msg = format!("unresolved link to `{}`", path_str);
+                            report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
+                                let msg = format!("this item resolved to {} {}, which did not match the disambiguator '{}'", kind.article(), kind.descr(id), disambiguator);
+                                if let Some(sp) = sp {
+                                    diag.span_note(sp, &msg);
+                                } else {
+                                    diag.note(&msg);
+                                }
+                            });
+                            continue;
+                        }
+                    }
+                }
+
                 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
                 if let Some((src_id, dst_id)) = res
                     .opt_def_id()
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
new file mode 100644
index 00000000000..6ab815eab54
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
@@ -0,0 +1,53 @@
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+pub enum S {}
+
+macro_rules! m {
+    () => {};
+}
+
+static s: usize = 0;
+const c: usize = 0;
+
+trait T {}
+
+/// Link to [struct@S]
+//~^ ERROR unresolved link to `S`
+//~| NOTE did not match
+
+/// Link to [mod@S]
+//~^ ERROR unresolved link to `S`
+//~| NOTE did not match
+
+/// Link to [union@S]
+//~^ ERROR unresolved link to `S`
+//~| NOTE did not match
+
+/// Link to [trait@S]
+//~^ ERROR unresolved link to `S`
+//~| NOTE did not match
+
+/// Link to [struct@T]
+//~^ ERROR unresolved link to `T`
+//~| NOTE did not match
+
+/// Link to [derive@m]
+//~^ ERROR unresolved link to `m`
+//~| NOTE did not match
+
+/// Link to [const@s]
+//~^ ERROR unresolved link to `s`
+//~| NOTE did not match
+
+/// Link to [static@c]
+//~^ ERROR unresolved link to `c`
+//~| NOTE did not match
+
+/// Link to [fn@c]
+//~^ ERROR unresolved link to `c`
+//~| NOTE did not match
+
+/// Link to [c()]
+//~^ ERROR unresolved link to `c`
+//~| NOTE did not match
+pub fn f() {}
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
new file mode 100644
index 00000000000..21276ecb20a
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
@@ -0,0 +1,127 @@
+error: unresolved link to `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:16:14
+   |
+LL | /// Link to [struct@S]
+   |              ^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+note: this item resolved to an enum, which did not match the disambiguator 'struct'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:16:14
+   |
+LL | /// Link to [struct@S]
+   |              ^^^^^^^^
+
+error: unresolved link to `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:20:14
+   |
+LL | /// Link to [mod@S]
+   |              ^^^^^
+   |
+note: this item resolved to an enum, which did not match the disambiguator 'mod'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:20:14
+   |
+LL | /// Link to [mod@S]
+   |              ^^^^^
+
+error: unresolved link to `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
+   |
+LL | /// Link to [union@S]
+   |              ^^^^^^^
+   |
+note: this item resolved to an enum, which did not match the disambiguator 'union'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
+   |
+LL | /// Link to [union@S]
+   |              ^^^^^^^
+
+error: unresolved link to `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:28:14
+   |
+LL | /// Link to [trait@S]
+   |              ^^^^^^^
+   |
+note: this item resolved to an enum, which did not match the disambiguator 'trait'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:28:14
+   |
+LL | /// Link to [trait@S]
+   |              ^^^^^^^
+
+error: unresolved link to `T`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:32:14
+   |
+LL | /// Link to [struct@T]
+   |              ^^^^^^^^
+   |
+note: this item resolved to a trait, which did not match the disambiguator 'struct'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:32:14
+   |
+LL | /// Link to [struct@T]
+   |              ^^^^^^^^
+
+error: unresolved link to `m`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:36:14
+   |
+LL | /// Link to [derive@m]
+   |              ^^^^^^^^
+   |
+note: this item resolved to a macro, which did not match the disambiguator 'derive'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:36:14
+   |
+LL | /// Link to [derive@m]
+   |              ^^^^^^^^
+
+error: unresolved link to `s`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:40:14
+   |
+LL | /// Link to [const@s]
+   |              ^^^^^^^
+   |
+note: this item resolved to a static, which did not match the disambiguator 'const'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:40:14
+   |
+LL | /// Link to [const@s]
+   |              ^^^^^^^
+
+error: unresolved link to `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
+   |
+LL | /// Link to [static@c]
+   |              ^^^^^^^^
+   |
+note: this item resolved to a constant, which did not match the disambiguator 'static'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
+   |
+LL | /// Link to [static@c]
+   |              ^^^^^^^^
+
+error: unresolved link to `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:48:14
+   |
+LL | /// Link to [fn@c]
+   |              ^^^^
+   |
+note: this item resolved to a constant, which did not match the disambiguator 'fn'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:48:14
+   |
+LL | /// Link to [fn@c]
+   |              ^^^^
+
+error: unresolved link to `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:52:14
+   |
+LL | /// Link to [c()]
+   |              ^^^
+   |
+note: this item resolved to a constant, which did not match the disambiguator 'fn'
+  --> $DIR/intra-links-disambiguator-mismatch.rs:52:14
+   |
+LL | /// Link to [c()]
+   |              ^^^
+
+error: aborting due to 10 previous errors
+