Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #19876 from ShoyuVanilla/layout-padding
feat: Render padding information when hovering on structs
Lukas Wirth 11 months ago
parent a420ef2 · parent e806957 · commit c44372d
-rw-r--r--crates/hir/src/lib.rs53
-rw-r--r--crates/ide/src/hover.rs1
-rw-r--r--crates/ide/src/hover/render.rs76
-rw-r--r--crates/ide/src/hover/tests.rs128
-rw-r--r--crates/rust-analyzer/src/config.rs3
-rw-r--r--docs/book/src/configuration_generated.md7
-rw-r--r--editors/code/package.json27
7 files changed, 272 insertions, 23 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 3a91050d15..e8218cf861 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -5972,6 +5972,59 @@ impl Layout {
}
}
+ pub fn tail_padding(&self, field_size: &mut impl FnMut(usize) -> Option<u64>) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => None,
+ layout::FieldsShape::Array { stride, count } => count.checked_sub(1).and_then(|tail| {
+ let tail_field_size = field_size(tail as usize)?;
+ let offset = stride.bytes() * tail;
+ self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
+ }),
+ layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
+ let tail = memory_index.last_index()?;
+ let tail_field_size = field_size(tail.0.into_raw().into_u32() as usize)?;
+ let offset = offsets.get(tail)?.bytes();
+ self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
+ }
+ }
+ }
+
+ pub fn largest_padding(
+ &self,
+ field_size: &mut impl FnMut(usize) -> Option<u64>,
+ ) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => None,
+ layout::FieldsShape::Array { stride: _, count: 0 } => None,
+ layout::FieldsShape::Array { stride, .. } => {
+ let size = field_size(0)?;
+ stride.bytes().checked_sub(size)
+ }
+ layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
+ let mut reverse_index = vec![None; memory_index.len()];
+ for (src, (mem, offset)) in memory_index.iter().zip(offsets.iter()).enumerate() {
+ reverse_index[*mem as usize] = Some((src, offset.bytes()));
+ }
+ if reverse_index.iter().any(|it| it.is_none()) {
+ stdx::never!();
+ return None;
+ }
+ reverse_index
+ .into_iter()
+ .flatten()
+ .chain(std::iter::once((0, self.0.size.bytes())))
+ .tuple_windows()
+ .filter_map(|((i, start), (_, end))| {
+ let size = field_size(i)?;
+ end.checked_sub(start)?.checked_sub(size)
+ })
+ .max()
+ }
+ }
+ }
+
pub fn enum_tag_size(&self) -> Option<usize> {
let tag_size =
if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 873e31b4a3..8bb1c708e2 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -58,6 +58,7 @@ pub struct MemoryLayoutHoverConfig {
pub size: Option<MemoryLayoutHoverRenderKind>,
pub offset: Option<MemoryLayoutHoverRenderKind>,
pub alignment: Option<MemoryLayoutHoverRenderKind>,
+ pub padding: Option<MemoryLayoutHoverRenderKind>,
pub niches: bool,
}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index ad720c8a62..c24864a18b 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -630,27 +630,57 @@ pub(super) fn definition(
}
},
|_| None,
+ |_| None,
+ ),
+ Definition::Adt(it @ Adt::Struct(strukt)) => render_memory_layout(
+ config.memory_layout,
+ || it.layout(db),
+ |_| None,
+ |layout| {
+ let mut field_size =
+ |i: usize| Some(strukt.fields(db).get(i)?.layout(db).ok()?.size());
+ if strukt.repr(db).is_some_and(|it| it.inhibit_struct_field_reordering()) {
+ Some(("tail padding", layout.tail_padding(&mut field_size)?))
+ } else {
+ Some(("largest padding", layout.largest_padding(&mut field_size)?))
+ }
+ },
+ |_| None,
+ ),
+ Definition::Adt(it) => render_memory_layout(
+ config.memory_layout,
+ || it.layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
),
- Definition::Adt(it) => {
- render_memory_layout(config.memory_layout, || it.layout(db), |_| None, |_| None)
- }
Definition::Variant(it) => render_memory_layout(
config.memory_layout,
|| it.layout(db),
|_| None,
+ |_| None,
|layout| layout.enum_tag_size(),
),
- Definition::TypeAlias(it) => {
- render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
- }
- Definition::Local(it) => {
- render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
- }
+ Definition::TypeAlias(it) => render_memory_layout(
+ config.memory_layout,
+ || it.ty(db).layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ),
+ Definition::Local(it) => render_memory_layout(
+ config.memory_layout,
+ || it.ty(db).layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ),
Definition::SelfType(it) => render_memory_layout(
config.memory_layout,
|| it.self_ty(db).layout(db),
|_| None,
|_| None,
+ |_| None,
),
_ => None,
};
@@ -1055,9 +1085,13 @@ fn closure_ty(
if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) {
push_new_def(hir::Trait::from(trait_).into())
}
- if let Some(layout) =
- render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
- {
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || original.layout(sema.db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ) {
format_to!(markup, "\n___\n{layout}");
}
format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,);
@@ -1142,6 +1176,7 @@ fn render_memory_layout(
config: Option<MemoryLayoutHoverConfig>,
layout: impl FnOnce() -> Result<Layout, LayoutError>,
offset: impl FnOnce(&Layout) -> Option<u64>,
+ padding: impl FnOnce(&Layout) -> Option<(&str, u64)>,
tag: impl FnOnce(&Layout) -> Option<usize>,
) -> Option<String> {
let config = config?;
@@ -1199,6 +1234,23 @@ fn render_memory_layout(
}
}
+ if let Some(render) = config.padding {
+ if let Some((padding_name, padding)) = padding(&layout) {
+ format_to!(label, "{padding_name} = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"),
+ MemoryLayoutHoverRenderKind::Both if padding >= 10 => {
+ format_to!(label, "{padding} ({padding:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{padding}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+ }
+
if config.niches {
if let Some(niches) = layout.niches() {
if niches > 1024 {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 06ca24c3ec..a281a49152 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -12,6 +12,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
size: Some(MemoryLayoutHoverRenderKind::Both),
offset: Some(MemoryLayoutHoverRenderKind::Both),
alignment: Some(MemoryLayoutHoverRenderKind::Both),
+ padding: Some(MemoryLayoutHoverRenderKind::Both),
niches: true,
}),
documentation: true,
@@ -933,7 +934,7 @@ struct Foo$0(pub u32) where u32: Copy;
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
}
@@ -959,7 +960,7 @@ struct Foo$0 { field: u32 }
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
check(
@@ -984,7 +985,7 @@ struct Foo$0 where u32: Copy { field: u32 }
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
}
@@ -1013,7 +1014,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1036,7 +1037,7 @@ fn hover_record_struct_limit() {
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1062,7 +1063,7 @@ fn hover_record_struct_limit() {
---
- size = 16 (0x10), align = 4, no Drop
+ size = 16 (0x10), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1083,7 +1084,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1104,7 +1105,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
@@ -3114,7 +3115,7 @@ struct S$0<T>(core::marker::PhantomData<T>);
---
- size = 0, align = 1, no Drop
+ size = 0, align = 1, largest padding = 0, no Drop
"#]],
);
}
@@ -3148,6 +3149,111 @@ fn test_hover_layout_of_enum() {
}
#[test]
+fn test_hover_layout_padding_info() {
+ check(
+ r#"struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 16 (0x10), align = 8, largest padding = 3, niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"#[repr(align(32))]
+ struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 32 (0x20), align = 32 (0x20), largest padding = 19 (0x13), niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"#[repr(C)]
+ struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 24 (0x18), align = 8, tail padding = 4, niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"struct $0Foo(i16, u128, u64)"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo(i16, u128, u64)
+ ```
+
+ ---
+
+ size = 32 (0x20), align = 8, largest padding = 6, no Drop
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_no_memory_layout() {
check_hover_no_memory_layout(
r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#,
@@ -9198,7 +9304,7 @@ struct Pedro$0<'a> {
---
- size = 16 (0x10), align = 8, niches = 1, no Drop
+ size = 16 (0x10), align = 8, largest padding = 0, niches = 1, no Drop
"#]],
)
}
@@ -10559,7 +10665,7 @@ struct DropField$0 {
---
- size = 4, align = 4, needs Drop
+ size = 4, align = 4, largest padding = 0, needs Drop
"#]],
);
check(
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index f7158235ca..d1ca8c1a91 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -149,6 +149,8 @@ config_data! {
hover_memoryLayout_niches: Option<bool> = Some(false),
/// How to render the offset information in a memory layout hover.
hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
+ /// How to render the padding information in a memory layout hover.
+ hover_memoryLayout_padding: Option<MemoryLayoutHoverRenderKindDef> = None,
/// How to render the size information in a memory layout hover.
hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both),
@@ -1635,6 +1637,7 @@ impl Config {
size: self.hover_memoryLayout_size().map(mem_kind),
offset: self.hover_memoryLayout_offset().map(mem_kind),
alignment: self.hover_memoryLayout_alignment().map(mem_kind),
+ padding: self.hover_memoryLayout_padding().map(mem_kind),
niches: self.hover_memoryLayout_niches().unwrap_or_default(),
}),
documentation: self.hover_documentation_enable().to_owned(),
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index de1d0ea4a3..0e07dadfb7 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -763,6 +763,13 @@ Default: `"hexadecimal"`
How to render the offset information in a memory layout hover.
+## rust-analyzer.hover.memoryLayout.padding {#hover.memoryLayout.padding}
+
+Default: `null`
+
+How to render the padding information in a memory layout hover.
+
+
## rust-analyzer.hover.memoryLayout.size {#hover.memoryLayout.size}
Default: `"both"`
diff --git a/editors/code/package.json b/editors/code/package.json
index 88a90aad8c..c8c36cd85c 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1782,6 +1782,33 @@
{
"title": "hover",
"properties": {
+ "rust-analyzer.hover.memoryLayout.padding": {
+ "markdownDescription": "How to render the padding information in a memory layout hover.",
+ "default": null,
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "both",
+ "decimal",
+ "hexadecimal"
+ ],
+ "enumDescriptions": [
+ "Render as 12 (0xC)",
+ "Render as 12",
+ "Render as 0xC"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "title": "hover",
+ "properties": {
"rust-analyzer.hover.memoryLayout.size": {
"markdownDescription": "How to render the size information in a memory layout hover.",
"default": "both",