mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 23:12:02 +00:00
Rollup merge of #113091 - GuillaumeGomez:prevent-cfg-merge-reexport, r=rustdoc
Don't merge cfg and doc(cfg) attributes for re-exports Fixes #112881. ## Explanations When re-exporting things with different `cfg`s there are two things that can happen: * The re-export uses a subset of `cfg`s, this subset is sufficient so that the item will appear exactly with the subset * The re-export uses a non-subset of `cfg`s (e.g. like the example I posted just above where the re-export is ungated), if the non-subset `cfg`s are active (e.g. compiling that example on windows) then this will be a compile error as the item doesn't exist to re-export, if the subset `cfg`s are active it behaves like 1. ### Glob re-exports? **This only applies to non-glob inlined re-exports.** For glob re-exports the item may or may not exist to be re-exported (potentially the `cfg`s on the path up until the glob can be removed, and only `cfg`s on the globbed item itself matter), for non-inlined re-exports see https://github.com/rust-lang/rust/issues/85043. cc `@Nemo157` r? `@notriddle`
This commit is contained in:
commit
ec0008a915
@ -2646,6 +2646,40 @@ fn filter_tokens_from_list(
|
||||
tokens
|
||||
}
|
||||
|
||||
fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
|
||||
if is_inline {
|
||||
ident == sym::hidden || ident == sym::inline || ident == sym::no_inline
|
||||
} else {
|
||||
ident == sym::cfg
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove attributes from `normal` that should not be inherited by `use` re-export.
|
||||
/// Before calling this function, make sure `normal` is a `#[doc]` attribute.
|
||||
fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
|
||||
match normal.item.args {
|
||||
ast::AttrArgs::Delimited(ref mut args) => {
|
||||
let tokens = filter_tokens_from_list(&args.tokens, |token| {
|
||||
!matches!(
|
||||
token,
|
||||
TokenTree::Token(
|
||||
Token {
|
||||
kind: TokenKind::Ident(
|
||||
ident,
|
||||
_,
|
||||
),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) if filter_doc_attr_ident(*ident, is_inline),
|
||||
)
|
||||
});
|
||||
args.tokens = TokenStream::new(tokens);
|
||||
}
|
||||
ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// When inlining items, we merge their attributes (and all the reexports attributes too) with the
|
||||
/// final reexport. For example:
|
||||
///
|
||||
@ -2672,13 +2706,6 @@ fn add_without_unwanted_attributes<'hir>(
|
||||
is_inline: bool,
|
||||
import_parent: Option<DefId>,
|
||||
) {
|
||||
// If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
|
||||
if !is_inline {
|
||||
for attr in new_attrs {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
return;
|
||||
}
|
||||
for attr in new_attrs {
|
||||
if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
@ -2687,33 +2714,14 @@ fn add_without_unwanted_attributes<'hir>(
|
||||
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
|
||||
&& ident == sym::doc
|
||||
{
|
||||
match normal.item.args {
|
||||
ast::AttrArgs::Delimited(ref mut args) => {
|
||||
let tokens = filter_tokens_from_list(&args.tokens, |token| {
|
||||
!matches!(
|
||||
token,
|
||||
TokenTree::Token(
|
||||
Token {
|
||||
kind: TokenKind::Ident(
|
||||
sym::hidden | sym::inline | sym::no_inline,
|
||||
_,
|
||||
),
|
||||
..
|
||||
},
|
||||
_,
|
||||
),
|
||||
)
|
||||
});
|
||||
args.tokens = TokenStream::new(tokens);
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
}
|
||||
ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
}
|
||||
if let [ident] = &*normal.item.path.segments {
|
||||
let ident = ident.ident.name;
|
||||
if ident == sym::doc {
|
||||
filter_doc_attr(normal, is_inline);
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
} else if ident != sym::cfg {
|
||||
// If it's not a `cfg()` attribute, we keep it.
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use clean::AttributesExt;
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
@ -465,16 +463,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
|
||||
|
||||
clean::ImportItem(ref import) => {
|
||||
let stab_tags = if let Some(import_def_id) = import.source.did {
|
||||
let ast_attrs = tcx.get_attrs_unchecked(import_def_id);
|
||||
let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs));
|
||||
|
||||
// Just need an item with the correct def_id and attrs
|
||||
let import_item = clean::Item {
|
||||
item_id: import_def_id.into(),
|
||||
attrs: import_attrs,
|
||||
cfg: ast_attrs.cfg(tcx, &cx.cache().hidden_cfg),
|
||||
..myitem.clone()
|
||||
};
|
||||
let import_item =
|
||||
clean::Item { item_id: import_def_id.into(), ..myitem.clone() };
|
||||
|
||||
let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
|
||||
stab_tags
|
||||
|
30
tests/rustdoc/reexport-cfg.rs
Normal file
30
tests/rustdoc/reexport-cfg.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// This test ensures that only the re-export `cfg` will be displayed and that it won't
|
||||
// include `cfg`s from the previous chained items.
|
||||
|
||||
#![crate_name = "foo"]
|
||||
#![feature(doc_auto_cfg, doc_cfg)]
|
||||
|
||||
mod foo {
|
||||
#[cfg(not(feature = "foo"))]
|
||||
pub struct Bar;
|
||||
|
||||
#[doc(cfg(not(feature = "bar")))]
|
||||
pub struct Bar2;
|
||||
}
|
||||
|
||||
// @has 'foo/index.html'
|
||||
// @has - '//*[@class="item-name"]' 'BabarNon-lie'
|
||||
#[cfg(not(feature = "lie"))]
|
||||
pub use crate::foo::Bar as Babar;
|
||||
|
||||
// @has - '//*[@class="item-name"]' 'Babar2Non-cake'
|
||||
#[doc(cfg(not(feature = "cake")))]
|
||||
pub use crate::foo::Bar2 as Babar2;
|
||||
|
||||
// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot'
|
||||
#[cfg(not(feature = "robot"))]
|
||||
pub use crate::Babar as Elephant;
|
||||
|
||||
// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat'
|
||||
#[doc(cfg(not(feature = "cat")))]
|
||||
pub use crate::Babar2 as Elephant2;
|
@ -27,7 +27,7 @@ pub mod mod1 {
|
||||
pub mod mod2 {
|
||||
// @has - '//code' 'pub use tag::Portability;'
|
||||
// @!has - '//span' 'Deprecated'
|
||||
// @has - '//span' 'sync'
|
||||
// @!has - '//span' 'sync'
|
||||
pub use tag::Portability;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ pub mod mod2 {
|
||||
pub mod mod3 {
|
||||
// @has - '//code' 'pub use tag::Both;'
|
||||
// @has - '//span' 'Deprecated'
|
||||
// @has - '//span' 'sync'
|
||||
// @!has - '//span' 'sync'
|
||||
pub use tag::Both;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ pub mod mod1 {
|
||||
pub mod mod2 {
|
||||
// @has - '//code' 'pub use tag::Portability;'
|
||||
// @!has - '//span' 'Experimental'
|
||||
// @has - '//span' 'sync'
|
||||
// @!has - '//span' 'sync'
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use tag::Portability;
|
||||
}
|
||||
@ -45,7 +45,7 @@ pub mod mod2 {
|
||||
pub mod mod3 {
|
||||
// @has - '//code' 'pub use tag::Both;'
|
||||
// @has - '//span' 'Experimental'
|
||||
// @has - '//span' 'sync'
|
||||
// @!has - '//span' 'sync'
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use tag::Both;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user