Skip to content

Commit 52c8084

Browse files
Rollup merge of #109330 - GuillaumeGomez:intermediate-reexport-intra-doc-ice, r=petrochenkov
rustdoc: Fix ICE for intra-doc link on intermediate re-export Fixes #109282. This PR is based on #109266 as it includes its commit to make this work. `@petrochenkov:` It was exactly as you predicted, adding the `DefId` to the attributes fixed the error for intermediate re-exports as well. Thanks a lot! r? `@petrochenkov`
2 parents dd19135 + 87ea994 commit 52c8084

File tree

4 files changed

+117
-51
lines changed

4 files changed

+117
-51
lines changed

src/librustdoc/clean/mod.rs

+58-30
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
3232
use rustc_span::{self, ExpnKind};
3333

3434
use std::assert_matches::assert_matches;
35+
use std::borrow::Cow;
3536
use std::collections::hash_map::Entry;
3637
use std::collections::BTreeMap;
3738
use std::default::Default;
3839
use std::hash::Hash;
3940
use std::mem;
4041
use thin_vec::ThinVec;
4142

42-
use crate::clean::inline::merge_attrs;
4343
use crate::core::{self, DocContext, ImplTraitParam};
4444
use crate::formats::item_type::ItemType;
4545
use crate::visit_ast::Module as DocModule;
@@ -2168,32 +2168,39 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
21682168
/// documentation. Otherwise, we repeat the same operation until we find the "end item".
21692169
fn get_all_import_attributes<'hir>(
21702170
mut item: &hir::Item<'hir>,
2171-
tcx: TyCtxt<'hir>,
2171+
cx: &mut DocContext<'hir>,
21722172
target_def_id: LocalDefId,
2173-
attributes: &mut Vec<ast::Attribute>,
21742173
is_inline: bool,
2175-
) {
2174+
mut prev_import: LocalDefId,
2175+
) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> {
2176+
let mut attributes: Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> = Vec::new();
21762177
let mut first = true;
2177-
let hir_map = tcx.hir();
2178+
let hir_map = cx.tcx.hir();
21782179
let mut visitor = OneLevelVisitor::new(hir_map, target_def_id);
21792180
let mut visited = FxHashSet::default();
21802181

21812182
// If the item is an import and has at least a path with two parts, we go into it.
21822183
while let hir::ItemKind::Use(path, _) = item.kind && visited.insert(item.hir_id()) {
2184+
let import_parent = cx.tcx.opt_local_parent(prev_import).map(|def_id| def_id.to_def_id());
21832185
if first {
21842186
// This is the "original" reexport so we get all its attributes without filtering them.
2185-
attributes.extend_from_slice(hir_map.attrs(item.hir_id()));
2187+
attributes = hir_map.attrs(item.hir_id())
2188+
.iter()
2189+
.map(|attr| (Cow::Borrowed(attr), import_parent))
2190+
.collect::<Vec<_>>();
21862191
first = false;
21872192
} else {
2188-
add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline);
2193+
add_without_unwanted_attributes(&mut attributes, hir_map.attrs(item.hir_id()), is_inline, import_parent);
21892194
}
21902195

2191-
if let Some(i) = visitor.find_target(tcx, item.owner_id.def_id.to_def_id(), path) {
2196+
if let Some(i) = visitor.find_target(cx.tcx, item.owner_id.def_id.to_def_id(), path) {
21922197
item = i;
21932198
} else {
21942199
break;
21952200
}
2201+
prev_import = item.owner_id.def_id;
21962202
}
2203+
attributes
21972204
}
21982205

21992206
fn filter_tokens_from_list(
@@ -2239,17 +2246,24 @@ fn filter_tokens_from_list(
22392246
/// * `doc(inline)`
22402247
/// * `doc(no_inline)`
22412248
/// * `doc(hidden)`
2242-
fn add_without_unwanted_attributes(
2243-
attrs: &mut Vec<ast::Attribute>,
2244-
new_attrs: &[ast::Attribute],
2249+
fn add_without_unwanted_attributes<'hir>(
2250+
attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)>,
2251+
new_attrs: &'hir [ast::Attribute],
22452252
is_inline: bool,
2253+
import_parent: Option<DefId>,
22462254
) {
2247-
// If it's `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
2255+
// If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
22482256
if !is_inline {
2249-
attrs.extend_from_slice(new_attrs);
2257+
for attr in new_attrs {
2258+
attrs.push((Cow::Borrowed(attr), import_parent));
2259+
}
22502260
return;
22512261
}
22522262
for attr in new_attrs {
2263+
if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
2264+
attrs.push((Cow::Borrowed(attr), import_parent));
2265+
continue;
2266+
}
22532267
let mut attr = attr.clone();
22542268
match attr.kind {
22552269
ast::AttrKind::Normal(ref mut normal) => {
@@ -2276,18 +2290,15 @@ fn add_without_unwanted_attributes(
22762290
)
22772291
});
22782292
args.tokens = TokenStream::new(tokens);
2279-
attrs.push(attr);
2293+
attrs.push((Cow::Owned(attr), import_parent));
22802294
}
22812295
ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {
2282-
attrs.push(attr);
2283-
continue;
2296+
attrs.push((Cow::Owned(attr), import_parent));
22842297
}
22852298
}
22862299
}
22872300
}
2288-
ast::AttrKind::DocComment(..) => {
2289-
attrs.push(attr);
2290-
}
2301+
_ => unreachable!(),
22912302
}
22922303
}
22932304
}
@@ -2374,26 +2385,43 @@ fn clean_maybe_renamed_item<'tcx>(
23742385
_ => unreachable!("not yet converted"),
23752386
};
23762387

2377-
let mut import_attrs = Vec::new();
2378-
let mut target_attrs = Vec::new();
2379-
if let Some(import_id) = import_id &&
2388+
let attrs = if let Some(import_id) = import_id &&
23802389
let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id)
23812390
{
2382-
let is_inline = inline::load_attrs(cx, import_id.to_def_id()).lists(sym::doc).get_word_attr(sym::inline).is_some();
2391+
let is_inline = inline::load_attrs(cx, import_id.to_def_id())
2392+
.lists(sym::doc)
2393+
.get_word_attr(sym::inline)
2394+
.is_some();
23832395
// Then we get all the various imports' attributes.
2384-
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut import_attrs, is_inline);
2385-
add_without_unwanted_attributes(&mut target_attrs, inline::load_attrs(cx, def_id), is_inline);
2396+
let mut attrs = get_all_import_attributes(
2397+
use_node,
2398+
cx,
2399+
item.owner_id.def_id,
2400+
is_inline,
2401+
import_id,
2402+
);
2403+
2404+
add_without_unwanted_attributes(
2405+
&mut attrs,
2406+
inline::load_attrs(cx, def_id),
2407+
is_inline,
2408+
None
2409+
);
2410+
attrs
23862411
} else {
23872412
// We only keep the item's attributes.
2388-
target_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
2389-
}
2413+
inline::load_attrs(cx, def_id).iter().map(|attr| (Cow::Borrowed(attr), None)).collect::<Vec<_>>()
2414+
};
23902415

2391-
let import_id = import_id.map(|def_id| def_id.to_def_id());
2392-
let (attrs, cfg) = merge_attrs(cx, &target_attrs, Some((&import_attrs, import_id)));
2416+
let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
2417+
let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| match attr {
2418+
Cow::Borrowed(attr) => (*attr, *did),
2419+
Cow::Owned(attr) => (attr, *did)
2420+
}), false);
23932421

23942422
let mut item =
23952423
Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg);
2396-
item.inline_stmt_id = import_id;
2424+
item.inline_stmt_id = import_id.map(|local| local.to_def_id());
23972425
vec![item]
23982426
})
23992427
}

