Repository: rust-lang/rust-analyzer
Pull request #14845 (2023-05-18 17:37:48)

Add layout info for enum variant and locals
The size of enum variant is what rustdoc shows (https://github.com/rust-lang/rust/pull/86263). I also added layout info for locals since it helps finding size of unnameable types like closures inside other structs or impl traits.


Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bc925552f3..8fac7fcd87 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -45,7 +45,7 @@ use hir_def::{
     hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
     item_tree::ItemTreeNode,
     lang_item::LangItemTarget,
-    layout::ReprOptions,
+    layout::{self, ReprOptions},
     macro_id_to_def_id,
     nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
     per_ns::PerNs,
@@ -62,7 +62,7 @@ use hir_ty::{
     consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
     display::HexifiedConst,
-    layout::{layout_of_ty, Layout, LayoutError},
+    layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx, TagEncoding},
     method_resolution::{self, TyFingerprint},
     mir::{self, interpret_mir},
     primitive::UintTy,
@@ -1089,7 +1089,7 @@ impl Enum {
         Type::new_for_crate(
             self.id.lookup(db.upcast()).container.krate(),
             TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
-                hir_def::layout::IntegerType::Pointer(sign) => match sign {
+                layout::IntegerType::Pointer(sign) => match sign {
                     true => hir_def::builtin_type::BuiltinType::Int(
                         hir_def::builtin_type::BuiltinInt::Isize,
                     ),
@@ -1097,20 +1097,20 @@ impl Enum {
                         hir_def::builtin_type::BuiltinUint::Usize,
                     ),
                 },
-                hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
+                layout::IntegerType::Fixed(i, sign) => match sign {
                     true => hir_def::builtin_type::BuiltinType::Int(match i {
-                        hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
-                        hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
-                        hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
-                        hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
-                        hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
+                        layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
+                        layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
+                        layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
+                        layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
+                        layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
                     }),
                     false => hir_def::builtin_type::BuiltinType::Uint(match i {
-                        hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
-                        hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
-                        hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
-                        hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
-                        hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
+                        layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
+                        layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
+                        layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
+                        layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
+                        layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
                     }),
                 },
             }),
@@ -1177,6 +1177,28 @@ impl Variant {
     pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> {
         db.const_eval_discriminant(self.into())
     }
+
+    /// Return layout of the variant and tag size of the parent enum.
+    pub fn layout(&self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> {
+        let parent_enum = self.parent_enum(db);
+        let parent_layout = Adt::from(parent_enum).layout(db)?;
+        if let layout::Variants::Multiple { variants, tag, tag_encoding, tag_field: _ } =
+            parent_layout.variants
+        {
+            let tag_size = match tag_encoding {
+                TagEncoding::Direct => {
+                    let target_data_layout = db
+                        .target_data_layout(parent_enum.module(db).krate().id)
+                        .ok_or(LayoutError::TargetLayoutNotAvailable)?;
+                    tag.size(&*target_data_layout).bytes_usize()
+                }
+                TagEncoding::Niche { .. } => 0,
+            };
+            Ok((variants[RustcEnumVariantIdx(self.id)].clone(), tag_size))
+        } else {
+            Ok((parent_layout, 0))
+        }
+    }
 }
 
 /// Variants inherit visibility from the parent enum.
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index c2b9222cb9..fffc837876 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -417,15 +417,25 @@ pub(super) fn definition(
             let layout = it.layout(db).ok()?;
             Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
         }),
-        Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
-            if !it.parent_enum(db).is_data_carrying(db) {
+        Definition::Variant(it) => label_value_and_layout_info_and_docs(db, it, config, |&it| {
+            let layout = (|| {
+                let (layout, tag_size) = it.layout(db).ok()?;
+                let size = layout.size.bytes_usize() - tag_size;
+                if size == 0 {
+                    // There is no value in showing layout info for fieldless variants
+                    return None;
+                }
+                Some(format!("size = {}", layout.size.bytes()))
+            })();
+            let value = if !it.parent_enum(db).is_data_carrying(db) {
                 match it.eval(db) {
                     Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
                     Err(_) => it.value(db).map(|x| format!("{x:?}")),
                 }
             } else {
                 None
-            }
+            };
+            (value, layout)
         }),
         Definition::Const(it) => label_value_and_docs(db, it, |it| {
             let body = it.render_eval(db);
@@ -460,7 +470,7 @@ pub(super) fn definition(
                 .and_then(|fd| builtin(fd, it))
                 .or_else(|| Some(Markup::fenced_block(&it.name())))
         }
-        Definition::Local(it) => return local(db, it),
+        Definition::Local(it) => return local(db, it, config),
         Definition::SelfType(impl_def) => {
             impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
         }
@@ -637,6 +647,32 @@ where
     (label, docs)
 }
 
+fn label_value_and_layout_info_and_docs<D, E, V, L>(
+    db: &RootDatabase,
+    def: D,
+    config: &HoverConfig,
+    value_extractor: E,
+) -> (String, Option<hir::Documentation>)
+where
+    D: HasAttrs + HirDisplay,
+    E: Fn(&D) -> (Option<V>, Option<L>),
+    V: Display,
+    L: Display,
+{
+    let (value, layout) = value_extractor(&def);
+    let label = if let Some(value) = value {
+        format!("{} = {value}", def.display(db))
+    } else {
+        def.display(db).to_string()
+    };
+    let label = match layout {
+        Some(layout) if config.memory_layout => format!("{} // {layout}", label),
+        _ => label,
+    };
+    let docs = def.attrs(db).docs();
+    (label, docs)
+}
+
 fn label_value_and_docs<D, E, V>(
     db: &RootDatabase,
     def: D,
@@ -696,11 +732,11 @@ fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::
         .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
 }
 
-fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
+fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
     let ty = it.ty(db);
     let ty = ty.display_truncated(db, None);
     let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let desc = match it.primary_source(db).into_ident_pat() {
+    let mut desc = match it.primary_source(db).into_ident_pat() {
         Some(ident) => {
             let name = it.name(db);
             let let_kw = if ident
@@ -716,6 +752,16 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
         }
         None => format!("{is_mut}self: {ty}"),
     };
+    if config.memory_layout {
+        if let Ok(layout) = it.ty(db).layout(db) {
+            format_to!(
+                desc,
+                " // size = {}, align = {}",
+                layout.size.bytes(),
+                layout.align.abi.bytes()
+            );
+        }
+    }
     markup(None, desc, None)
 }
 


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 0d8fc8a5f7..60708cb42e 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -150,7 +150,7 @@ fn foo() {
             *local*
 
             ```rust
-            let local: i32
+            let local: i32 // size = 4, align = 4
             ```
         "#]],
     );
@@ -396,12 +396,12 @@ fn main() {
 }
 "#,
         expect![[r#"
-                *iter*
+            *iter*
 
-                ```rust
-                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>>
-                ```
-            "#]],
+            ```rust
+            let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> // size = 8, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -778,12 +778,12 @@ fn main() {
     let zz$0 = Test { t: 23u8, k: 33 };
 }"#,
         expect![[r#"
-                *zz*
+            *zz*
 
-                ```rust
-                let zz: Test<i32>
-                ```
-            "#]],
+            ```rust
+            let zz: Test<i32> // size = 8, align = 4
+            ```
+        "#]],
     );
     check_hover_range(
         r#"
@@ -829,12 +829,12 @@ use Option::Some;
 fn main() { let b$0ar = Some(12); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                let bar: Option<i32>
-                ```
-            "#]],
+            ```rust
+            let bar: Option<i32> // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -898,12 +898,12 @@ fn hover_for_local_variable() {
     check(
         r#"fn func(foo: i32) { fo$0o; }"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -912,12 +912,12 @@ fn hover_for_local_variable_pat() {
     check(
         r#"fn func(fo$0o: i32) {}"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -926,12 +926,12 @@ fn hover_local_var_edge() {
     check(
         r#"fn func(foo: i32) { if true { $0foo; }; }"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -940,12 +940,12 @@ fn hover_for_param_edge() {
     check(
         r#"fn func($0foo: i32) {}"#,
         expect![[r#"
-                *foo*
+            *foo*
 
-                ```rust
-                foo: i32
-                ```
-            "#]],
+            ```rust
+            foo: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -984,12 +984,12 @@ impl Thing {
 fn main() { let foo_$0test = Thing::new(); }
 "#,
         expect![[r#"
-                *foo_test*
+            *foo_test*
 
-                ```rust
-                let foo_test: Thing
-                ```
-            "#]],
+            ```rust
+            let foo_test: Thing // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -1144,12 +1144,12 @@ fn y() {
 }
 "#,
         expect![[r#"
-                *x*
+            *x*
 
-                ```rust
-                let x: i32
-                ```
-            "#]],
+            ```rust
+            let x: i32 // size = 4, align = 4
+            ```
+        "#]],
     )
 }
 
@@ -1274,12 +1274,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
 fn foo(bar:u32) { let a = id!(ba$0r); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                bar: u32
-                ```
-            "#]],
+            ```rust
+            bar: u32 // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -1292,12 +1292,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
 fn foo(bar:u32) { let a = id!(ba$0r); }
 "#,
         expect![[r#"
-                *bar*
+            *bar*
 
-                ```rust
-                bar: u32
-                ```
-            "#]],
+            ```rust
+            bar: u32 // size = 4, align = 4
+            ```
+        "#]],
     );
 }
 
@@ -1841,6 +1841,27 @@ pub fn fo$0o() {}
     );
 }
 
+#[test]
+fn test_hover_layout_of_variant() {
+    check(
+        r#"enum Foo {
+            Va$0riant1(u8, u16),
+            Variant2(i32, u8, i64),
+        }"#,
+        expect![[r#"
+            *Variant1*
+
+            ```rust
+            test::Foo
+            ```
+
+            ```rust
+            Variant1(u8, u16) // size = 4
+            ```
+        "#]],
+    );
+}
+
 #[test]
 fn test_hover_no_memory_layout() {
     check_hover_no_memory_layout(
@@ -3135,7 +3156,7 @@ fn main() {
             *f*
 
             ```rust
-            f: &i32
+            f: &i32 // size = 8, align = 8
             ```
             ---
 
@@ -3185,7 +3206,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<1>
+            let value: Const<1> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3205,7 +3226,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<0>
+            let value: Const<0> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3225,7 +3246,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<-1>
+            let value: Const<-1> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3245,7 +3266,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<true>
+            let value: Const<true> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3265,7 +3286,7 @@ fn main() {
             *value*
 
             ```rust
-            let value: Const<'🦀'>
+            let value: Const<'🦀'> // size = 0, align = 1
             ```
         "#]],
     );
@@ -3281,12 +3302,12 @@ impl Foo {
 }
 "#,
         expect![[r#"
-                *self*
+            *self*
 
-                ```rust
-                self: &Foo
-                ```
-            "#]],
+            ```rust
+            self: &Foo // size = 8, align = 8
+            ```
+        "#]],
     );
 }
 
@@ -3301,12 +3322,12 @@ impl Foo {
 }
 "#,
         expect![[r#"
-                *self*
+            *self*
 
-                ```rust
-                self: Arc<Foo>
-                ```
-            "#]],
+            ```rust
+            self: Arc<Foo> // size = 0, align = 1
+            ```
+        "#]],
     );
 }
 
@@ -4364,9 +4385,9 @@ fn main() {
             *tile4*
 
             ```rust
-            let tile4: [u32; 8]
+            let tile4: [u32; 8] // size = 32, align = 4
             ```
-            "#]],
+        "#]],
     );
 }
 
@@ -5541,7 +5562,7 @@ enum Enum {
             ```
 
             ```rust
-            RecordV { field: u32 }
+            RecordV { field: u32 } // size = 4
             ```
         "#]],
     );