Skip to content

Commit 3bc2ca7

Browse files
committed
Auto merge of #53162 - QuietMisdreavus:crouching-impl-hidden-trait, r=GuillaumeGomez
rustdoc: collect trait impls as an early pass Fixes #52545, fixes #41480, fixes #36922 Right now, rustdoc pulls all its impl information by scanning a crate's HIR for any items it finds. However, it doesn't recurse into anything other than modules, preventing it from seeing trait impls that may be inside things like functions or consts. Thanks to #53002, now these items actually *exist* for rustdoc to see, but they still weren't getting collected for display. But there was a secret. Whenever we pull in an item from another crate, we don't have any of its impls in the local HIR, so instead we ask the compiler for *everything* and filter out after the fact. This process is only triggered if there's a cross-crate re-export in the crate being documented, which can sometimes leave this info out of the docs. This PR instead moves this collection into an early pass, which occurs immediately after crate cleaning, so that that collection occurs regardless. In addition, by including the HIR's own `trait_impls` in addition to the existing `all_trait_implementations` calls, we can collect all these tricky trait impls without having to scan for them!
2 parents f7f4c50 + 1106577 commit 3bc2ca7

25 files changed

+643
-221
lines changed

src/Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2443,6 +2443,7 @@ name = "rustdoc"
24432443
version = "0.0.0"
24442444
dependencies = [
24452445
"minifier 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
2446+
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
24462447
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
24472448
"tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
24482449
]

src/librustc/hir/map/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -709,17 +709,22 @@ impl<'hir> Map<'hir> {
709709
}
710710
}
711711

