Rollup merge of #83501 - camelid:rustdoc-layout, r=jyn514,GuillaumeGomez
rustdoc: Add unstable CLI option to show basic type layout information Closes #75988. Right now it just shows the size.
This commit is contained in:
commit
40be1d3152
@ -549,6 +549,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
cargo.rustdocflag("--enable-index-page");
|
||||
cargo.rustdocflag("-Zunstable-options");
|
||||
cargo.rustdocflag("-Znormalize-docs");
|
||||
cargo.rustdocflag("--show-type-layout");
|
||||
compile::rustc_cargo(builder, &mut cargo, target);
|
||||
|
||||
// Only include compiler crates, no dependencies of those, such as `libc`.
|
||||
@ -648,6 +649,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
|
||||
cargo.rustdocflag("--document-private-items");
|
||||
cargo.rustdocflag("--enable-index-page");
|
||||
cargo.rustdocflag("--show-type-layout");
|
||||
cargo.rustdocflag("-Zunstable-options");
|
||||
builder.run(&mut cargo.into());
|
||||
}
|
||||
|
@ -267,6 +267,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
crate document_hidden: bool,
|
||||
/// If `true`, generate a JSON file in the crate folder instead of HTML redirection files.
|
||||
crate generate_redirect_map: bool,
|
||||
/// Show the memory layout of types in the docs.
|
||||
crate show_type_layout: bool,
|
||||
crate unstable_features: rustc_feature::UnstableFeatures,
|
||||
crate emit: Vec<EmitType>,
|
||||
}
|
||||
@ -636,6 +638,7 @@ fn println_condition(condition: Condition) {
|
||||
let document_hidden = matches.opt_present("document-hidden-items");
|
||||
let run_check = matches.opt_present("check");
|
||||
let generate_redirect_map = matches.opt_present("generate-redirect-map");
|
||||
let show_type_layout = matches.opt_present("show-type-layout");
|
||||
|
||||
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
|
||||
|
||||
@ -695,6 +698,7 @@ fn println_condition(condition: Condition) {
|
||||
document_private,
|
||||
document_hidden,
|
||||
generate_redirect_map,
|
||||
show_type_layout,
|
||||
unstable_features: rustc_feature::UnstableFeatures::from_environment(
|
||||
crate_name.as_deref(),
|
||||
),
|
||||
|
@ -91,6 +91,8 @@
|
||||
crate include_sources: bool,
|
||||
/// The local file sources we've emitted and their respective url-paths.
|
||||
crate local_sources: FxHashMap<PathBuf, String>,
|
||||
/// Show the memory layout of types in the docs.
|
||||
pub(super) show_type_layout: bool,
|
||||
/// Whether the collapsed pass ran
|
||||
collapsed: bool,
|
||||
/// The base-URL of the issue tracker for when an item has been tagged with
|
||||
@ -373,6 +375,7 @@ fn init(
|
||||
generate_search_filter,
|
||||
unstable_features,
|
||||
generate_redirect_map,
|
||||
show_type_layout,
|
||||
..
|
||||
} = options;
|
||||
|
||||
@ -446,6 +449,7 @@ fn init(
|
||||
all: RefCell::new(AllTypes::new()),
|
||||
errors: receiver,
|
||||
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
|
||||
show_type_layout,
|
||||
};
|
||||
|
||||
// Add the default themes to the `Vec` of stylepaths
|
||||
|
@ -7,6 +7,7 @@
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
@ -830,11 +831,12 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
|
||||
|
||||
document(w, cx, it, None);
|
||||
|
||||
let def_id = it.def_id.expect_real();
|
||||
// Render any items associated directly to this alias, as otherwise they
|
||||
// won't be visible anywhere in the docs. It would be nice to also show
|
||||
// associated items from the aliased type (see discussion in #32077), but
|
||||
// we need #14072 to make sense of the generics.
|
||||
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
|
||||
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
|
||||
}
|
||||
|
||||
fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
|
||||
@ -846,6 +848,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
|
||||
});
|
||||
|
||||
document(w, cx, it, None);
|
||||
|
||||
let mut fields = s
|
||||
.fields
|
||||
.iter()
|
||||
@ -880,7 +883,9 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
|
||||
document(w, cx, field, Some(it));
|
||||
}
|
||||
}
|
||||
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
|
||||
let def_id = it.def_id.expect_real();
|
||||
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
|
||||
document_type_layout(w, cx, def_id);
|
||||
}
|
||||
|
||||
fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
|
||||
@ -940,6 +945,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
|
||||
});
|
||||
|
||||
document(w, cx, it, None);
|
||||
|
||||
if !e.variants.is_empty() {
|
||||
write!(
|
||||
w,
|
||||
@ -1014,7 +1020,9 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
|
||||
render_stability_since(w, variant, it, cx.tcx());
|
||||
}
|
||||
}
|
||||
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
|
||||
let def_id = it.def_id.expect_real();
|
||||
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
|
||||
document_type_layout(w, cx, def_id);
|
||||
}
|
||||
|
||||
fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
|
||||
@ -1114,6 +1122,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
|
||||
});
|
||||
|
||||
document(w, cx, it, None);
|
||||
|
||||
let mut fields = s
|
||||
.fields
|
||||
.iter()
|
||||
@ -1152,7 +1161,9 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
|
||||
}
|
||||
}
|
||||
}
|
||||
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
|
||||
let def_id = it.def_id.expect_real();
|
||||
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
|
||||
document_type_layout(w, cx, def_id);
|
||||
}
|
||||
|
||||
fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
|
||||
@ -1522,3 +1533,62 @@ struct update syntax will not work.",
|
||||
w.write_str("</div></details>");
|
||||
}
|
||||
}
|
||||
|
||||
fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
|
||||
if !cx.shared.show_type_layout {
|
||||
return;
|
||||
}
|
||||
|
||||
writeln!(w, "<h2 class=\"small-section-header\">Layout</h2>");
|
||||
writeln!(w, "<div class=\"docblock\">");
|
||||
|
||||
let tcx = cx.tcx();
|
||||
let param_env = tcx.param_env(ty_def_id);
|
||||
let ty = tcx.type_of(ty_def_id);
|
||||
match tcx.layout_of(param_env.and(ty)) {
|
||||
Ok(ty_layout) => {
|
||||
writeln!(
|
||||
w,
|
||||
"<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
|
||||
completely unstable and may be different between compiler versions and platforms. \
|
||||
The only exception is types with certain <code>repr(...)</code> attributes. \
|
||||
Please see the Rust Reference’s \
|
||||
<a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
|
||||
chapter for details on type layout guarantees.</p></div>"
|
||||
);
|
||||
if ty_layout.layout.abi.is_unsized() {
|
||||
writeln!(w, "<p><strong>Size:</strong> (unsized)</p>");
|
||||
} else {
|
||||
let bytes = ty_layout.layout.size.bytes();
|
||||
writeln!(
|
||||
w,
|
||||
"<p><strong>Size:</strong> {size} byte{pl}</p>",
|
||||
size = bytes,
|
||||
pl = if bytes == 1 { "" } else { "s" },
|
||||
);
|
||||
}
|
||||
}
|
||||
// This kind of layout error can occur with valid code, e.g. if you try to
|
||||
// get the layout of a generic type such as `Vec<T>`.
|
||||
Err(LayoutError::Unknown(_)) => {
|
||||
writeln!(
|
||||
w,
|
||||
"<p><strong>Note:</strong> Unable to compute type layout, \
|
||||
possibly due to this type having generic parameters. \
|
||||
Layout can only be computed for concrete, fully-instantiated types.</p>"
|
||||
);
|
||||
}
|
||||
// This kind of error probably can't happen with valid code, but we don't
|
||||
// want to panic and prevent the docs from building, so we just let the
|
||||
// user know that we couldn't compute the layout.
|
||||
Err(LayoutError::SizeOverflow(_)) => {
|
||||
writeln!(
|
||||
w,
|
||||
"<p><strong>Note:</strong> Encountered an error during type layout; \
|
||||
the type was too big.</p>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(w, "</div>");
|
||||
}
|
||||
|
@ -594,6 +594,9 @@ fn opts() -> Vec<RustcOptGroup> {
|
||||
)
|
||||
}),
|
||||
unstable("no-run", |o| o.optflag("", "no-run", "Compile doctests without running them")),
|
||||
unstable("show-type-layout", |o| {
|
||||
o.optflag("", "show-type-layout", "Include the memory layout of types in the docs")
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
|
4
src/test/rustdoc/type-layout-flag-required.rs
Normal file
4
src/test/rustdoc/type-layout-flag-required.rs
Normal file
@ -0,0 +1,4 @@
|
||||
// Tests that `--show-type-layout` is required in order to show layout info.
|
||||
|
||||
// @!has type_layout_flag_required/struct.Foo.html 'Size: '
|
||||
pub struct Foo(usize);
|
54
src/test/rustdoc/type-layout.rs
Normal file
54
src/test/rustdoc/type-layout.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// compile-flags: --show-type-layout -Z unstable-options
|
||||
|
||||
// @has type_layout/struct.Foo.html 'Size: '
|
||||
// @has - ' bytes'
|
||||
pub struct Foo {
|
||||
pub a: usize,
|
||||
b: Vec<String>,
|
||||
}
|
||||
|
||||
// @has type_layout/enum.Bar.html 'Size: '
|
||||
// @has - ' bytes'
|
||||
pub enum Bar<'a> {
|
||||
A(String),
|
||||
B(&'a str, (std::collections::HashMap<String, usize>, Foo)),
|
||||
}
|
||||
|
||||
// @has type_layout/union.Baz.html 'Size: '
|
||||
// @has - ' bytes'
|
||||
pub union Baz {
|
||||
a: &'static str,
|
||||
b: usize,
|
||||
c: &'static [u8],
|
||||
}
|
||||
|
||||
// @has type_layout/struct.X.html 'Size: '
|
||||
// @has - ' bytes'
|
||||
pub struct X(usize);
|
||||
|
||||
// @has type_layout/struct.Y.html 'Size: '
|
||||
// @has - '1 byte'
|
||||
// @!has - ' bytes'
|
||||
pub struct Y(u8);
|
||||
|
||||
// @has type_layout/struct.Z.html 'Size: '
|
||||
// @has - '0 bytes'
|
||||
pub struct Z;
|
||||
|
||||
// We can't compute layout for generic types.
|
||||
// @has type_layout/struct.Generic.html 'Unable to compute type layout, possibly due to this type having generic parameters'
|
||||
// @!has - 'Size: '
|
||||
pub struct Generic<T>(T);
|
||||
|
||||
// We *can*, however, compute layout for types that are only generic over lifetimes,
|
||||
// because lifetimes are a type-system construct.
|
||||
// @has type_layout/struct.GenericLifetimes.html 'Size: '
|
||||
// @has - ' bytes'
|
||||
pub struct GenericLifetimes<'a>(&'a str);
|
||||
|
||||
// @has type_layout/struct.Unsized.html 'Size: '
|
||||
// @has - '(unsized)'
|
||||
pub struct Unsized([u8]);
|
||||
|
||||
// @!has type_layout/trait.MyTrait.html 'Size: '
|
||||
pub trait MyTrait {}
|
Loading…
Reference in New Issue
Block a user