Auto merge of #112890 - GuillaumeGomez:rollup-7e01q69, r=GuillaumeGomez

Rollup of 7 pull requests

Successful merges:

 - #99587 (Document memory orderings of `thread::{park, unpark}`)
 - #112836 ([rustdoc] partially fix invalid files creation)
 - #112853 (Add `lazy_type_alias` feature gate)
 - #112863 (Fix copy-paste typo in `eprint(ln)` docs)
 - #112883 (Make queries traceable again)
 - #112885 (Fix msg passed to span_bug)
 - #112886 (Revert 'Rename profile=user to profile=dist')

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-06-21 13:53:11 +00:00
commit 536635c89b
22 changed files with 246 additions and 49 deletions

View File

@ -535,7 +535,7 @@ pub fn compile_declarative_macro(
.pop()
.unwrap();
}
sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs")
})
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),

View File

@ -442,6 +442,8 @@ pub fn set(&self, features: &mut Features, span: Span) {
(active, intra_doc_pointers, "1.51.0", Some(80896), None),
// Allows setting the threshold for the `large_assignments` lint.
(active, large_assignments, "1.52.0", Some(83518), None),
/// Allow to have type alias types for inter-crate use.
(active, lazy_type_alias, "CURRENT_RUSTC_VERSION", Some(112792), None),
/// Allows `if/while p && let q = r && ...` chains.
(active, let_chains, "1.37.0", Some(53667), None),
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.

View File

@ -896,7 +896,7 @@ fn ast_path_to_ty(
let ty = self.tcx().at(span).type_of(did);
if matches!(self.tcx().def_kind(did), DefKind::TyAlias)
&& ty.skip_binder().has_opaque_types()
&& (ty.skip_binder().has_opaque_types() || self.tcx().features().lazy_type_alias)
{
// Type aliases referring to types that contain opaque types (but aren't just directly
// referencing a single opaque type) get encoded as a type alias that normalization will

View File

@ -531,6 +531,8 @@ pub fn __rust_end_short_backtrace<'tcx>(
key: queries::$name::Key<'tcx>,
mode: QueryMode,
) -> Option<Erase<queries::$name::Value<'tcx>>> {
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
get_query_incr(
QueryType::config(tcx),
QueryCtxt::new(tcx),
@ -571,10 +573,16 @@ pub fn dynamic_query<'tcx>() -> DynamicQuery<'tcx, queries::$name::Storage<'tcx>
cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key),
execute_query: |tcx, key| erase(tcx.$name(key)),
compute: |tcx, key| {
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
__rust_begin_short_backtrace(||
queries::$name::provided_to_erased(
tcx,
call_provider!([$($modifiers)*][tcx, $name, key])
{
let ret = call_provider!([$($modifiers)*][tcx, $name, key]);
tracing::trace!(?ret);
ret
}
)
)
},

View File

@ -871,6 +871,7 @@
large_assignments,
lateout,
lazy_normalization_consts,
lazy_type_alias,
le,
len,
let_chains,

View File

@ -154,7 +154,7 @@ macro_rules! println {
///
/// Panics if writing to `io::stderr` fails.
///
/// Writing to non-blocking stdout can cause an error, which will lead
/// Writing to non-blocking stderr can cause an error, which will lead
/// this macro to panic.
///
/// # Examples
@ -189,7 +189,7 @@ macro_rules! eprint {
///
/// Panics if writing to `io::stderr` fails.
///
/// Writing to non-blocking stdout can cause an error, which will lead
/// Writing to non-blocking stderr can cause an error, which will lead
/// this macro to panic.
///
/// # Examples

View File

@ -889,7 +889,7 @@ fn drop(&mut self) {
/// it is guaranteed that this function will not panic (it may abort the
/// process if the implementation encounters some rare errors).
///
/// # park and unpark
/// # `park` and `unpark`
///
/// Every thread is equipped with some basic low-level blocking support, via the
/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`]
@ -910,14 +910,6 @@ fn drop(&mut self) {
/// if it wasn't already. Because the token is initially absent, [`unpark`]
/// followed by [`park`] will result in the second call returning immediately.
///
/// In other words, each [`Thread`] acts a bit like a spinlock that can be
/// locked and unlocked using `park` and `unpark`.
///
/// Notice that being unblocked does not imply any synchronization with someone
/// that unparked this thread, it could also be spurious.
/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and
/// [`unpark`] return immediately without doing anything.
///
/// The API is typically used by acquiring a handle to the current thread,
/// placing that handle in a shared data structure so that other threads can
/// find it, and then `park`ing in a loop. When some desired condition is met, another
@ -931,6 +923,23 @@ fn drop(&mut self) {
///
/// * It can be implemented very efficiently on many platforms.
///
/// # Memory Ordering
///
/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory
/// operations performed before a call to `unpark` are made visible to the thread that
/// consumes the token and returns from `park`. Note that all `park` and `unpark`
/// operations for a given thread form a total order and `park` synchronizes-with
/// _all_ prior `unpark` operations.
///
/// In atomic ordering terms, `unpark` performs a `Release` operation and `park`
/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same
/// thread form a [release sequence].
///
/// Note that being unblocked does not imply a call was made to `unpark`, because
/// wakeups can also be spurious. For example, a valid, but inefficient,
/// implementation could have `park` and `unpark` return immediately without doing anything,
/// making *all* wakeups spurious.
///
/// # Examples
///
/// ```
@ -944,7 +953,7 @@ fn drop(&mut self) {
/// let parked_thread = thread::spawn(move || {
/// // We want to wait until the flag is set. We *could* just spin, but using
/// // park/unpark is more efficient.
/// while !flag2.load(Ordering::Acquire) {
/// while !flag2.load(Ordering::Relaxed) {
/// println!("Parking thread");
/// thread::park();
/// // We *could* get here spuriously, i.e., way before the 10ms below are over!
@ -961,7 +970,7 @@ fn drop(&mut self) {
/// // There is no race condition here, if `unpark`
/// // happens first, `park` will return immediately.
/// // Hence there is no risk of a deadlock.
/// flag.store(true, Ordering::Release);
/// flag.store(true, Ordering::Relaxed);
/// println!("Unpark the thread");
/// parked_thread.thread().unpark();
///
@ -970,6 +979,7 @@ fn drop(&mut self) {
///
/// [`unpark`]: Thread::unpark
/// [`thread::park_timeout`]: park_timeout
/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence
#[stable(feature = "rust1", since = "1.0.0")]
pub fn park() {
let guard = PanicGuard;

View File

@ -20,7 +20,7 @@ pub enum Profile {
Codegen,
Library,
Tools,
Dist,
User,
None,
}
@ -43,7 +43,7 @@ fn include_path(&self, src_path: &Path) -> PathBuf {
pub fn all() -> impl Iterator<Item = Self> {
use Profile::*;
// N.B. these are ordered by how they are displayed, not alphabetically
[Library, Compiler, Codegen, Tools, Dist, None].iter().copied()
[Library, Compiler, Codegen, Tools, User, None].iter().copied()
}
pub fn purpose(&self) -> String {
@ -53,7 +53,7 @@ pub fn purpose(&self) -> String {
Compiler => "Contribute to the compiler itself",
Codegen => "Contribute to the compiler, and also modify LLVM or codegen",
Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)",
Dist => "Install Rust from source",
User => "Install Rust from source",
None => "Do not modify `config.toml`"
}
.to_string()
@ -73,7 +73,7 @@ pub fn as_str(&self) -> &'static str {
Profile::Codegen => "codegen",
Profile::Library => "library",
Profile::Tools => "tools",
Profile::Dist => "dist",
Profile::User => "user",
Profile::None => "none",
}
}
@ -87,7 +87,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
"lib" | "library" => Ok(Profile::Library),
"compiler" => Ok(Profile::Compiler),
"llvm" | "codegen" => Ok(Profile::Codegen),
"maintainer" | "dist" => Ok(Profile::Dist),
"maintainer" | "user" => Ok(Profile::User),
"tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => {
Ok(Profile::Tools)
}
@ -160,7 +160,7 @@ pub fn setup(config: &Config, profile: Profile) {
"test src/tools/rustfmt",
],
Profile::Library => &["check", "build", "test library/std", "doc"],
Profile::Dist => &["dist", "build"],
Profile::User => &["dist", "build"],
};
println!();
@ -170,7 +170,7 @@ pub fn setup(config: &Config, profile: Profile) {
println!("- `x.py {}`", cmd);
}
if profile != Profile::Dist {
if profile != Profile::User {
println!(
"For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
);

View File

@ -2023,8 +2023,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect())
}
ty::Alias(ty::Projection, ref data) => {
clean_projection(bound_ty.rebind(*data), cx, parent_def_id)
ty::Alias(ty::Projection, data) => {
clean_projection(bound_ty.rebind(data), cx, parent_def_id)
}
ty::Alias(ty::Inherent, alias_ty) => {
@ -2052,8 +2052,21 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
ty::Alias(ty::Weak, data) => {
let ty = cx.tcx.type_of(data.def_id).subst(cx.tcx, data.substs);
clean_middle_ty(bound_ty.rebind(ty), cx, None, None)
if cx.tcx.features().lazy_type_alias {
// Weak type alias `data` represents the `type X` in `type X = Y`. If we need `Y`,
// we need to use `type_of`.
let path = external_path(
cx,
data.def_id,
false,
ThinVec::new(),
bound_ty.rebind(data.substs),
);
Type::Path { path }
} else {
let ty = cx.tcx.type_of(data.def_id).subst(cx.tcx, data.substs);
clean_middle_ty(bound_ty.rebind(ty), cx, None, None)
}
}
ty::Param(ref p) => {

View File

@ -358,15 +358,15 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
impl Item {
pub(crate) fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<Stability> {
self.item_id.as_def_id().and_then(|did| tcx.lookup_stability(did))
self.def_id().and_then(|did| tcx.lookup_stability(did))
}
pub(crate) fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<ConstStability> {
self.item_id.as_def_id().and_then(|did| tcx.lookup_const_stability(did))
self.def_id().and_then(|did| tcx.lookup_const_stability(did))
}
pub(crate) fn deprecation(&self, tcx: TyCtxt<'_>) -> Option<Deprecation> {
self.item_id.as_def_id().and_then(|did| tcx.lookup_deprecation(did))
self.def_id().and_then(|did| tcx.lookup_deprecation(did))
}
pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool {
@ -391,7 +391,7 @@ pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option<Span> {
panic!("blanket impl item has non-blanket ID")
}
}
_ => self.item_id.as_def_id().map(|did| rustc_span(did, tcx)),
_ => self.def_id().map(|did| rustc_span(did, tcx)),
}
}
@ -501,7 +501,7 @@ pub(crate) fn link_names(&self, cache: &Cache) -> Vec<RenderedLink> {
}
pub(crate) fn is_crate(&self) -> bool {
self.is_mod() && self.item_id.as_def_id().map_or(false, |did| did.is_crate_root())
self.is_mod() && self.def_id().map_or(false, |did| did.is_crate_root())
}
pub(crate) fn is_mod(&self) -> bool {
self.type_() == ItemType::Module
@ -638,11 +638,11 @@ fn build_fn_header(
}
let header = match *self.kind {
ItemKind::ForeignFunctionItem(_) => {
let def_id = self.item_id.as_def_id().unwrap();
let def_id = self.def_id().unwrap();
let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader {
unsafety: if abi == Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap())
intrinsic_operation_unsafety(tcx, self.def_id().unwrap())
} else {
hir::Unsafety::Unsafe
},
@ -659,7 +659,7 @@ fn build_fn_header(
}
}
ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => {
let def_id = self.item_id.as_def_id().unwrap();
let def_id = self.def_id().unwrap();
build_fn_header(def_id, tcx, tcx.asyncness(def_id))
}
_ => return None,
@ -738,7 +738,7 @@ pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, keep_as_is: bool) -> Vec<String
}
})
.collect();
if let Some(def_id) = self.item_id.as_def_id() &&
if let Some(def_id) = self.def_id() &&
!def_id.is_local() &&
// This check is needed because `adt_def` will panic if not a compatible type otherwise...
matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union)
@ -787,6 +787,10 @@ pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, keep_as_is: bool) -> Vec<String
pub fn is_doc_hidden(&self) -> bool {
self.attrs.is_doc_hidden()
}
pub fn def_id(&self) -> Option<DefId> {
self.item_id.as_def_id()
}
}
#[derive(Clone, Debug)]

View File

@ -121,6 +121,11 @@ pub(crate) struct Cache {
pub(crate) intra_doc_links: FxHashMap<ItemId, FxIndexSet<clean::ItemLink>>,
/// Cfg that have been hidden via #![doc(cfg_hide(...))]
pub(crate) hidden_cfg: FxHashSet<clean::cfg::Cfg>,
/// Contains the list of `DefId`s which have been inlined. It is used when generating files
/// to check if a stripped item should get its file generated or not: if it's inside a
/// `#[doc(hidden)]` item or a private one and not inlined, it shouldn't get a file.
pub(crate) inlined_items: DefIdSet,
}
/// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.

View File

@ -73,6 +73,8 @@ pub(crate) struct Context<'tcx> {
pub(crate) include_sources: bool,
/// Collection of all types with notable traits referenced in the current module.
pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
/// Field used during rendering, to know if we're inside an inlined item.
pub(crate) is_inside_inlined_module: bool,
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
@ -171,6 +173,19 @@ pub(super) fn root_path(&self) -> String {
}
fn render_item(&mut self, it: &clean::Item, is_module: bool) -> String {
let mut render_redirect_pages = self.render_redirect_pages;
// If the item is stripped but inlined, links won't point to the item so no need to generate
// a file for it.
if it.is_stripped() &&
let Some(def_id) = it.def_id() &&
def_id.is_local()
{
if self.is_inside_inlined_module || self.shared.cache.inlined_items.contains(&def_id) {
// For now we're forced to generate a redirect page for stripped items until
// `record_extern_fqn` correctly points to external items.
render_redirect_pages = true;
}
}
let mut title = String::new();
if !is_module {
title.push_str(it.name.unwrap().as_str());
@ -205,7 +220,7 @@ fn render_item(&mut self, it: &clean::Item, is_module: bool) -> String {
tyname.as_str()
};
if !self.render_redirect_pages {
if !render_redirect_pages {
let clone_shared = Rc::clone(&self.shared);
let page = layout::Page {
css_class: tyname_s,
@ -545,6 +560,7 @@ fn init(
shared: Rc::new(scx),
include_sources,
types_with_notable_traits: FxHashSet::default(),
is_inside_inlined_module: false,
};
if emit_crate {
@ -574,6 +590,7 @@ fn make_child_renderer(&self) -> Self {
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
types_with_notable_traits: FxHashSet::default(),
is_inside_inlined_module: self.is_inside_inlined_module,
}
}
@ -768,12 +785,22 @@ fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
info!("Recursing into {}", self.dst.display());
let buf = self.render_item(item, true);
// buf will be empty if the module is stripped and there is no redirect for it
if !buf.is_empty() {
self.shared.ensure_dir(&self.dst)?;
let joint_dst = self.dst.join("index.html");
self.shared.fs.write(joint_dst, buf)?;
if !item.is_stripped() {
let buf = self.render_item(item, true);
// buf will be empty if the module is stripped and there is no redirect for it
if !buf.is_empty() {
self.shared.ensure_dir(&self.dst)?;
let joint_dst = self.dst.join("index.html");
self.shared.fs.write(joint_dst, buf)?;
}
}
if !self.is_inside_inlined_module {
if let Some(def_id) = item.def_id() && self.cache().inlined_items.contains(&def_id) {
self.is_inside_inlined_module = true;
}
} else if item.is_doc_hidden() {
// We're not inside an inlined module anymore since this one cannot be re-exported.
self.is_inside_inlined_module = false;
}
// Render sidebar-items.js used throughout this module.

View File

@ -313,7 +313,7 @@ fn maybe_inline_local(
return false;
}
let ret = match tcx.hir().get_by_def_id(res_did) {
let inlined = match tcx.hir().get_by_def_id(res_did) {
// Bang macros are handled a bit on their because of how they are handled by the
// compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
// `#[doc(inline)]`, then we don't inline it.
@ -344,7 +344,10 @@ fn maybe_inline_local(
_ => false,
};
self.view_item_stack.remove(&res_did);
ret
if inlined {
self.cx.cache.inlined_items.insert(res_did.to_def_id());
}
inlined
}
/// Returns `true` if the item is visible, meaning it's not `#[doc(hidden)]` or private.

View File

@ -0,0 +1,16 @@
// aux-build:alias-reexport.rs
// aux-build:alias-reexport2.rs
#![crate_name = "foo"]
#![feature(lazy_type_alias)]
extern crate alias_reexport2;
// @has 'foo/reexport/fn.foo.html'
// @has - '//*[@class="rust item-decl"]' 'pub fn foo() -> Reexported'
// @has 'foo/reexport/fn.foo2.html'
// @has - '//*[@class="rust item-decl"]' 'pub fn foo2() -> Result<Reexported, ()>'
// @has 'foo/reexport/type.Reexported.html'
// @has - '//*[@class="rust item-decl"]' 'pub type Reexported = u8;'
#[doc(inline)]
pub use alias_reexport2 as reexport;

View File

@ -0,0 +1,16 @@
// gate-test-lazy_type_alias
// aux-build:alias-reexport.rs
#![crate_name = "foo"]
#![feature(lazy_type_alias)]
extern crate alias_reexport;
use alias_reexport::Reexported;
// @has 'foo/fn.foo.html'
// @has - '//*[@class="rust item-decl"]' 'pub fn foo() -> Reexported'
pub fn foo() -> Reexported { 0 }
// @has 'foo/fn.foo2.html'
// @has - '//*[@class="rust item-decl"]' 'pub fn foo2() -> Result<Reexported, ()>'
pub fn foo2() -> Result<Reexported, ()> { Ok(0) }

View File

@ -0,0 +1,3 @@
#![feature(lazy_type_alias)]
pub type Reexported = u8;

View File

@ -0,0 +1,12 @@
#![feature(lazy_type_alias)]
extern crate alias_reexport;
pub use alias_reexport::Reexported;
// @has 'foo/fn.foo.html'
// @has - '//*[@class="docblock item-decl"]' 'pub fn foo() -> Reexported'
pub fn foo() -> Reexported { 0 }
// @has 'foo/fn.foo2.html'
// @has - '//*[@class="docblock item-decl"]' 'pub fn foo2() -> Result<Reexported, ()>'
pub fn foo2() -> Result<Reexported, ()> { Ok(0) }

View File

@ -0,0 +1,23 @@
#![crate_name="foo"]
// @!has "foo/struct.Foo.html"
#[doc(hidden)]
pub struct Foo;
// @!has "foo/struct.Bar.html"
pub use crate::Foo as Bar;
// @!has "foo/struct.Baz.html"
#[doc(hidden)]
pub use crate::Foo as Baz;
// @!has "foo/foo/index.html"
#[doc(hidden)]
pub mod foo {}
// @!has "foo/bar/index.html"
pub use crate::foo as bar;
// @!has "foo/baz/index.html"
#[doc(hidden)]
pub use crate::foo as baz;

View File

@ -0,0 +1,18 @@
#![crate_name="foo"]
// @!has "foo/priv/index.html"
// @!has "foo/priv/struct.Foo.html"
mod private {
pub struct Foo;
}
// @has "foo/struct.Bar.html"
pub use crate::private::Foo as Bar;
// @!has "foo/foo/index.html"
mod foo {
pub mod subfoo {}
}
// @has "foo/bar/index.html"
pub use crate::foo::subfoo as bar;

View File

@ -3,10 +3,10 @@
#![crate_name = "foo"]
// @!has 'foo/hidden/index.html'
// FIXME: add missing `@` for the two next tests once issue is fixed!
// To be done in <https://github.com/rust-lang/rust/issues/111249>.
// !has 'foo/hidden/inner/index.html'
// !has 'foo/hidden/inner/trait.Foo.html'
// @!has 'foo/hidden/inner/index.html'
// FIXME: Should be `@!has`: https://github.com/rust-lang/rust/issues/111249
// @has 'foo/hidden/inner/trait.Foo.html'
// @matchesraw - '<meta http-equiv="refresh" content="0;URL=../../../foo/visible/trait.Foo.html">'
#[doc(hidden)]
pub mod hidden {
pub mod inner {

View File

@ -0,0 +1,34 @@
#![crate_name = "foo"]
#![feature(no_core)]
#![no_core]
// The following five should not fail!
// @!has 'foo/hidden/index.html'
// @!has 'foo/hidden/inner/index.html'
// FIXME: Should be `@!has`: https://github.com/rust-lang/rust/issues/111249
// @has 'foo/hidden/inner/trait.Foo.html'
// @matchesraw - '<meta http-equiv="refresh" content="0;URL=../../../foo/visible/trait.Foo.html">'
// @!has 'foo/hidden/inner/inner_hidden/index.html'
// @!has 'foo/hidden/inner/inner_hidden/trait.HiddenFoo.html'
#[doc(hidden)]
pub mod hidden {
pub mod inner {
pub trait Foo {}
#[doc(hidden)]
pub mod inner_hidden {
pub trait HiddenFoo {}
}
}
}
// @has 'foo/visible/index.html'
// @has 'foo/visible/trait.Foo.html'
#[doc(inline)]
pub use hidden::inner as visible;
// @has 'foo/struct.Bar.html'
// @count - '//*[@id="impl-Foo-for-Bar"]' 1
pub struct Bar;
impl visible::Foo for Bar {}

View File

@ -10,7 +10,9 @@ pub trait Foo {}
// @has - '//code' 'pub use reexp_stripped::Bar'
// @has - '//code/a' 'Bar'
// @has - '//a[@href="../reexp_stripped/hidden/struct.Bar.html"]' 'Bar'
// FIXME: Should be `@!has`: https://github.com/rust-lang/rust/issues/111249
// @has reexp_stripped/hidden/struct.Bar.html
// @matchesraw - '<meta http-equiv="refresh" content="0;URL=../../reexp_stripped/struct.Bar.html">'
// @has 'reexp_stripped/struct.Bar.html'
// @has - '//a[@href="struct.Bar.html"]' 'Bar'
#[doc(no_inline)]