712-
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
712+
/// Returns the DefId of `id`'s nearest module parent, or `id` itself if no
713713
/// module parent is in this map.
714714
pub fn get_module_parent(&self, id: NodeId) -> DefId {
715-
let id = match self.walk_parent_nodes(id, |node| match *node {
715+
self.local_def_id(self.get_module_parent_node(id))
716+
}
717+
718+
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
719+
/// module parent is in this map.
720+
pub fn get_module_parent_node(&self, id: NodeId) -> NodeId {
721+
match self.walk_parent_nodes(id, |node| match *node {
716722
Node::Item(&Item { node: ItemKind::Mod(_), .. }) => true,
717723
_ => false,
718724
}, |_| false) {
719725
Ok(id) => id,
720726
Err(id) => id,
721-
};
722-
self.local_def_id(id)
727+
}
723728
}
724729

725730
/// Returns the nearest enclosing scope. A scope is an item or block.

src/librustdoc/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ path = "lib.rs"
1111
pulldown-cmark = { version = "0.1.2", default-features = false }
1212
minifier = "0.0.19"
1313
tempfile = "3"
14+
parking_lot = "0.6.4"

src/librustdoc/clean/blanket_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> BlanketImplFinder <'a, 'tcx, 'rcx, 'cstore> {
6969
let real_name = name.clone().map(|name| Ident::from_str(&name));
7070
let param_env = self.cx.tcx.param_env(def_id);
7171
for &trait_def_id in self.cx.all_traits.iter() {
72-
if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
72+
if !self.cx.renderinfo.borrow().access_levels.is_doc_reachable(trait_def_id) ||
7373
self.cx.generated_synthetics
7474
.borrow_mut()
7575
.get(&(def_id, trait_def_id))

src/librustdoc/clean/inline.rs

+81-111
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ use clean::{
2929
self,
3030
GetDefId,
3131
ToSource,
32-
get_auto_traits_with_def_id,
33-
get_blanket_impls_with_def_id,
3432
};
3533

3634
use super::Clean;
@@ -56,7 +54,7 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa
5654
let inner = match def {
5755
Def::Trait(did) => {
5856
record_extern_fqn(cx, did, clean::TypeKind::Trait);
59-
ret.extend(build_impls(cx, did, false));
57+
ret.extend(build_impls(cx, did));
6058
clean::TraitItem(build_external_trait(cx, did))
6159
}
6260
Def::Fn(did) => {
@@ -65,27 +63,27 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHa
6563
}
6664
Def::Struct(did) => {
6765
record_extern_fqn(cx, did, clean::TypeKind::Struct);
68-
ret.extend(build_impls(cx, did, true));
66+
ret.extend(build_impls(cx, did));
6967
clean::StructItem(build_struct(cx, did))
7068
}
7169
Def::Union(did) => {
7270
record_extern_fqn(cx, did, clean::TypeKind::Union);
73-
ret.extend(build_impls(cx, did, true));
71+
ret.extend(build_impls(cx, did));
7472
clean::UnionItem(build_union(cx, did))
7573
}
7674
Def::TyAlias(did) => {
7775
record_extern_fqn(cx, did, clean::TypeKind::Typedef);
78-
ret.extend(build_impls(cx, did, false));
76+
ret.extend(build_impls(cx, did));
7977
clean::TypedefItem(build_type_alias(cx, did), false)
8078
}
8179
Def::Enum(did) => {
8280
record_extern_fqn(cx, did, clean::TypeKind::Enum);
83-
ret.extend(build_impls(cx, did, true));
81+
ret.extend(build_impls(cx, did));
8482
clean::EnumItem(build_enum(cx, did))
8583
}
8684
Def::ForeignTy(did) => {
8785
record_extern_fqn(cx, did, clean::TypeKind::Foreign);
88-
ret.extend(build_impls(cx, did, false));
86+
ret.extend(build_impls(cx, did));
8987
clean::ForeignTypeItem
9088
}
9189
// Never inline enum variants but leave them shown as re-exports.
@@ -159,12 +157,11 @@ pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes {
159157
/// These names are used later on by HTML rendering to generate things like
160158
/// source links back to the original item.
161159
pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
160+
let mut crate_name = cx.tcx.crate_name(did.krate).to_string();
162161
if did.is_local() {
163-
debug!("record_extern_fqn(did={:?}, kind+{:?}): def_id is local, aborting", did, kind);
164-
return;
162+
crate_name = cx.crate_name.clone().unwrap_or(crate_name);
165163
}
166164

167-
let crate_name = cx.tcx.crate_name(did.krate).to_string();
168165
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
169166
// extern blocks have an empty name
170167
let s = elem.data.to_string();
@@ -179,7 +176,12 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
179176
} else {
180177
once(crate_name).chain(relative).collect()
181178
};
182-
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
179+
180+
if did.is_local() {
181+
cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn);
182+
} else {
183+
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
184+
}
183185
}
184186

185187
pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
@@ -271,93 +273,14 @@ fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef {
271273
}
272274
}
273275

274-
pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean::Item> {
276+
pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
275277
let tcx = cx.tcx;
276278
let mut impls = Vec::new();
277279

278280
for &did in tcx.inherent_impls(did).iter() {
279281
build_impl(cx, did, &mut impls);
280282
}
281283

282-
if auto_traits {
283-
let auto_impls = get_auto_traits_with_def_id(cx, did);
284-
{
285-
let mut renderinfo = cx.renderinfo.borrow_mut();
286-
let new_impls: Vec<clean::Item> = auto_impls.into_iter()
287-
.filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
288-
289-
impls.extend(new_impls);
290-
}
291-
impls.extend(get_blanket_impls_with_def_id(cx, did));
292-
}
293-
294-
// If this is the first time we've inlined something from another crate, then
295-
// we inline *all* impls from all the crates into this crate. Note that there's
296-
// currently no way for us to filter this based on type, and we likely need
297-
// many impls for a variety of reasons.
298-
//
299-
// Primarily, the impls will be used to populate the documentation for this
300-
// type being inlined, but impls can also be used when generating
301-
// documentation for primitives (no way to find those specifically).
302-
if cx.populated_all_crate_impls.get() {
303-
return impls;
304-
}
305-
306-
cx.populated_all_crate_impls.set(true);
307-
308-
for &cnum in tcx.crates().iter() {
309-
for did in tcx.all_trait_implementations(cnum).iter() {
310-
build_impl(cx, *did, &mut impls);
311-
}
312-
}
313-
314-
// Also try to inline primitive impls from other crates.
315-
let lang_items = tcx.lang_items();
316-
let primitive_impls = [
317-
lang_items.isize_impl(),
318-
lang_items.i8_impl(),
319-
lang_items.i16_impl(),
320-
lang_items.i32_impl(),
321-
lang_items.i64_impl(),
322-
lang_items.i128_impl(),
323-
lang_items.usize_impl(),
324-
lang_items.u8_impl(),
325-
lang_items.u16_impl(),
326-
lang_items.u32_impl(),
327-
lang_items.u64_impl(),
328-
lang_items.u128_impl(),
329-
lang_items.f32_impl(),
330-
lang_items.f64_impl(),
331-
lang_items.f32_runtime_impl(),
332-
lang_items.f64_runtime_impl(),
333-
lang_items.char_impl(),
334-
lang_items.str_impl(),
335-
lang_items.slice_impl(),
336-
lang_items.slice_u8_impl(),
337-
lang_items.str_alloc_impl(),
338-
lang_items.slice_alloc_impl(),
339-
lang_items.slice_u8_alloc_impl(),
340-
lang_items.const_ptr_impl(),
341-
lang_items.mut_ptr_impl(),
342-
];
343-
344-
for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) {
345-
if !def_id.is_local() {
346-
build_impl(cx, def_id, &mut impls);
347-
348-
let auto_impls = get_auto_traits_with_def_id(cx, def_id);
349-
let blanket_impls = get_blanket_impls_with_def_id(cx, def_id);
350-
let mut renderinfo = cx.renderinfo.borrow_mut();
351-
352-
let new_impls: Vec<clean::Item> = auto_impls.into_iter()
353-
.chain(blanket_impls.into_iter())
354-
.filter(|i| renderinfo.inlined.insert(i.def_id))
355-
.collect();
356-
357-
impls.extend(new_impls);
358-
}
359-
}
360-
361284
impls
362285
}
363286

@@ -372,30 +295,60 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
372295

