about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-06-11 13:16:08 +0200
committerGitHub <noreply@github.com>2020-06-11 13:16:08 +0200
commitd9cf7a178446ea0da1842de83c84dc687f6b2d6f (patch)
treebd1b4bb314199a8d310982540701cfaab006108b
parent6cc757e698ec7994411ef327f463726b070a3410 (diff)
parent34c6b38e68f2e88bcc6c943494a7c05d35a71f17 (diff)
downloadrust-d9cf7a178446ea0da1842de83c84dc687f6b2d6f.tar.gz
rust-d9cf7a178446ea0da1842de83c84dc687f6b2d6f.zip
Rollup merge of #73183 - Manishearth:intra-doc-macro, r=GuillaumeGomez
Support proc macros in intra doc link resolution

The feature was written pre-proc macro resolution, so it only supported the wacky MBE resolution rules. This adds support for proc macros as well.

cc @GuillaumeGomez

Fixes #73173
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs100
-rw-r--r--src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs35
-rw-r--r--src/test/rustdoc/intra-link-proc-macro.rs27
3 files changed, 134 insertions, 28 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index ede48f7eed4..f5b2f1bb5b1 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -12,6 +12,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty;
 use rustc_resolve::ParentScope;
 use rustc_session::lint;
+use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::Ident;
 use rustc_span::symbol::Symbol;
 use rustc_span::DUMMY_SP;
@@ -122,6 +123,42 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
         }
     }
 
+    /// Resolves a string as a macro.
+    fn macro_resolve(&self, path_str: &str, parent_id: Option<hir::HirId>) -> Option<Res> {
+        let cx = self.cx;
+        let path = ast::Path::from_ident(Ident::from_str(path_str));
+        cx.enter_resolver(|resolver| {
+            if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
+                &path,
+                None,
+                &ParentScope::module(resolver.graph_root()),
+                false,
+                false,
+            ) {
+                if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
+                    return Some(res.map_id(|_| panic!("unexpected id")));
+                }
+            }
+            if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
+                return Some(res.map_id(|_| panic!("unexpected id")));
+            }
+            if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
+                let module_id = cx.tcx.hir().local_def_id(module_id);
+                if let Ok((_, res)) =
+                    resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
+                {
+                    // don't resolve builtins like `#[derive]`
+                    if let Res::Def(..) = res {
+                        let res = res.map_id(|_| panic!("unexpected node_id"));
+                        return Some(res);
+                    }
+                }
+            } else {
+                debug!("attempting to resolve item without parent module: {}", path_str);
+            }
+            None
+        })
+    }
     /// Resolves a string as a path within a particular namespace. Also returns an optional
     /// URL fragment in the case of variants and methods.
     fn resolve(
@@ -371,6 +408,22 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
     }
 }
 
