about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2021-05-12 07:17:59 +0900
committerGitHub <noreply@github.com>2021-05-12 07:17:59 +0900
commit40be1d3152542470ec5955a1bd03de4e2c87053d (patch)
treee849c74ba3b379ec0351dd98f25fd471a366105a
parent5c029265465301fe9cb3960ce2a5da6c99b8dcf2 (diff)
parentd43701caa01003d9e0386fbd7a61d530e830564b (diff)
downloadrust-40be1d3152542470ec5955a1bd03de4e2c87053d.tar.gz
rust-40be1d3152542470ec5955a1bd03de4e2c87053d.zip
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.
-rw-r--r--src/bootstrap/doc.rs2
-rw-r--r--src/librustdoc/config.rs4
-rw-r--r--src/librustdoc/html/render/context.rs4
-rw-r--r--src/librustdoc/html/render/print_item.rs78
-rw-r--r--src/librustdoc/lib.rs3
-rw-r--r--src/test/rustdoc/type-layout-flag-required.rs4
-rw-r--r--src/test/rustdoc/type-layout.rs54
7 files changed, 145 insertions, 4 deletions
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index a32b92ef1af..326a6fdaa80 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -549,6 +549,7 @@ impl Step for Rustc {
         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 @@ impl Step for Rustdoc {
 
         cargo.rustdocflag("--document-private-items");
         cargo.rustdocflag("--enable-index-page");
+        cargo.rustdocflag("--show-type-layout");
         cargo.rustdocflag("-Zunstable-options");
         builder.run(&mut cargo.into());
     }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 045b42d0dca..b75e98ae16c 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -267,6 +267,8 @@ crate struct RenderOptions {
     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 @@ impl Options {
         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 @@ impl Options {
                 document_private,
                 document_hidden,
                 generate_redirect_map,
+                show_type_layout,
                 unstable_features: rustc_feature::UnstableFeatures::from_environment(
                     crate_name.as_deref(),
                 ),
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 4e17dc8d3a7..666d9dfc3e9 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -91,6 +91,8 @@ crate struct SharedContext<'tcx> {
     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 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             generate_search_filter,
             unstable_features,
             generate_redirect_map,
+            show_type_layout,
             ..
         } = options;
 
@@ -446,6 +449,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             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
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 7ccc313cc59..f0ca24b8f02 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -7,6 +7,7 @@ use rustc_hir as hir;
 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 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
         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>");
+}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 169ef015fa8..5ede3780e87 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -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")
+        }),
     ]
 }
 
diff --git a/src/test/rustdoc/type-layout-flag-required.rs b/src/test/rustdoc/type-layout-flag-required.rs
new file mode 100644
index 00000000000..a01fbd22950
--- /dev/null
+++ b/src/test/rustdoc/type-layout-flag-required.rs
@@ -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);
diff --git a/src/test/rustdoc/type-layout.rs b/src/test/rustdoc/type-layout.rs
new file mode 100644
index 00000000000..272911de681
--- /dev/null
+++ b/src/test/rustdoc/type-layout.rs
@@ -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 {}