From f58c8aba503f40f3e56d91e12140f3441e6cf221 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Tue, 21 Feb 2023 15:02:01 +0100
Subject: [PATCH 1/2] Fix duplicated attributes for first reexport

---
 src/librustdoc/clean/mod.rs | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 0c70d31ed607e..5541136ef490d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2114,17 +2114,29 @@ fn get_all_import_attributes<'hir>(
     attributes: &mut Vec<ast::Attribute>,
     is_inline: bool,
 ) {
+    let mut first = true;
     let hir_map = 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()) {
-        // We add the attributes from this import into the list.
-        add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline);
+        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()));
+            first = false;
+        } else {
+            add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline);
+        }
 
-        let def_id = if path.segments.len() > 1 {
-            match path.segments[path.segments.len() - 2].res {
+        let def_id = if let [.., parent_segment, _] = &path.segments {
+            match parent_segment.res {
                 hir::def::Res::Def(_, def_id) => 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.
+                    tcx.parent(item.owner_id.def_id.to_def_id())
+                }
                 _ => break,
             }
         } else {
@@ -2341,9 +2353,7 @@ fn clean_maybe_renamed_item<'tcx>(
         if let Some(import_id) = import_id &&
             let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id)
         {
-            // First, we add the attributes from the current import.
-            extra_attrs.extend_from_slice(inline::load_attrs(cx, import_id.to_def_id()));
-            let is_inline = extra_attrs.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 extra_attrs, is_inline);
             add_without_unwanted_attributes(&mut extra_attrs, inline::load_attrs(cx, def_id), is_inline);

From fec6ad60589c4c4bd2535190bf756345a9bb3bbc Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Tue, 21 Feb 2023 15:10:58 +0100
Subject: [PATCH 2/2] Add regression test for #108281

---
 tests/rustdoc/issue-108281.rs | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 tests/rustdoc/issue-108281.rs

diff --git a/tests/rustdoc/issue-108281.rs b/tests/rustdoc/issue-108281.rs
new file mode 100644
index 0000000000000..8e1b6ba88a692
--- /dev/null
+++ b/tests/rustdoc/issue-108281.rs
@@ -0,0 +1,25 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/108281>.
+// It ensures that the attributes on the first reexport are not duplicated.
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+
+#[doc(hidden)]
+pub fn bar() {}
+mod sub {
+    pub fn public() {}
+}
+
+// @matches - '//*[@class="desc docblock-short"]' '^Displayed$'
+/// Displayed
+#[doc(inline)]
+pub use crate::bar as Bar;
+// @matches - '//*[@class="desc docblock-short"]' '^Hello\sDisplayed$'
+#[doc(inline)]
+/// Hello
+pub use crate::Bar as Bar2;
+
+// @matches - '//*[@class="desc docblock-short"]' '^Public$'
+/// Public
+pub use crate::sub::public as Public;