about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs6
-rw-r--r--compiler/rustc_resolve/src/lib.rs10
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs23
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules-error.rs27
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr22
-rw-r--r--src/test/rustdoc-ui/intra-doc/macro-rules.rs15
6 files changed, 99 insertions, 4 deletions
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 650a0dc82ce..f1e07de77f8 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1267,13 +1267,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 self.insert_unused_macro(ident, def_id, item.id);
             }
             self.r.visibilities.insert(def_id, vis);
-            self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+            let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
                 self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
                     parent_macro_rules_scope: parent_scope.macro_rules,
                     binding,
                     ident,
                 }),
-            ))
+            ));
+            self.r.macro_rules_scopes.insert(def_id, scope);
+            scope
         } else {
             let module = parent_scope.module;
             let vis = match item.kind {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 6142283e2e1..c917ea5a08f 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -145,7 +145,7 @@ enum ScopeSet<'a> {
 pub struct ParentScope<'a> {
     pub module: Module<'a>,
     expansion: LocalExpnId,
-    macro_rules: MacroRulesScopeRef<'a>,
+    pub macro_rules: MacroRulesScopeRef<'a>,
     derives: &'a [ast::Path],
 }
 
@@ -990,6 +990,8 @@ pub struct Resolver<'a> {
     /// `macro_rules` scopes *produced* by expanding the macro invocations,
     /// include all the `macro_rules` items and other invocations generated by them.
     output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
+    /// `macro_rules` scopes produced by `macro_rules` item definitions.
+    macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
     /// Helper attributes that are in scope for the given expansion.
     helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
     /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
@@ -1361,6 +1363,7 @@ impl<'a> Resolver<'a> {
             non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
             invocation_parent_scopes: Default::default(),
             output_macro_rules_scopes: Default::default(),
+            macro_rules_scopes: Default::default(),
             helper_attrs: Default::default(),
             derive_data: Default::default(),
             local_macro_def_scopes: FxHashMap::default(),
@@ -1919,6 +1922,11 @@ impl<'a> Resolver<'a> {
         }
     }
 
+    /// For rustdoc.
+    pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
+        *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
+    }
+
     /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
     #[inline]
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index 974ef7a537a..3858c1cb056 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -15,6 +15,7 @@ use rustc_middle::ty::{DefIdTree, Visibility};
 use rustc_resolve::{ParentScope, Resolver};
 use rustc_session::config::Externs;
 use rustc_session::Session;
+use rustc_span::symbol::sym;
 use rustc_span::{Symbol, SyntaxContext};
 
 use std::collections::hash_map::Entry;
@@ -216,6 +217,8 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
         ns: Namespace,
         parent_scope: &ParentScope<'ra>,
     ) -> bool {
+        // FIXME: This caching may be incorrect in case of multiple `macro_rules`
+        // items with the same name in the same module.
         self.doc_link_resolutions
             .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
             .or_insert_with_key(|(path, ns, _)| {
@@ -307,18 +310,30 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
             let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
             let module = self.resolver.expect_module(module_def_id);
             let old_module = mem::replace(&mut self.parent_scope.module, module);
+            let old_macro_rules = self.parent_scope.macro_rules;
             self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
             self.process_module_children_or_reexports(module_def_id);
             visit::walk_item(self, item);
+            if item
+                .attrs
+                .iter()
+                .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
+            {
+                self.parent_scope.macro_rules = old_macro_rules;
+            }
             self.parent_scope.module = old_module;
         } else {
-            match item.kind {
+            match &item.kind {
                 ItemKind::Trait(..) => {
                     self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
                 }
                 ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
                     self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
                 }
+                ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
+                    self.parent_scope.macro_rules =
+                        self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
+                }
                 _ => {}
             }
             visit::walk_item(self, item);
@@ -345,6 +360,12 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
         visit::walk_field_def(self, field)
     }
 
+    fn visit_block(&mut self, block: &ast::Block) {
+        let old_macro_rules = self.parent_scope.macro_rules;
+        visit::walk_block(self, block);
+        self.parent_scope.macro_rules = old_macro_rules;
+    }
+
     // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
     // then this will have to implement other visitor methods too.
 }
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs b/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
new file mode 100644
index 00000000000..84d63c20aa8
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
@@ -0,0 +1,27 @@
+// `macro_rules` scopes are respected during doc link resolution.
+
+// compile-flags: --document-private-items
+
+#![deny(rustdoc::broken_intra_doc_links)]
+
+mod no_escape {
+    macro_rules! before_but_limited_to_module {
+        () => {};
+    }
+}
+
+/// [before_but_limited_to_module] FIXME: This error should be reported
+// ERROR unresolved link to `before_but_limited_to_module`
+/// [after] FIXME: This error should be reported
+// ERROR unresolved link to `after`
+/// [str] FIXME: This error shouldn not be reported
+//~^ ERROR `str` is both a builtin type and a macro
+fn check() {}
+
+macro_rules! after {
+    () => {};
+}
+
+macro_rules! str {
+    () => {};
+}
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr b/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
new file mode 100644
index 00000000000..4b984f4f6c0
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
@@ -0,0 +1,22 @@
+error: `str` is both a builtin type and a macro
+  --> $DIR/macro-rules-error.rs:17:6
+   |
+LL | /// [str] FIXME: This error shouldn not be reported
+   |      ^^^ ambiguous link
+   |
+note: the lint level is defined here
+  --> $DIR/macro-rules-error.rs:5:9
+   |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: to link to the builtin type, prefix with `prim@`
+   |
+LL | /// [prim@str] FIXME: This error shouldn not be reported
+   |      +++++
+help: to link to the macro, add an exclamation mark
+   |
+LL | /// [str!] FIXME: This error shouldn not be reported
+   |         +
+
+error: aborting due to previous error
+
diff --git a/src/test/rustdoc-ui/intra-doc/macro-rules.rs b/src/test/rustdoc-ui/intra-doc/macro-rules.rs
index a14e4bdf1d7..3aeb370ef6d 100644
--- a/src/test/rustdoc-ui/intra-doc/macro-rules.rs
+++ b/src/test/rustdoc-ui/intra-doc/macro-rules.rs
@@ -7,3 +7,18 @@ macro_rules! foo {
 
 /// [foo!]
 pub fn baz() {}
+
+#[macro_use]
+mod macros {
+    macro_rules! escaping {
+        () => {};
+    }
+}
+
+pub mod inner {
+    /// [foo!]
+    /// [escaping]
+    pub fn baz() {
+        foo!();
+    }
+}