+/// Check for resolve collisions between a trait and its derive
+///
+/// These are common and we should just resolve to the trait in that case
+fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
+    if let PerNS {
+        type_ns: Some((Res::Def(DefKind::Trait, _), _)),
+        macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
+        ..
+    } = *ns
+    {
+        true
+    } else {
+        false
+    }
+}
+
 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
         let item_hir_id = if item.is_mod() {
@@ -532,6 +585,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                 } else if link.starts_with("macro@") {
                     kind = Some(MacroNS);
                     link.trim_start_matches("macro@")
+                } else if link.starts_with("derive@") {
+                    kind = Some(MacroNS);
+                    link.trim_start_matches("derive@")
                 } else if link.ends_with('!') {
                     kind = Some(MacroNS);
                     link.trim_end_matches('!')
@@ -614,8 +670,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                     }
                     None => {
                         // Try everything!
-                        let candidates = PerNS {
-                            macro_ns: macro_resolve(cx, path_str)
+                        let mut candidates = PerNS {
+                            macro_ns: self
+                                .macro_resolve(path_str, base_node)
                                 .map(|res| (res, extra_fragment.clone())),
                             type_ns: match self.resolve(
                                 path_str,
@@ -668,10 +725,16 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                             continue;
                         }
 
-                        let is_unambiguous = candidates.clone().present_items().count() == 1;
-                        if is_unambiguous {
+                        let len = candidates.clone().present_items().count();
+
+                        if len == 1 {
                             candidates.present_items().next().unwrap()
+                        } else if len == 2 && is_derive_trait_collision(&candidates) {
+                            candidates.type_ns.unwrap()
                         } else {
+                            if is_derive_trait_collision(&candidates) {
+                                candidates.macro_ns = None;
+                            }
                             ambiguity_error(
                                 cx,
                                 &item,
@@ -684,7 +747,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                         }
                     }
                     Some(MacroNS) => {
-                        if let Some(res) = macro_resolve(cx, path_str) {
+                        if let Some(res) = self.macro_resolve(path_str, base_node) {
                             (res, extra_fragment)
                         } else {
                             resolution_failure(cx, &item, path_str, &dox, link_range);
@@ -727,28 +790,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
     }
 }
 
-/// Resolves a string as a macro.
-fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option<Res> {
-    let path = ast::Path::from_ident(Ident::from_str(path_str));
-    cx.enter_resolver(|resolver| {
-        if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
-            &path,
-            None,
-            &ParentScope::module(resolver.graph_root()),
-            false,
-            false,
-        ) {
-            if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
-                return Some(res.map_id(|_| panic!("unexpected id")));
-            }
-        }
-        if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
-            return Some(res.map_id(|_| panic!("unexpected id")));
-        }
-        None
-    })
-}
-
 fn build_diagnostic(
     cx: &DocContext<'_>,
     item: &Item,
@@ -916,7 +957,7 @@ fn ambiguity_error(
                             Res::Def(DefKind::AssocFn | DefKind::Fn, _) => {
                                 ("add parentheses", format!("{}()", path_str))
                             }
-                            Res::Def(DefKind::Macro(..), _) => {
+                            Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
                                 ("add an exclamation mark", format!("{}!", path_str))
                             }
                             _ => {
@@ -930,6 +971,9 @@ fn ambiguity_error(
                                     (Res::Def(DefKind::Mod, _), _) => "module",
                                     (_, TypeNS) => "type",
                                     (_, ValueNS) => "value",
+                                    (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => {
+                                        "derive"
+                                    }
                                     (_, MacroNS) => "macro",
                                 };
 
diff --git a/src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs b/src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs
new file mode 100644
index 00000000000..04a431d9902
--- /dev/null
+++ b/src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs
@@ -0,0 +1,35 @@
+// force-host
+// no-prefer-dynamic
+// compile-flags: --crate-type proc-macro
+
+#![crate_type="proc-macro"]
+#![crate_name="intra_link_proc_macro_macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(DeriveA)]
+pub fn a_derive(input: TokenStream) -> TokenStream {
+    input
+}
+
+#[proc_macro_derive(DeriveB)]
+pub fn b_derive(input: TokenStream) -> TokenStream {
+    input
+}
+
+#[proc_macro_derive(DeriveTrait)]
+pub fn trait_derive(input: TokenStream) -> TokenStream {
+    input
+}
+
+#[proc_macro_attribute]
+pub fn attr_a(input: TokenStream, _args: TokenStream) -> TokenStream {
+    input
+}
+
+#[proc_macro_attribute]
+pub fn attr_b(input: TokenStream, _args: TokenStream) -> TokenStream {
+    input
+}
diff --git a/src/test/rustdoc/intra-link-proc-macro.rs b/src/test/rustdoc/intra-link-proc-macro.rs
new file mode 100644
index 00000000000..7b6ea5d60f8
--- /dev/null
+++ b/src/test/rustdoc/intra-link-proc-macro.rs
@@ -0,0 +1,27 @@
+// aux-build:intra-link-proc-macro-macro.rs
+// build-aux-docs
+#![deny(intra_doc_link_resolution_failure)]
+
+extern crate intra_link_proc_macro_macro;
+
+
+pub use intra_link_proc_macro_macro::{DeriveA, attr_a};
+use intra_link_proc_macro_macro::{DeriveB, attr_b};
+
+// @has intra_link_proc_macro/struct.Foo.html
+// @has - '//a/@href' '../intra_link_proc_macro/derive.DeriveA.html'
+// @has - '//a/@href' '../intra_link_proc_macro/attr.attr_a.html'
+// @has - '//a/@href' '../intra_link_proc_macro/trait.DeriveTrait.html'
+// @has - '//a/@href' '../intra_link_proc_macro_macro/derive.DeriveB.html'
+// @has - '//a/@href' '../intra_link_proc_macro_macro/attr.attr_b.html'
+/// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait]
+pub struct Foo;
+
+// @has intra_link_proc_macro/struct.Bar.html
+// @has - '//a/@href' '../intra_link_proc_macro/derive.DeriveA.html'
+// @has - '//a/@href' '../intra_link_proc_macro/attr.attr_a.html'
+/// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a)
+pub struct Bar;
+
+// this should not cause ambiguity errors
+pub trait DeriveTrait {}