rustdoc: Track macro_rules
scopes during early doc link resolution
This way links referring to `macro_rules` items are resolved correctly
This commit is contained in:
parent
044e45c595
commit
8b21421873
6 changed files with 99 additions and 4 deletions
|
@ -1267,13 +1267,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
|
||||||
self.insert_unused_macro(ident, def_id, item.id);
|
self.insert_unused_macro(ident, def_id, item.id);
|
||||||
}
|
}
|
||||||
self.r.visibilities.insert(def_id, vis);
|
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 {
|
self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
|
||||||
parent_macro_rules_scope: parent_scope.macro_rules,
|
parent_macro_rules_scope: parent_scope.macro_rules,
|
||||||
binding,
|
binding,
|
||||||
ident,
|
ident,
|
||||||
}),
|
}),
|
||||||
))
|
));
|
||||||
|
self.r.macro_rules_scopes.insert(def_id, scope);
|
||||||
|
scope
|
||||||
} else {
|
} else {
|
||||||
let module = parent_scope.module;
|
let module = parent_scope.module;
|
||||||
let vis = match item.kind {
|
let vis = match item.kind {
|
||||||
|
|
|
@ -145,7 +145,7 @@ enum ScopeSet<'a> {
|
||||||
pub struct ParentScope<'a> {
|
pub struct ParentScope<'a> {
|
||||||
pub module: Module<'a>,
|
pub module: Module<'a>,
|
||||||
expansion: LocalExpnId,
|
expansion: LocalExpnId,
|
||||||
macro_rules: MacroRulesScopeRef<'a>,
|
pub macro_rules: MacroRulesScopeRef<'a>,
|
||||||
derives: &'a [ast::Path],
|
derives: &'a [ast::Path],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,6 +990,8 @@ pub struct Resolver<'a> {
|
||||||
/// `macro_rules` scopes *produced* by expanding the macro invocations,
|
/// `macro_rules` scopes *produced* by expanding the macro invocations,
|
||||||
/// include all the `macro_rules` items and other invocations generated by them.
|
/// include all the `macro_rules` items and other invocations generated by them.
|
||||||
output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
|
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 attributes that are in scope for the given expansion.
|
||||||
helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
|
helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
|
||||||
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
|
/// 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())),
|
non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
|
||||||
invocation_parent_scopes: Default::default(),
|
invocation_parent_scopes: Default::default(),
|
||||||
output_macro_rules_scopes: Default::default(),
|
output_macro_rules_scopes: Default::default(),
|
||||||
|
macro_rules_scopes: Default::default(),
|
||||||
helper_attrs: Default::default(),
|
helper_attrs: Default::default(),
|
||||||
derive_data: Default::default(),
|
derive_data: Default::default(),
|
||||||
local_macro_def_scopes: FxHashMap::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.
|
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
|
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use rustc_middle::ty::{DefIdTree, Visibility};
|
||||||
use rustc_resolve::{ParentScope, Resolver};
|
use rustc_resolve::{ParentScope, Resolver};
|
||||||
use rustc_session::config::Externs;
|
use rustc_session::config::Externs;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{Symbol, SyntaxContext};
|
use rustc_span::{Symbol, SyntaxContext};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
@ -216,6 +217,8 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
|
||||||
ns: Namespace,
|
ns: Namespace,
|
||||||
parent_scope: &ParentScope<'ra>,
|
parent_scope: &ParentScope<'ra>,
|
||||||
) -> bool {
|
) -> 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
|
self.doc_link_resolutions
|
||||||
.entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
|
.entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
|
||||||
.or_insert_with_key(|(path, ns, _)| {
|
.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_def_id = self.resolver.local_def_id(item.id).to_def_id();
|
||||||
let module = self.resolver.expect_module(module_def_id);
|
let module = self.resolver.expect_module(module_def_id);
|
||||||
let old_module = mem::replace(&mut self.parent_scope.module, module);
|
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.resolve_doc_links_local(&item.attrs); // Inner attribute scope
|
||||||
self.process_module_children_or_reexports(module_def_id);
|
self.process_module_children_or_reexports(module_def_id);
|
||||||
visit::walk_item(self, item);
|
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;
|
self.parent_scope.module = old_module;
|
||||||
} else {
|
} else {
|
||||||
match item.kind {
|
match &item.kind {
|
||||||
ItemKind::Trait(..) => {
|
ItemKind::Trait(..) => {
|
||||||
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
|
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
|
||||||
}
|
}
|
||||||
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
|
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
|
||||||
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
|
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);
|
visit::walk_item(self, item);
|
||||||
|
@ -345,6 +360,12 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
|
||||||
visit::walk_field_def(self, field)
|
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),
|
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
|
||||||
// then this will have to implement other visitor methods too.
|
// then this will have to implement other visitor methods too.
|
||||||
}
|
}
|
||||||
|
|
27
src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
Normal file
27
src/test/rustdoc-ui/intra-doc/macro-rules-error.rs
Normal file
|
@ -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 {
|
||||||
|
() => {};
|
||||||
|
}
|
22
src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
Normal file
22
src/test/rustdoc-ui/intra-doc/macro-rules-error.stderr
Normal file
|
@ -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
|
||||||
|
|
|
@ -7,3 +7,18 @@ macro_rules! foo {
|
||||||
|
|
||||||
/// [foo!]
|
/// [foo!]
|
||||||
pub fn baz() {}
|
pub fn baz() {}
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros {
|
||||||
|
macro_rules! escaping {
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod inner {
|
||||||
|
/// [foo!]
|
||||||
|
/// [escaping]
|
||||||
|
pub fn baz() {
|
||||||
|
foo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue