Skip to content

Commit 4dd8b42

Browse files
committed
Auto merge of rust-lang#96521 - petrochenkov:docrules, r=notriddle,GuillaumeGomez
rustdoc: Resolve doc links referring to `macro_rules` items cc rust-lang#81633 UPD: the fallback to considering *all* `macro_rules` in the crate for unresolved names is not removed in this PR, it will be removed separately and will be run through crater.
2 parents a933de8 + 6083db7 commit 4dd8b42

File tree

11 files changed

+175
-41
lines changed

11 files changed

+175
-41
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

+15-12
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ enum ScopeSet<'a> {
143143
/// but not for late resolution yet.
144144
#[derive(Clone, Copy, Debug)]
145145
pub struct ParentScope<'a> {
146-
module: Module<'a>,
146+
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(),
@@ -1874,25 +1877,25 @@ impl<'a> Resolver<'a> {
18741877
&mut self,
18751878
path_str: &str,
18761879
ns: Namespace,
1877-
mut module_id: DefId,
1880+
mut parent_scope: ParentScope<'a>,
18781881
) -> Option<Res> {
18791882
let mut segments =
18801883
Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
18811884
if let Some(segment) = segments.first_mut() {
18821885
if segment.ident.name == kw::Crate {
18831886
// FIXME: `resolve_path` always resolves `crate` to the current crate root, but
1884-
// rustdoc wants it to resolve to the `module_id`'s crate root. This trick of
1887+
// rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
18851888
// replacing `crate` with `self` and changing the current module should achieve
18861889
// the same effect.
18871890
segment.ident.name = kw::SelfLower;
1888-
module_id = module_id.krate.as_def_id();
1891+
parent_scope.module =
1892+
self.expect_module(parent_scope.module.def_id().krate.as_def_id());
18891893
} else if segment.ident.name == kw::Empty {
18901894
segment.ident.name = kw::PathRoot;
18911895
}
18921896
}
18931897

1894-
let module = self.expect_module(module_id);
1895-
match self.maybe_resolve_path(&segments, Some(ns), &ParentScope::module(module, self)) {
1898+
match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) {
18961899
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
18971900
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
18981901
Some(path_res.base_res())
@@ -1904,11 +1907,6 @@ impl<'a> Resolver<'a> {
19041907
}
19051908
}
19061909

1907-
// For rustdoc.
1908-
pub fn graph_root(&self) -> Module<'a> {
1909-
self.graph_root
1910-
}
1911-
19121910
// For rustdoc.
19131911
pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> {
19141912
mem::take(&mut self.all_macro_rules)
@@ -1924,6 +1922,11 @@ impl<'a> Resolver<'a> {
19241922
}
19251923
}
19261924

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+
19271930
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
19281931
#[inline]
19291932
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {

library/core/src/macros/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,8 @@ macro_rules! unreachable {
640640
///
641641
/// Like `panic!`, this macro has a second form for displaying custom values.
642642
///
643+
/// [`todo!`]: crate::todo
644+
///
643645
/// # Examples
644646
///
645647
/// Say we have a trait `Foo`:

library/std/src/macros.rs

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ macro_rules! panic {
3131
/// [`eprint!`] instead to print error and progress messages.
3232
///
3333
/// [flush]: crate::io::Write::flush
34+
/// [`println!`]: crate::println
35+
/// [`eprint!`]: crate::eprint
3436
///
3537
/// # Panics
3638
///
@@ -77,6 +79,7 @@ macro_rules! print {
7779
/// [`eprintln!`] instead to print error and progress messages.
7880
///
7981
/// [`std::fmt`]: crate::fmt
82+
/// [`eprintln!`]: crate::eprintln
8083
///
8184
/// # Panics
8285
///
@@ -146,6 +149,7 @@ macro_rules! eprint {
146149
///
147150
/// [`io::stderr`]: crate::io::stderr
148151
/// [`io::stdout`]: crate::io::stdout
152+
/// [`println!`]: crate::println
149153
///
150154
/// # Panics
151155
///

library/std/src/thread/local.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use crate::fmt;
2121
/// The [`with`] method yields a reference to the contained value which cannot be
2222
/// sent across threads or escape the given closure.
2323
///
24+
/// [`thread_local!`]: crate::thread_local
25+
///
2426
/// # Initialization and Destruction
2527
///
2628
/// Initialization is dynamically performed on the first call to [`with`]

library/std/src/thread/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
//! [`Cell`]: crate::cell::Cell
147147
//! [`RefCell`]: crate::cell::RefCell
148148
//! [`with`]: LocalKey::with
149+
//! [`thread_local!`]: crate::thread_local
149150
150151
#![stable(feature = "rust1", since = "1.0.0")]
151152
#![deny(unsafe_op_in_unsafe_fn)]

src/librustdoc/passes/collect_intra_doc_links.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
1212
use rustc_hir::Mutability;
1313
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
1414
use rustc_middle::{bug, span_bug, ty};
15+
use rustc_resolve::ParentScope;
1516
use rustc_session::lint::Lint;
1617
use rustc_span::hygiene::MacroKind;
1718
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -564,7 +565,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
564565
.copied()
565566
.unwrap_or_else(|| {
566567
self.cx.enter_resolver(|resolver| {
567-
resolver.resolve_rustdoc_path(path_str, ns, module_id)
568+
let parent_scope =
569+
ParentScope::module(resolver.expect_module(module_id), resolver);
570+
resolver.resolve_rustdoc_path(path_str, ns, parent_scope)
568571
})
569572
})
570573
.and_then(|res| res.try_into().ok())

src/librustdoc/passes/collect_intra_doc_links/early.rs

+79-26
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
use crate::clean::Attributes;
22
use crate::core::ResolverCaches;
33
use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
4-
use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
4+
use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
55

66
use rustc_ast::visit::{self, AssocCtxt, Visitor};
77
use rustc_ast::{self as ast, ItemKind};
88
use rustc_ast_lowering::ResolverAstLowering;
99
use rustc_data_structures::fx::FxHashMap;
1010
use rustc_hir::def::Namespace::*;
1111
use rustc_hir::def::{DefKind, Namespace, Res};
12-
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, CRATE_DEF_ID};
12+
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
1313
use rustc_hir::TraitCandidate;
1414
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;
@@ -27,10 +28,12 @@ crate fn early_resolve_intra_doc_links(
2728
externs: Externs,
2829
document_private_items: bool,
2930
) -> ResolverCaches {
31+
let parent_scope =
32+
ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
3033
let mut link_resolver = EarlyDocLinkResolver {
3134
resolver,
3235
sess,
33-
current_mod: CRATE_DEF_ID,
36+
parent_scope,
3437
visited_mods: Default::default(),
3538
markdown_links: Default::default(),
3639
doc_link_resolutions: Default::default(),
@@ -52,7 +55,7 @@ crate fn early_resolve_intra_doc_links(
5255
// DO NOT REMOVE THIS without first testing on the reproducer in
5356
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
5457
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
55-
link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
58+
link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
5659
}
5760

5861
ResolverCaches {
@@ -72,7 +75,7 @@ fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes
7275
struct EarlyDocLinkResolver<'r, 'ra> {
7376
resolver: &'r mut Resolver<'ra>,
7477
sess: &'r Session,
75-
current_mod: LocalDefId,
78+
parent_scope: ParentScope<'ra>,
7679
visited_mods: DefIdSet,
7780
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
7881
doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
@@ -82,7 +85,7 @@ struct EarlyDocLinkResolver<'r, 'ra> {
8285
document_private_items: bool,
8386
}
8487

85-
impl EarlyDocLinkResolver<'_, '_> {
88+
impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
8689
fn add_traits_in_scope(&mut self, def_id: DefId) {
8790
// Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
8891
// Keys in the `traits_in_scope` cache are always module IDs.
@@ -205,34 +208,64 @@ impl EarlyDocLinkResolver<'_, '_> {
205208
if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
206209
return;
207210
}
208-
let module_id = self.current_mod.to_def_id();
209-
self.resolve_doc_links(doc_attrs(attrs.iter()), module_id);
211+
self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
210212
}
211213

212-
fn resolve_doc_links(&mut self, attrs: Attributes, module_id: DefId) {
214+
fn resolve_and_cache(
215+
&mut self,
216+
path_str: &str,
217+
ns: Namespace,
218+
parent_scope: &ParentScope<'ra>,
219+
) -> bool {
220+
// FIXME: This caching may be incorrect in case of multiple `macro_rules`
221+
// items with the same name in the same module.
222+
self.doc_link_resolutions
223+
.entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
224+
.or_insert_with_key(|(path, ns, _)| {
225+
self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
226+
})
227+
.is_some()
228+
}
229+
230+
fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
213231
let mut need_traits_in_scope = false;
214232
for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
215233
assert_eq!(doc_module, None);
216-
let links = self
217-
.markdown_links
218-
.entry(doc)
219-
.or_insert_with_key(|doc| preprocessed_markdown_links(doc));
234+
let mut tmp_links = mem::take(&mut self.markdown_links);
235+
let links =
236+
tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
220237
for PreprocessedMarkdownLink(pp_link, _) in links {
221238
if let Ok(pinfo) = pp_link {
222-
// FIXME: Resolve the path in all namespaces and resolve its prefixes too.
223-
let ns = TypeNS;
224-
self.doc_link_resolutions
225-
.entry((Symbol::intern(&pinfo.path_str), ns, module_id))
226-
.or_insert_with_key(|(path, ns, module_id)| {
227-
self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *module_id)
228-
});
229-
need_traits_in_scope = true;
239+
// The logic here is a conservative approximation for path resolution in
240+
// `resolve_with_disambiguator`.
241+
if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
242+
if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
243+
continue;
244+
}
245+
}
246+
247+
// Resolve all namespaces due to no disambiguator or for diagnostics.
248+
let mut any_resolved = false;
249+
let mut need_assoc = false;
250+
for ns in [TypeNS, ValueNS, MacroNS] {
251+
if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
252+
any_resolved = true;
253+
} else if ns != MacroNS {
254+
need_assoc = true;
255+
}
256+
}
257+
258+
// FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
259+
if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
260+
need_traits_in_scope = true;
261+
}
230262
}
231263
}
264+
self.markdown_links = tmp_links;
232265
}
233266

234267
if need_traits_in_scope {
235-
self.add_traits_in_scope(module_id);
268+
self.add_traits_in_scope(parent_scope.module.def_id());
236269
}
237270
}
238271

@@ -274,19 +307,33 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
274307
fn visit_item(&mut self, item: &ast::Item) {
275308
self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
276309
if let ItemKind::Mod(..) = item.kind {
277-
let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
310+
let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
311+
let module = self.resolver.expect_module(module_def_id);
312+
let old_module = mem::replace(&mut self.parent_scope.module, module);
313+
let old_macro_rules = self.parent_scope.macro_rules;
278314
self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
279-
self.process_module_children_or_reexports(self.current_mod.to_def_id());
315+
self.process_module_children_or_reexports(module_def_id);
280316
visit::walk_item(self, item);
281-
self.current_mod = old_mod;
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+
}
324+
self.parent_scope.module = old_module;
282325
} else {
283-
match item.kind {
326+
match &item.kind {
284327
ItemKind::Trait(..) => {
285328
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
286329
}
287330
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
288331
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
289332
}
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+
}
290337
_ => {}
291338
}
292339
visit::walk_item(self, item);
@@ -313,6 +360,12 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
313360
visit::walk_field_def(self, field)
314361
}
315362

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+
316369
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
317370
// then this will have to implement other visitor methods too.
318371
}
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+
}

0 commit comments

Comments
 (0)