Skip to content

Commit 8b21421

Browse files
committed
rustdoc: Track macro_rules scopes during early doc link resolution
This way links referring to `macro_rules` items are resolved correctly
1 parent 044e45c commit 8b21421

File tree

6 files changed

+99
-4
lines changed

6 files changed

+99
-4
lines changed

compiler/rustc_resolve/src/build_reduced_graph.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1267,13 +1267,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
12671267
self.insert_unused_macro(ident, def_id, item.id);
12681268
}
12691269
self.r.visibilities.insert(def_id, vis);
1270-
self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
1270+
let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
12711271
self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
12721272
parent_macro_rules_scope: parent_scope.macro_rules,
12731273
binding,
12741274
ident,
12751275
}),
1276-
))
1276+
));
1277+
self.r.macro_rules_scopes.insert(def_id, scope);
1278+
scope
12771279
} else {
12781280
let module = parent_scope.module;
12791281
let vis = match item.kind {

compiler/rustc_resolve/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ enum ScopeSet<'a> {
145145
pub struct ParentScope<'a> {
146146
pub module: Module<'a>,
147147
expansion: LocalExpnId,
148-
macro_rules: MacroRulesScopeRef<'a>,
148+
pub macro_rules: MacroRulesScopeRef<'a>,
149149
derives: &'a [ast::Path],
150150
}
151151

@@ -990,6 +990,8 @@ pub struct Resolver<'a> {
990990
/// `macro_rules` scopes *produced* by expanding the macro invocations,
991991
/// include all the `macro_rules` items and other invocations generated by them.
992992
output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
993+
/// `macro_rules` scopes produced by `macro_rules` item definitions.
994+
macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
993995
/// Helper attributes that are in scope for the given expansion.
994996
helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
995997
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
@@ -1361,6 +1363,7 @@ impl<'a> Resolver<'a> {
13611363
non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
13621364
invocation_parent_scopes: Default::default(),
13631365
output_macro_rules_scopes: Default::default(),
1366+
macro_rules_scopes: Default::default(),
13641367
helper_attrs: Default::default(),
13651368
derive_data: Default::default(),
13661369
local_macro_def_scopes: FxHashMap::default(),
@@ -1919,6 +1922,11 @@ impl<'a> Resolver<'a> {
19191922
}
19201923
}
19211924

1925+
/// For rustdoc.
1926+
pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
1927+
*self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
1928+
}
1929+
19221930
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
19231931
#[inline]
19241932
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {

src/librustdoc/passes/collect_intra_doc_links/early.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_middle::ty::{DefIdTree, Visibility};
1515
use rustc_resolve::{ParentScope, Resolver};
1616
use rustc_session::config::Externs;
1717
use rustc_session::Session;
18+
use rustc_span::symbol::sym;
1819
use rustc_span::{Symbol, SyntaxContext};
1920

2021
use std::collections::hash_map::Entry;
@@ -216,6 +217,8 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
216217
ns: Namespace,
217218
parent_scope: &ParentScope<'ra>,
218219
) -> bool {
220+
// FIXME: This caching may be incorrect in case of multiple `macro_rules`
221+
// items with the same name in the same module.
219222
self.doc_link_resolutions
220223
.entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
221224
.or_insert_with_key(|(path, ns, _)| {
@@ -307,18 +310,30 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
307310
let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
308311
let module = self.resolver.expect_module(module_def_id);
309312
let old_module = mem::replace(&mut self.parent_scope.module, module);
313+
let old_macro_rules = self.parent_scope.macro_rules;
310314
self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
311315
self.process_module_children_or_reexports(module_def_id);
312316
visit::walk_item(self, item);
317+
if item
318+
.attrs
319+
.iter()
320+
.all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
321+
{
322+
self.parent_scope.macro_rules = old_macro_rules;
323+
}
313324
self.parent_scope.module = old_module;
314325
} else {
315-
match item.kind {
326+
match &item.kind {
316327
ItemKind::Trait(..) => {
317328
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
318329
}
319330
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
320331
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
321332
}
333+
ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
334+
self.parent_scope.macro_rules =
335+
self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
336+
}
322337
_ => {}
323338
}
324339
visit::walk_item(self, item);
@@ -345,6 +360,12 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
345360
visit::walk_field_def(self, field)
346361
}
347362

363+
fn visit_block(&mut self, block: &ast::Block) {
364+
let old_macro_rules = self.parent_scope.macro_rules;
365+
visit::walk_block(self, block);
366+
self.parent_scope.macro_rules = old_macro_rules;
367+
}
368+
348369
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
349370
// then this will have to implement other visitor methods too.
350371
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// `macro_rules` scopes are respected during doc link resolution.
2+
3+
// compile-flags: --document-private-items
4+
5+
#![deny(rustdoc::broken_intra_doc_links)]
6+
7+
mod no_escape {
8+
macro_rules! before_but_limited_to_module {
9+
() => {};
10+
}
11+
}
12+
13+
/// [before_but_limited_to_module] FIXME: This error should be reported
14+
// ERROR unresolved link to `before_but_limited_to_module`
15+
/// [after] FIXME: This error should be reported
16+
// ERROR unresolved link to `after`
17+
/// [str] FIXME: This error shouldn not be reported
18+
//~^ ERROR `str` is both a builtin type and a macro
19+
fn check() {}
20+
21+
macro_rules! after {
22+
() => {};
23+
}
24+
25+
macro_rules! str {
26+
() => {};
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: `str` is both a builtin type and a macro
2+
--> $DIR/macro-rules-error.rs:17:6
3+
|
4+
LL | /// [str] FIXME: This error shouldn not be reported
5+
| ^^^ ambiguous link
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/macro-rules-error.rs:5:9
9+
|
10+
LL | #![deny(rustdoc::broken_intra_doc_links)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
help: to link to the builtin type, prefix with `prim@`
13+
|
14+
LL | /// [prim@str] FIXME: This error shouldn not be reported
15+
| +++++
16+
help: to link to the macro, add an exclamation mark
17+
|
18+
LL | /// [str!] FIXME: This error shouldn not be reported
19+
| +
20+
21+
error: aborting due to previous error
22+

src/test/rustdoc-ui/intra-doc/macro-rules.rs

+15
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,18 @@ macro_rules! foo {
77

88
/// [foo!]
99
pub fn baz() {}
10+
11+
#[macro_use]
12+
mod macros {
13+
macro_rules! escaping {
14+
() => {};
15+
}
16+
}
17+
18+
pub mod inner {
19+
/// [foo!]
20+
/// [escaping]
21+
pub fn baz() {
22+
foo!();
23+
}
24+
}

0 commit comments

Comments
 (0)