about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2023-03-18 23:10:42 +0100
committerGuillaume Gomez <guillaume.gomez@huawei.com>2023-03-23 12:53:30 +0100
commit1dad788d8d18672e79d20c832c9219967421f11d (patch)
tree4c6d4c8b85d7051442950ed8d01d8d81b4c40b32
parent84dd6dfd9d19176cc3c94bc1448a841e44d57890 (diff)
downloadrust-1dad788d8d18672e79d20c832c9219967421f11d.tar.gz
rust-1dad788d8d18672e79d20c832c9219967421f11d.zip
Fix ICE for intra-doc link on intermediate re-export
-rw-r--r--src/librustdoc/clean/mod.rs64
-rw-r--r--src/librustdoc/clean/types.rs51
2 files changed, 80 insertions, 35 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2e1f456f50e..b419c81301c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -39,7 +39,6 @@ use std::hash::Hash;
 use std::mem;
 use thin_vec::ThinVec;
 
-use crate::clean::inline::merge_attrs;
 use crate::core::{self, DocContext, ImplTraitParam};
 use crate::formats::item_type::ItemType;
 use crate::visit_ast::Module as DocModule;
@@ -2168,32 +2167,40 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
 /// documentation. Otherwise, we repeat the same operation until we find the "end item".
 fn get_all_import_attributes<'hir>(
     mut item: &hir::Item<'hir>,
-    tcx: TyCtxt<'hir>,
+    cx: &mut DocContext<'hir>,
     target_def_id: LocalDefId,
-    attributes: &mut Vec<ast::Attribute>,
     is_inline: bool,
-) {
+    mut prev_import: LocalDefId,
+) -> Vec<(ast::Attribute, Option<DefId>)> {
+    let mut attributes: Vec<(ast::Attribute, Option<DefId>)> = Vec::new();
     let mut first = true;
-    let hir_map = tcx.hir();
+    let hir_map = cx.tcx.hir();
     let mut visitor = OneLevelVisitor::new(hir_map, target_def_id);
     let mut visited = FxHashSet::default();
+    let mut import_attrs = Vec::new();
 
     // 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());
         if first {
             // This is the "original" reexport so we get all its attributes without filtering them.
-            attributes.extend_from_slice(hir_map.attrs(item.hir_id()));
+            attributes = hir_map.attrs(item.hir_id()).iter().cloned().map(|attr| (attr, import_parent)).collect::<Vec<_>>();
             first = false;
         } else {
-            add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline);
+            add_without_unwanted_attributes(&mut import_attrs, hir_map.attrs(item.hir_id()), is_inline);
+            for attr in import_attrs.drain(..) {
+                attributes.push((attr, import_parent));
+            }
         }
 
-        if let Some(i) = visitor.find_target(tcx, item.owner_id.def_id.to_def_id(), path) {
+        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
 }
 
 fn filter_tokens_from_list(
@@ -2244,7 +2251,7 @@ fn add_without_unwanted_attributes(
     new_attrs: &[ast::Attribute],
     is_inline: bool,
 ) {
-    // If it's `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
+    // If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
     if !is_inline {
         attrs.extend_from_slice(new_attrs);
         return;
@@ -2374,26 +2381,43 @@ fn clean_maybe_renamed_item<'tcx>(
             _ => unreachable!("not yet converted"),
         };
 
-        let mut import_attrs = Vec::new();
-        let mut target_attrs = Vec::new();
-        if let Some(import_id) = import_id &&
+        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 is_inline = inline::load_attrs(cx, import_id.to_def_id()).lists(sym::doc).get_word_attr(sym::inline).is_some();
+            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.
-            get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut import_attrs, is_inline);
-            add_without_unwanted_attributes(&mut target_attrs, inline::load_attrs(cx, def_id), is_inline);
+            let mut attrs = get_all_import_attributes(
+                use_node,
+                cx,
+                item.owner_id.def_id,
+                is_inline,
+                import_id,
+            );
+
+            let mut target_attrs = Vec::new();
+            add_without_unwanted_attributes(
+                &mut target_attrs,
+                inline::load_attrs(cx, def_id),
+                is_inline,
+            );
+            for attr in target_attrs.into_iter() {
+                attrs.push((attr, None));
+            }
+            attrs
         } else {
             // We only keep the item's attributes.
-            target_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
-        }
+            inline::load_attrs(cx, def_id).iter().cloned().map(|attr| (attr, None)).collect::<Vec<_>>()
+        };
 
-        let import_id = import_id.map(|def_id| def_id.to_def_id());
-        let (attrs, cfg) =  merge_attrs(cx, &target_attrs, Some((&import_attrs, import_id)));
+        let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
+        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);
-        item.inline_stmt_id = import_id;
+        item.inline_stmt_id = import_id.map(|local| local.to_def_id());
         vec![item]
     })
 }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 7dbb3f76a0a..74831811aa2 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -869,25 +869,14 @@ pub(crate) trait AttributesExt {
     type AttributeIterator<'a>: Iterator<Item = ast::NestedMetaItem>
     where
         Self: 'a;
+    type Attributes<'a>: Iterator<Item = &'a ast::Attribute>
+    where
+        Self: 'a;
 
     fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a>;
 
-    fn span(&self) -> Option<rustc_span::Span>;
-
-    fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>;
-}
-
-impl AttributesExt for [ast::Attribute] {
-    type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
+    fn iter<'a>(&'a self) -> Self::Attributes<'a>;
 
-    fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
-        self.iter()
-            .filter(move |attr| attr.has_name(name))
-            .filter_map(ast::Attribute::meta_item_list)
-            .flatten()
-    }
-
-    /// Return the span of the first doc-comment, if it exists.
     fn span(&self) -> Option<rustc_span::Span> {
         self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span)
     }
@@ -980,6 +969,38 @@ impl AttributesExt for [ast::Attribute] {
     }
 }
 
+impl AttributesExt for [ast::Attribute] {
+    type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
+    type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a;
+
+    fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
+        self.iter()
+            .filter(move |attr| attr.has_name(name))
+            .filter_map(ast::Attribute::meta_item_list)
+            .flatten()
+    }
+
+    fn iter<'a>(&'a self) -> Self::Attributes<'a> {
+        self.into_iter()
+    }
+}
+
+impl AttributesExt for [(ast::Attribute, Option<DefId>)] {
+    type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
+    type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a;
+
+    fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
+        AttributesExt::iter(self)
+            .filter(move |attr| attr.has_name(name))
+            .filter_map(ast::Attribute::meta_item_list)
+            .flatten()
+    }
+
+    fn iter<'a>(&'a self) -> Self::Attributes<'a> {
+        self.into_iter().map(|(attr, _)| attr)
+    }
+}
+
 pub(crate) trait NestedAttributesExt {
     /// Returns `true` if the attribute list contains a specific `word`
     fn has_word(self, word: Symbol) -> bool