about summary refs log tree commit diff
path: root/src/librustdoc/clean
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2023-02-14 18:27:59 +0100
committerGuillaume Gomez <guillaume.gomez@huawei.com>2023-02-14 18:42:04 +0100
commit5f93edd4b8b03a4b16e0efa6868166a6bc7ed645 (patch)
treea09c58b91447725fd2f227f69fffaa55ef950320 /src/librustdoc/clean
parent9bb6e60d1f1360234aae90c97964c0fa5524f141 (diff)
downloadrust-5f93edd4b8b03a4b16e0efa6868166a6bc7ed645.tar.gz
rust-5f93edd4b8b03a4b16e0efa6868166a6bc7ed645.zip
Prevent some attributes from being merged with others on reexports
Diffstat (limited to 'src/librustdoc/clean')
-rw-r--r--src/librustdoc/clean/mod.rs97
1 files changed, 89 insertions, 8 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index bf3bbeb2dd1..bdb559af037 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2126,6 +2126,87 @@ fn get_all_import_attributes<'hir>(
     }
 }
 
+/// When inlining items, we merge its attributes (and all the reexports attributes too) with the
+/// final reexport. For example:
+///
+/// ```
+/// #[doc(hidden, cfg(feature = "foo"))]
+/// pub struct Foo;
+///
+/// #[doc(cfg(feature = "bar"))]
+/// #[doc(hidden, no_inline)]
+/// pub use Foo as Foo1;
+///
+/// #[doc(inline)]
+/// pub use Foo2 as Bar;
+/// ```
+///
+/// So `Bar` at the end will have both `cfg(feature = "...")`. However, we don't want to merge all
+/// attributes so we filter out the following ones:
+/// * `doc(inline)`
+/// * `doc(no_inline)`
+/// * `doc(hidden)`
+fn add_without_unwanted_attributes(attrs: &mut Vec<ast::Attribute>, new_attrs: &[ast::Attribute]) {
+    use rustc_ast::token::{Token, TokenKind};
+    use rustc_ast::tokenstream::{TokenStream, TokenTree};
+
+    for attr in new_attrs {
+        let mut attr = attr.clone();
+        match attr.kind {
+            ast::AttrKind::Normal(ref mut normal) => {
+                if let [ident] = &*normal.item.path.segments {
+                    let ident = ident.ident.name;
+                    if ident == sym::doc {
+                        match normal.item.args {
+                            ast::AttrArgs::Delimited(ref mut args) => {
+                                let mut tokens = Vec::with_capacity(args.tokens.len());
+                                let mut skip_next_comma = false;
+                                for token in args.tokens.clone().into_trees() {
+                                    match token {
+                                        TokenTree::Token(
+                                            Token {
+                                                kind:
+                                                    TokenKind::Ident(
+                                                        sym::hidden | sym::inline | sym::no_inline,
+                                                        _,
+                                                    ),
+                                                ..
+                                            },
+                                            _,
+                                        ) => {
+                                            skip_next_comma = true;
+                                            continue;
+                                        }
+                                        TokenTree::Token(
+                                            Token { kind: TokenKind::Comma, .. },
+                                            _,
+                                        ) if skip_next_comma => {
+                                            skip_next_comma = false;
+                                            continue;
+                                        }
+                                        _ => {}
+                                    }
+                                    skip_next_comma = false;
+                                    tokens.push(token);
+                                }
+                                args.tokens = TokenStream::new(tokens);
+                                attrs.push(attr);
+                            }
+                            ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {
+                                attrs.push(attr);
+                                continue;
+                            }
+                        }
+                    }
+                }
+            }
+            ast::AttrKind::DocComment(..) => {
+                attrs.push(attr);
+            }
+        }
+    }
+}
+
 fn clean_maybe_renamed_item<'tcx>(
     cx: &mut DocContext<'tcx>,
     item: &hir::Item<'tcx>,
@@ -2216,17 +2297,17 @@ fn clean_maybe_renamed_item<'tcx>(
             extra_attrs.extend_from_slice(inline::load_attrs(cx, import_id.to_def_id()));
             // Then we get all the various imports' attributes.
             get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
+            add_without_unwanted_attributes(&mut extra_attrs, inline::load_attrs(cx, def_id));
+        } else {
+            // We only keep the item's attributes.
+            extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
         }
 
-        let mut item = if !extra_attrs.is_empty() {
-            extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
-            let attrs = Attributes::from_ast(&extra_attrs);
-            let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
+        let attrs = Attributes::from_ast(&extra_attrs);
+        let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
 
-            Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg)
-        } else {
-            Item::from_def_id_and_parts(def_id, Some(name), kind, cx)
-        };
+        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.map(|def_id| def_id.to_def_id());
         vec![item]
     })