373296
// Only inline impl if the implemented trait is
374297
// reachable in rustdoc generated documentation
375-
if let Some(traitref) = associated_trait {
376-
if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) {
377-
return
298+
if !did.is_local() {
299+
if let Some(traitref) = associated_trait {
300+
if !cx.renderinfo.borrow().access_levels.is_doc_reachable(traitref.def_id) {
301+
return
302+
}
378303
}
379304
}
380305

381-
let for_ = tcx.type_of(did).clean(cx);
306+
let for_ = if let Some(nodeid) = tcx.hir.as_local_node_id(did) {
307+
match tcx.hir.expect_item(nodeid).node {
308+
hir::ItemKind::Impl(.., ref t, _) => {
309+
t.clean(cx)
310+
}
311+
_ => panic!("did given to build_impl was not an impl"),
312+
}
313+
} else {
314+
tcx.type_of(did).clean(cx)
315+
};
382316

383317
// Only inline impl if the implementing type is
384318
// reachable in rustdoc generated documentation
385-
if let Some(did) = for_.def_id() {
386-
if !cx.access_levels.borrow().is_doc_reachable(did) {
387-
return
319+
if !did.is_local() {
320+
if let Some(did) = for_.def_id() {
321+
if !cx.renderinfo.borrow().access_levels.is_doc_reachable(did) {
322+
return
323+
}
388324
}
389325
}
390326

391327
let predicates = tcx.predicates_of(did);
392-
let trait_items = tcx.associated_items(did).filter_map(|item| {
393-
if associated_trait.is_some() || item.vis == ty::Visibility::Public {
394-
Some(item.clean(cx))
395-
} else {
396-
None
328+
let (trait_items, generics) = if let Some(nodeid) = tcx.hir.as_local_node_id(did) {
329+
match tcx.hir.expect_item(nodeid).node {
330+
hir::ItemKind::Impl(.., ref gen, _, _, ref item_ids) => {
331+
(
332+
item_ids.iter()
333+
.map(|ii| tcx.hir.impl_item(ii.id).clean(cx))
334+
.collect::<Vec<_>>(),
335+
gen.clean(cx),
336+
)
337+
}
338+
_ => panic!("did given to build_impl was not an impl"),
397339
}
398-
}).collect::<Vec<_>>();
340+
} else {
341+
(
342+
tcx.associated_items(did).filter_map(|item| {
343+
if associated_trait.is_some() || item.vis == ty::Visibility::Public {
344+
Some(item.clean(cx))
345+
} else {
346+
None
347+
}
348+
}).collect::<Vec<_>>(),
349+
(tcx.generics_of(did), &predicates).clean(cx),
350+
)
351+
};
399352
let polarity = tcx.impl_polarity(did);
400353
let trait_ = associated_trait.clean(cx).map(|bound| {
401354
match bound {
@@ -417,10 +370,12 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
417370
.collect()
418371
}).unwrap_or(FxHashSet());
419372

373+
debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
374+
420375
ret.push(clean::Item {
421376
inner: clean::ImplItem(clean::Impl {
422377
unsafety: hir::Unsafety::Normal,
423-
generics: (tcx.generics_of(did), &predicates).clean(cx),
378+
generics,
424379
provided_trait_methods: provided,
425380
trait_,
426381
for_,
@@ -465,7 +420,11 @@ fn build_module(cx: &DocContext, did: DefId, visited: &mut FxHashSet<DefId>) ->
465420
}
466421

467422
pub fn print_inlined_const(cx: &DocContext, did: DefId) -> String {
468-
cx.tcx.rendered_const(did)
423+
if let Some(node_id) = cx.tcx.hir.as_local_node_id(did) {
424+
cx.tcx.hir.node_to_pretty_string(node_id)
425+
} else {
426+
cx.tcx.rendered_const(did)
427+
}
469428
}
470429

471430
fn build_const(cx: &DocContext, did: DefId) -> clean::Constant {
@@ -576,16 +535,27 @@ fn separate_supertrait_bounds(mut g: clean::Generics)
576535
}
577536

578537
pub fn record_extern_trait(cx: &DocContext, did: DefId) {
579-
if cx.external_traits.borrow().contains_key(&did) ||
580-
cx.active_extern_traits.borrow().contains(&did)
581-
{
538+
if did.is_local() {
582539
return;
583540
}
584541

542+
{
543+
let external_traits = cx.external_traits.lock();
544+
if external_traits.borrow().contains_key(&did) ||
545+
cx.active_extern_traits.borrow().contains(&did)
546+
{
547+
return;
548+
}
549+
}
550+
585551
cx.active_extern_traits.borrow_mut().push(did);
586552

553+
debug!("record_extern_trait: {:?}", did);
587554
let trait_ = build_external_trait(cx, did);
588555

589-
cx.external_traits.borrow_mut().insert(did, trait_);
556+
{
557+
let external_traits = cx.external_traits.lock();
558+
external_traits.borrow_mut().insert(did, trait_);
559+
}
590560
cx.active_extern_traits.borrow_mut().remove_item(&did);
591561
}

0 commit comments

Comments
 (0)