diff options
| author | bors <bors@rust-lang.org> | 2023-04-09 13:05:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-04-09 13:05:56 +0000 |
| commit | 7201301df6afe8b68c6a8f5d2abba67bbba435ea (patch) | |
| tree | 2ef2b24cdda8c266e98cf4d06f9fc9ffcc3f12f3 /src | |
| parent | 56e0626836d92973cd12cb505179eef9795efc61 (diff) | |
| parent | 9da9373bf0ebaf5fb2e4911d263a1950e2306157 (diff) | |
| download | rust-7201301df6afe8b68c6a8f5d2abba67bbba435ea.tar.gz rust-7201301df6afe8b68c6a8f5d2abba67bbba435ea.zip | |
Auto merge of #109500 - petrochenkov:modchainld, r=oli-obk
resolve: Preserve reexport chains in `ModChild`ren This may be potentially useful for - avoiding uses of `hir::ItemKind::Use` (which usually lead to correctness issues) - preserving documentation comments on all reexports, including those from other crates - preserving and checking stability/deprecation info on reexports - all kinds of diagnostics The second commit then migrates some hacky logic from rustdoc to `module_reexports` to make it simpler and more correct. Ideally rustdoc should use `module_reexports` immediately at the top level, so `hir::ItemKind::Use`s are never used. The second commit also fixes issues with https://github.com/rust-lang/rust/pull/109330 and therefore Fixes https://github.com/rust-lang/rust/issues/109631 Fixes https://github.com/rust-lang/rust/issues/109614 Fixes https://github.com/rust-lang/rust/issues/109424
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustdoc/clean/inline.rs | 3 | ||||
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 169 | ||||
| -rw-r--r-- | src/librustdoc/visit_ast.rs | 14 |
3 files changed, 36 insertions, 150 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 768f8bb7bc8..9270d1c02e2 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -153,7 +153,6 @@ pub(crate) fn try_inline_glob( let reexports = cx .tcx .module_reexports(current_mod) - .unwrap_or_default() .iter() .filter_map(|child| child.res.opt_def_id()) .collect(); @@ -558,7 +557,7 @@ fn build_module_items( // If we're re-exporting a re-export it may actually re-export something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. - for &item in cx.tcx.module_children(did).iter() { + for item in cx.tcx.module_children(did).iter() { if item.vis.is_public() { let res = item.res.expect_non_local(); if let Some(def_id) = res.opt_def_id() diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1c57cd7a946..7f150f38025 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,6 +21,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::InternalSubsts; @@ -2056,141 +2057,44 @@ fn clean_bare_fn_ty<'tcx>( BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } } -/// Get DefId of of an item's user-visible parent. -/// -/// "User-visible" should account for re-exporting and inlining, which is why this function isn't -/// just `tcx.parent(def_id)`. If the provided `path` has more than one path element, the `DefId` -/// of the second-to-last will be given. -/// -/// ```text -/// use crate::foo::Bar; -/// ^^^ DefId of this item will be returned -/// ``` -/// -/// If the provided path has only one item, `tcx.parent(def_id)` will be returned instead. -fn get_path_parent_def_id( - tcx: TyCtxt<'_>, - def_id: DefId, - path: &hir::UsePath<'_>, -) -> Option<DefId> { - if let [.., parent_segment, _] = &path.segments { - match parent_segment.res { - hir::def::Res::Def(_, parent_def_id) => Some(parent_def_id), - _ if parent_segment.ident.name == kw::Crate => { - // In case the "parent" is the crate, it'll give `Res::Err` so we need to - // circumvent it this way. - Some(tcx.parent(def_id)) - } - _ => None, - } - } else { - // If the path doesn't have a parent, then the parent is the current module. - Some(tcx.parent(def_id)) - } -} - -/// This visitor is used to find an HIR Item based on its `use` path. This doesn't use the ordinary -/// name resolver because it does not walk all the way through a chain of re-exports. -pub(crate) struct OneLevelVisitor<'hir> { - map: rustc_middle::hir::map::Map<'hir>, - pub(crate) item: Option<&'hir hir::Item<'hir>>, - looking_for: Ident, +pub(crate) fn reexport_chain<'tcx>( + tcx: TyCtxt<'tcx>, + import_def_id: LocalDefId, target_def_id: LocalDefId, -} - -impl<'hir> OneLevelVisitor<'hir> { - pub(crate) fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self { - Self { map, item: None, looking_for: Ident::empty(), target_def_id } - } - - pub(crate) fn find_target( - &mut self, - tcx: TyCtxt<'_>, - def_id: DefId, - path: &hir::UsePath<'_>, - ) -> Option<&'hir hir::Item<'hir>> { - let parent_def_id = get_path_parent_def_id(tcx, def_id, path)?; - let parent = self.map.get_if_local(parent_def_id)?; - - // We get the `Ident` we will be looking for into `item`. - self.looking_for = path.segments[path.segments.len() - 1].ident; - // We reset the `item`. - self.item = None; - - match parent { - hir::Node::Item(parent_item) => { - hir::intravisit::walk_item(self, parent_item); - } - hir::Node::Crate(m) => { - hir::intravisit::walk_mod( - self, - m, - tcx.local_def_id_to_hir_id(parent_def_id.as_local().unwrap()), - ); - } - _ => return None, - } - self.item - } -} - -impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> { - type NestedFilter = rustc_middle::hir::nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.map - } - - fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { - if self.item.is_none() - && item.ident == self.looking_for - && (matches!(item.kind, hir::ItemKind::Use(_, _)) - || item.owner_id.def_id == self.target_def_id) +) -> &'tcx [Reexport] { + for child in tcx.module_reexports(tcx.local_parent(import_def_id)) { + if child.res.opt_def_id() == Some(target_def_id.to_def_id()) + && child.reexport_chain[0].id() == Some(import_def_id.to_def_id()) { - self.item = Some(item); + return &child.reexport_chain; } } + &[] } -/// Because a `Use` item directly links to the imported item, we need to manually go through each -/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then, -/// if we found the "end item" (the imported one), we stop there because we don't need its -/// documentation. Otherwise, we repeat the same operation until we find the "end item". +/// Collect attributes from the whole import chain. fn get_all_import_attributes<'hir>( - mut item: &hir::Item<'hir>, cx: &mut DocContext<'hir>, + import_def_id: LocalDefId, target_def_id: LocalDefId, is_inline: bool, - mut prev_import: LocalDefId, ) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> { - let mut attributes: Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> = Vec::new(); + let mut attrs = Vec::new(); let mut first = true; - let hir_map = cx.tcx.hir(); - let mut visitor = OneLevelVisitor::new(hir_map, target_def_id); - let mut visited = FxHashSet::default(); - - // If the item is an import and has at least a path with two parts, we go into it. - while let hir::ItemKind::Use(path, _) = item.kind && visited.insert(item.hir_id()) { - let import_parent = cx.tcx.opt_local_parent(prev_import).map(|def_id| def_id.to_def_id()); + for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + { + let import_attrs = inline::load_attrs(cx, def_id); if first { // This is the "original" reexport so we get all its attributes without filtering them. - attributes = hir_map.attrs(item.hir_id()) - .iter() - .map(|attr| (Cow::Borrowed(attr), import_parent)) - .collect::<Vec<_>>(); + attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); first = false; } else { - add_without_unwanted_attributes(&mut attributes, hir_map.attrs(item.hir_id()), is_inline, import_parent); + add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id)); } - - if let Some(i) = visitor.find_target(cx.tcx, item.owner_id.def_id.to_def_id(), path) { - item = i; - } else { - break; - } - prev_import = item.owner_id.def_id; } - attributes + attrs } fn filter_tokens_from_list( @@ -2375,39 +2279,24 @@ fn clean_maybe_renamed_item<'tcx>( _ => unreachable!("not yet converted"), }; - let attrs = if let Some(import_id) = import_id && - let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id) - { + let target_attrs = inline::load_attrs(cx, def_id); + let attrs = if let Some(import_id) = import_id { let is_inline = inline::load_attrs(cx, import_id.to_def_id()) .lists(sym::doc) .get_word_attr(sym::inline) .is_some(); - // Then we get all the various imports' attributes. - let mut attrs = get_all_import_attributes( - use_node, - cx, - item.owner_id.def_id, - is_inline, - import_id, - ); - - add_without_unwanted_attributes( - &mut attrs, - inline::load_attrs(cx, def_id), - is_inline, - None - ); + let mut attrs = + get_all_import_attributes(cx, import_id, item.owner_id.def_id, is_inline); + add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); attrs } else { // We only keep the item's attributes. - inline::load_attrs(cx, def_id).iter().map(|attr| (Cow::Borrowed(attr), None)).collect::<Vec<_>>() + target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() }; let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); - let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| match attr { - Cow::Borrowed(attr) => (*attr, *did), - Cow::Owned(attr) => (attr, *did) - }), false); + let attrs = + Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let mut item = Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index c959bb3701a..393d51fe090 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -13,9 +13,9 @@ use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::mem; +use std::{iter, mem}; -use crate::clean::{cfg::Cfg, AttributesExt, NestedAttributesExt, OneLevelVisitor}; +use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt}; use crate::core; /// This module is used to store stuff from Rust's AST in a more convenient @@ -133,7 +133,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // is declared but also a reexport of itself producing two exports of the same // macro in the same module. let mut inserted = FxHashSet::default(); - for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) { + for export in self.cx.tcx.module_reexports(CRATE_DEF_ID) { if let Res::Def(DefKind::Macro(_), def_id) = export.res && let Some(local_def_id) = def_id.as_local() && self.cx.tcx.has_attr(def_id, sym::macro_export) && @@ -220,7 +220,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { renamed: Option<Symbol>, glob: bool, please_inline: bool, - path: &hir::UsePath<'_>, ) -> bool { debug!("maybe_inline_local res: {:?}", res); @@ -266,9 +265,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } if !please_inline && - let mut visitor = OneLevelVisitor::new(self.cx.tcx.hir(), res_did) && - let Some(item) = visitor.find_target(self.cx.tcx, def_id.to_def_id(), path) && - let item_def_id = item.owner_id.def_id && + let Some(item_def_id) = reexport_chain(self.cx.tcx, def_id, res_did).iter() + .flat_map(|reexport| reexport.id()).map(|id| id.expect_local()) + .chain(iter::once(res_did)).nth(1) && item_def_id != def_id && self .cx @@ -383,7 +382,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ident, is_glob, please_inline, - path, ) { continue; } |