src/librustdoc/clean/types.rs

+42-19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::cell::RefCell;
23
use std::default::Default;
34
use std::hash::Hash;
@@ -867,30 +868,15 @@ pub(crate) struct Module {
867868

868869
pub(crate) trait AttributesExt {
869870
type AttributeIterator<'a>: Iterator<Item = ast::NestedMetaItem>
871+
where
872+
Self: 'a;
873+
type Attributes<'a>: Iterator<Item = &'a ast::Attribute>
870874
where
871875
Self: 'a;
872876

873877
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a>;
874878

875-
fn span(&self) -> Option<rustc_span::Span>;
876-
877-
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>;
878-
}
879-
880-
impl AttributesExt for [ast::Attribute] {
881-
type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
882-
883-
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
884-
self.iter()
885-
.filter(move |attr| attr.has_name(name))
886-
.filter_map(ast::Attribute::meta_item_list)
887-
.flatten()
888-
}
889-
890-
/// Return the span of the first doc-comment, if it exists.
891-
fn span(&self) -> Option<rustc_span::Span> {
892-
self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span)
893-
}
879+
fn iter<'a>(&'a self) -> Self::Attributes<'a>;
894880

895881
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
896882
let sess = tcx.sess;
@@ -980,6 +966,43 @@ impl AttributesExt for [ast::Attribute] {
980966
}
981967
}
982968

969+
impl AttributesExt for [ast::Attribute] {
970+
type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
971+
type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a;
972+
973+
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
974+
self.iter()
975+
.filter(move |attr| attr.has_name(name))
976+
.filter_map(ast::Attribute::meta_item_list)
977+
.flatten()
978+
}
979+
980+
fn iter<'a>(&'a self) -> Self::Attributes<'a> {
981+
self.into_iter()
982+
}
983+
}
984+
985+
impl AttributesExt for [(Cow<'_, ast::Attribute>, Option<DefId>)] {
986+
type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a
987+
where Self: 'a;
988+
type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a
989+
where Self: 'a;
990+
991+
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
992+
AttributesExt::iter(self)
993+
.filter(move |attr| attr.has_name(name))
994+
.filter_map(ast::Attribute::meta_item_list)
995+
.flatten()
996+
}
997+
998+
fn iter<'a>(&'a self) -> Self::Attributes<'a> {
999+
self.into_iter().map(move |(attr, _)| match attr {
1000+
Cow::Borrowed(attr) => *attr,
1001+
Cow::Owned(attr) => attr,
1002+
})
1003+
}
1004+
}
1005+
9831006
pub(crate) trait NestedAttributesExt {
9841007
/// Returns `true` if the attribute list contains a specific `word`
9851008
fn has_word(self, word: Symbol) -> bool

src/librustdoc/doctest.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1239,8 +1239,9 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
12391239
if let Some(doc) = attrs.collapsed_doc_value() {
12401240
// Use the outermost invocation, so that doctest names come from where the docs were written.
12411241
let span = ast_attrs
1242-
.span()
1243-
.map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
1242+
.iter()
1243+
.find(|attr| attr.doc_str().is_some())
1244+
.map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span))
12441245
.unwrap_or(DUMMY_SP);
12451246
self.collector.set_position(span);
12461247
markdown::find_testable_code(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Regression test for <https://github.com/rust-lang/rust/issues/109282>.
2+
// Import for `ValueEnum` is inlined and doc comments on the import and `ValueEnum` itself are
3+
// merged. After the merge they still have correct parent scopes to resolve both `[ValueEnum]`.
4+
5+
// check-pass
6+
7+
mod m {
8+
pub enum ValueEnum {}
9+
}
10+
mod m2 {
11+
/// [`ValueEnum`]
12+
pub use crate::m::ValueEnum;
13+
}
14+
pub use m2::ValueEnum;

0 commit comments

Comments
 (0)