mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-04 18:58:41 +00:00
feat: Render padding information when hovering on structs
This commit is contained in:
parent
7230ded9c7
commit
e806957098
7 changed files with 272 additions and 23 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -3147,6 +3148,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(
|
||||
|
@ -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(
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -1779,6 +1779,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": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue