From 3c862507b9ab8154bb9ccf41f7568a4ca69bac93 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 30 May 2023 16:20:01 +0200 Subject: [PATCH] Add render configs for memory layout hovers --- crates/hir/src/lib.rs | 74 +++--- .../convert_named_struct_to_tuple_struct.rs | 3 +- .../convert_tuple_struct_to_named_struct.rs | 3 +- crates/ide-assists/src/tests.rs | 3 +- crates/ide/src/hover.rs | 17 +- crates/ide/src/hover/render.rs | 226 +++++++++++------- crates/ide/src/hover/tests.rs | 79 +++--- crates/ide/src/lib.rs | 5 +- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 57 ++++- docs/user/generated_config.adoc | 20 ++ editors/code/package.json | 74 ++++++ 12 files changed, 400 insertions(+), 163 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5c0320f7334..eee45b74d94 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, lang_item::LangItemTarget, - layout::{self, ReprOptions}, + layout::{self, ReprOptions, TargetDataLayout}, macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, @@ -62,7 +62,7 @@ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::{Layout as TyLayout, LayoutError, RustcEnumVariantIdx, TagEncoding}, + layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -133,6 +133,7 @@ }, hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + layout::LayoutError, mir::MirEvalError, PointerCast, Safety, }, @@ -962,7 +963,8 @@ pub fn ty(&self, db: &dyn HirDatabase) -> Type { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()).map(Layout) + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -1135,23 +1137,8 @@ pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> { - let layout = Adt::from(self).layout(db)?; - let tag_size = - if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.0.variants { - match tag_encoding { - TagEncoding::Direct => { - let target_data_layout = db - .target_data_layout(self.module(db).krate().id) - .ok_or(LayoutError::TargetLayoutNotAvailable)?; - tag.size(&*target_data_layout).bytes_usize() - } - TagEncoding::Niche { .. } => 0, - } - } else { - 0 - }; - Ok((layout, tag_size)) + pub fn layout(self, db: &dyn HirDatabase) -> Result { + Adt::from(self).layout(db) } } @@ -1214,19 +1201,16 @@ pub fn eval(self, db: &dyn HirDatabase) -> Result { 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> { + pub fn layout(&self, db: &dyn HirDatabase) -> Result { let parent_enum = self.parent_enum(db); - let (parent_layout, tag_size) = parent_enum.layout(db)?; - Ok(( - match &parent_layout.0.variants { - layout::Variants::Multiple { variants, .. } => { - Layout(Arc::new(variants[RustcEnumVariantIdx(self.id)].clone())) - } - _ => parent_layout, - }, - tag_size, - )) + let parent_layout = parent_enum.layout(db)?; + Ok(match &parent_layout.0.variants { + layout::Variants::Multiple { variants, .. } => Layout( + Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()), + db.target_data_layout(parent_enum.krate(db).into()).unwrap(), + ), + _ => parent_layout, + }) } } @@ -1259,7 +1243,9 @@ pub fn layout(self, db: &dyn HirDatabase) -> Result { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner), self.krate(db).id).map(Layout) + let krate = self.krate(db).id; + db.layout_of_adt(self.into(), Substitution::empty(Interner), krate) + .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -4244,7 +4230,8 @@ pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty.clone(), self.env.krate).map(Layout) + db.layout_of_ty(self.ty.clone(), self.env.krate) + .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } } @@ -4356,7 +4343,7 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option); +pub struct Layout(Arc, Arc); impl Layout { pub fn size(&self) -> u64 { @@ -4367,8 +4354,8 @@ pub fn align(&self) -> u64 { self.0.align.abi.bytes() } - pub fn niches(&self, db: &dyn HirDatabase, krate: Crate) -> Option { - Some(self.0.largest_niche?.available(&*db.target_data_layout(krate.id)?)) + pub fn niches(&self) -> Option { + Some(self.0.largest_niche?.available(&*self.1)) } pub fn field_offset(&self, idx: usize) -> Option { @@ -4382,6 +4369,19 @@ pub fn field_offset(&self, idx: usize) -> Option { layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), } } + + pub fn enum_tag_size(&self) -> Option { + let tag_size = + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { + match tag_encoding { + TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Niche { .. } => 0, + } + } else { + return None; + }; + Some(tag_size) + } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 00a4e0530d2..fe1cb6fce36 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -55,7 +55,8 @@ pub(crate) fn convert_named_struct_to_tuple_struct( // XXX: We don't currently provide this assist for struct definitions inside macros, but if we // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files // too. - let strukt = ctx.find_node_at_offset::>()?; + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let record_fields = match field_list { ast::FieldList::RecordFieldList(it) => it, diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 772e032fb29..017853a4a20 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -50,7 +50,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let strukt = ctx.find_node_at_offset::>()?; + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let tuple_fields = match field_list { ast::FieldList::TupleFieldList(it) => it, diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 6e161ca4396..344f2bfcce1 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -273,8 +273,9 @@ fn assist_order_field_struct() { assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); - assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct"); assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); + assert_eq!(assists.next().expect("expected assist").label, "Generate `new`"); + assert_eq!(assists.next().map(|it| it.label.to_string()), None); } #[test] diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 1a84a963f55..5ef6ac94807 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -27,12 +27,27 @@ #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { pub links_in_hover: bool, - pub memory_layout: bool, + pub memory_layout: Option, pub documentation: bool, pub keywords: bool, pub format: HoverDocFormat, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct MemoryLayoutHoverConfig { + pub size: Option, + pub offset: Option, + pub alignment: Option, + pub niches: bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MemoryLayoutHoverRenderKind { + Decimal, + Hexadecimal, + Both, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum HoverDocFormat { Markdown, diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 96a97ab44c0..1362146413e 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -3,8 +3,8 @@ use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasCrate, HasSource, HirDisplay, - Layout, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout, + LayoutError, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -27,7 +27,8 @@ use crate::{ doc_links::{remove_links, rewrite_links}, hover::walk_and_push_ty, - HoverAction, HoverConfig, HoverResult, Markup, + HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, + MemoryLayoutHoverRenderKind, }; pub(super) fn type_info_of( @@ -393,32 +394,27 @@ pub(super) fn definition( let mod_path = definition_mod_path(db, &def); let (label, docs) = match def { Definition::Macro(it) => label_and_docs(db, it), - Definition::Field(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let var_def = it.parent_def(db); - let id = it.index(); - let layout = it.layout(db).ok()?; - let offset = match var_def { - hir::VariantDef::Struct(s) => Adt::from(s) - .layout(db) - .ok() - .and_then(|layout| Some(format!(", offset = {:#X}", layout.field_offset(id)?))), - _ => None, - }; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!( - "size = {:#X}, align = {:#X}{}{niches}", - layout.size(), - layout.align(), - offset.as_deref().unwrap_or_default() - )) - }), + Definition::Field(it) => label_and_layout_info_and_docs( + db, + it, + config, + |&it| it.layout(db), + |_| { + let var_def = it.parent_def(db); + let id = it.index(); + match var_def { + hir::VariantDef::Struct(s) => { + Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id)) + } + _ => None, + } + }, + ), Definition::Module(it) => label_and_docs(db, it), Definition::Function(it) => label_and_docs(db, it), - Definition::Adt(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let layout = it.layout(db).ok()?; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align())) - }), + Definition::Adt(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None) + } Definition::Variant(it) => label_value_and_layout_info_and_docs( db, it, @@ -435,16 +431,8 @@ pub(super) fn definition( None } }, - |&it| { - let (layout, tag_size) = it.layout(db).ok()?; - let size = layout.size() as usize - tag_size; - if size == 0 { - // There is no value in showing layout info for fieldless variants - return None; - } - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}{niches}", layout.size())) - }, + |it| it.layout(db), + |layout| layout.enum_tag_size(), ), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); @@ -470,11 +458,9 @@ pub(super) fn definition( }), Definition::Trait(it) => label_and_docs(db, it), Definition::TraitAlias(it) => label_and_docs(db, it), - Definition::TypeAlias(it) => label_and_layout_info_and_docs(db, it, config, |&it| { - let layout = it.ty(db).layout(db).ok()?; - let niches = niches(db, it, &layout).unwrap_or_default(); - Some(format!("size = {:#X}, align = {:#X}{niches}", layout.size(), layout.align(),)) - }), + Definition::TypeAlias(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None) + } Definition::BuiltinType(it) => { return famous_defs .and_then(|fd| builtin(fd, it)) @@ -509,10 +495,6 @@ pub(super) fn definition( markup(docs, label, mod_path) } -fn niches(db: &RootDatabase, it: impl HasCrate, layout: &Layout) -> Option { - Some(format!(", niches = {}", layout.niches(db, it.krate(db).into())?)) -} - fn type_info( sema: &Semantics<'_, RootDatabase>, config: &HoverConfig, @@ -557,14 +539,6 @@ fn closure_ty( TypeInfo { original, adjusted }: &TypeInfo, ) -> Option { let c = original.as_closure()?; - let layout = if config.memory_layout { - original - .layout(sema.db) - .map(|x| format!(" // size = {}, align = {}", x.size(), x.align())) - .unwrap_or_default() - } else { - String::default() - }; let mut captures_rendered = c.captured_items(sema.db) .into_iter() .map(|it| { @@ -600,17 +574,23 @@ fn closure_ty( } else { String::new() }; + let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),); + + if let Some(layout) = + render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) + { + format_to!(markup, "{layout}"); + } + format_to!( + markup, + "\n{}\n```{adjusted}\n\n## Captures\n{}", + c.display_with_impl(sema.db), + captures_rendered, + ); let mut res = HoverResult::default(); res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); - res.markup = format!( - "```rust\n{}{}\n{}\n```{adjusted}\n\n## Captures\n{}", - c.display_with_id(sema.db), - layout, - c.display_with_impl(sema.db), - captures_rendered, - ) - .into(); + res.markup = markup.into(); Some(res) } @@ -644,48 +624,59 @@ fn label_and_docs(db: &RootDatabase, def: D) -> (String, Option( +fn label_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, layout_extractor: E, + layout_offset_extractor: E2, ) -> (String, Option) where D: HasAttrs + HirDisplay, - E: Fn(&D) -> Option, - V: Display, + E: Fn(&D) -> Result, + E2: Fn(&Layout) -> Option, { - let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { - Some(layout) => format!("{} // {layout}", def.display(db)), - _ => def.display(db).to_string(), - }; + let mut label = def.display(db).to_string(); + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + layout_offset_extractor, + |_| None, + ) { + format_to!(label, "{layout}"); + } let docs = def.attrs(db).docs(); (label, docs) } -fn label_value_and_layout_info_and_docs( +fn label_value_and_layout_info_and_docs( db: &RootDatabase, def: D, config: &HoverConfig, value_extractor: E, layout_extractor: E2, + layout_tag_extractor: E3, ) -> (String, Option) where D: HasAttrs + HirDisplay, E: Fn(&D) -> Option, - E2: Fn(&D) -> Option, + E2: Fn(&D) -> Result, + E3: Fn(&Layout) -> Option, V: Display, - L: Display, { let value = value_extractor(&def); - let label = match value { + let mut label = match value { Some(value) => format!("{} = {value}", def.display(db)), None => def.display(db).to_string(), }; - let label = match config.memory_layout.then(|| layout_extractor(&def)).flatten() { - Some(layout) => format!("{} // {layout}", label), - _ => label, - }; + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + |_| None, + layout_tag_extractor, + ) { + format_to!(label, "{layout}"); + } let docs = def.attrs(db).docs(); (label, docs) } @@ -769,14 +760,87 @@ fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option 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(), layout.align()); - } + if let Some(layout) = + render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) + { + format_to!(desc, "{layout}"); } markup(None, desc, None) } +fn render_memory_layout( + config: Option, + layout: impl FnOnce() -> Result, + offset: impl FnOnce(&Layout) -> Option, + tag: impl FnOnce(&Layout) -> Option, +) -> Option { + // field + + let config = config?; + let layout = layout().ok()?; + + let mut label = String::from(" // "); + + if let Some(render) = config.size { + let size = match tag(&layout) { + Some(tag) => layout.size() as usize - tag, + None => layout.size() as usize, + }; + format_to!(label, "size = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"), + MemoryLayoutHoverRenderKind::Both if size >= 10 => { + format_to!(label, "{size} ({size:#X})") + } + MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"), + } + format_to!(label, ", "); + } + + if let Some(render) = config.alignment { + let align = layout.align(); + format_to!(label, "align = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",), + MemoryLayoutHoverRenderKind::Both if align >= 10 => { + format_to!(label, "{align} ({align:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{align}") + } + } + format_to!(label, ", "); + } + + if let Some(render) = config.offset { + if let Some(offset) = offset(&layout) { + format_to!(label, "offset = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"), + MemoryLayoutHoverRenderKind::Both if offset >= 10 => { + format_to!(label, "{offset} ({offset:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{offset}") + } + } + format_to!(label, ", "); + } + } + + if config.niches { + if let Some(niches) = layout.niches() { + format_to!(label, "niches = {niches}, "); + } + } + label.pop(); // ' ' + label.pop(); // ',' + Some(label) +} + struct KeywordHint { description: String, keyword_mod: String, diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f8b5b654546..f2ee79a23e6 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -2,11 +2,18 @@ use ide_db::base_db::{FileLoader, FileRange}; use syntax::TextRange; -use crate::{fixture, HoverConfig, HoverDocFormat}; +use crate::{ + fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, +}; const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, - memory_layout: true, + memory_layout: Some(MemoryLayoutHoverConfig { + size: Some(MemoryLayoutHoverRenderKind::Both), + offset: Some(MemoryLayoutHoverRenderKind::Both), + alignment: Some(MemoryLayoutHoverRenderKind::Both), + niches: true, + }), documentation: true, format: HoverDocFormat::Markdown, keywords: true, @@ -62,7 +69,7 @@ fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { memory_layout: false, ..HOVER_BASE_CONFIG }, + &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -237,7 +244,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl Fn(i32) -> i32 ``` @@ -292,7 +299,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 16, align = 8 + {closure#0} // size = 16 (0x10), align = 8, niches = 1 impl FnOnce() ``` @@ -320,7 +327,7 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl FnMut() ``` @@ -344,7 +351,7 @@ fn main() { "#, expect![[r#" ```rust - {closure#0} // size = 8, align = 8 + {closure#0} // size = 8, align = 8, niches = 1 impl FnOnce() -> S2 ``` Coerced to: &impl FnOnce() -> S2 @@ -667,7 +674,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - field_a: u8 // size = 0x1, align = 0x1, offset = 0x4 + field_a: u8 // size = 1, align = 1, offset = 4 ``` "#]], ); @@ -692,7 +699,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 + field_a: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -714,7 +721,7 @@ fn main() { ``` ```rust - field_a: u32 // size = 0x4, align = 0x4, offset = 0x0 + field_a: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -1528,7 +1535,7 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - type foo = fn(a: i32, b: i32) -> i32 // size = 0x8, align = 0x8, niches = 1 + type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -1546,7 +1553,7 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - type foo = fn(i32, i32) -> i32 // size = 0x8, align = 0x8, niches = 1 + type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -1674,7 +1681,7 @@ fn test_hover_struct_doc_comment() { ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1710,7 +1717,7 @@ fn test_hover_struct_doc_attr() { ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1739,7 +1746,7 @@ fn test_hover_struct_doc_attr_multiple_and_mixed() { ``` ```rust - struct Bar // size = 0x0, align = 0x1 + struct Bar // size = 0, align = 1 ``` --- @@ -1767,7 +1774,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0x0, align = 0x1 + pub struct Bar // size = 0, align = 1 ``` --- @@ -1794,7 +1801,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar // size = 0x0, align = 0x1 + pub struct Bar // size = 0, align = 1 ``` --- @@ -1883,7 +1890,7 @@ fn test_hover_layout_of_variant() { ``` ```rust - Variant1(u8, u16) // size = 0x4 + Variant1(u8, u16) // size = 4, align = 2 ``` "#]], ); @@ -1904,7 +1911,7 @@ fn test_hover_layout_of_enum() { ``` ```rust - enum Foo // size = 0x10, align = 0x8, niches = 254 + enum Foo // size = 16 (0x10), align = 8, niches = 254 ``` "#]], ); @@ -3204,7 +3211,7 @@ fn main() { *f* ```rust - f: &i32 // size = 8, align = 8 + f: &i32 // size = 8, align = 8, niches = 1 ``` --- @@ -3213,7 +3220,7 @@ fn main() { ``` ```rust - f: i32 // size = 0x4, align = 0x4, offset = 0x0 + f: i32 // size = 4, align = 4, offset = 0 ``` "#]], ); @@ -3353,7 +3360,7 @@ fn bar(&sel$0f) {} *self* ```rust - self: &Foo // size = 8, align = 8 + self: &Foo // size = 8, align = 8, niches = 1 ``` "#]], ); @@ -3758,7 +3765,7 @@ fn hover_const_generic_type_alias() { ``` ```rust - type Foo2 = Foo<2> // size = 0x0, align = 0x1 + type Foo2 = Foo<2> // size = 0, align = 1 ``` "#]], ); @@ -3800,7 +3807,7 @@ enum E { ``` ```rust - A = 8 + A = 8 // size = 1, align = 1 ``` --- @@ -3825,7 +3832,7 @@ enum E { ``` ```rust - A = 12 (0xC) + A = 12 (0xC) // size = 1, align = 1 ``` --- @@ -3851,7 +3858,7 @@ enum E { ``` ```rust - B = 2 + B = 2 // size = 1, align = 1 ``` --- @@ -3877,7 +3884,7 @@ enum E { ``` ```rust - B = 5 + B = 5 // size = 1, align = 1 ``` --- @@ -4411,7 +4418,7 @@ fn foo(e: E) { ``` ```rust - A = 3 + A = 3 // size = 0, align = 1 ``` --- @@ -4433,7 +4440,7 @@ fn main() { *tile4* ```rust - let tile4: [u32; 8] // size = 32, align = 4 + let tile4: [u32; 8] // size = 32 (0x20), align = 4 ``` "#]], ); @@ -4669,7 +4676,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - pub struct TheItem // size = 0x0, align = 0x1 + pub struct TheItem // size = 0, align = 1 ``` --- @@ -4817,7 +4824,7 @@ mod string { ``` ```rust - struct String // size = 0x0, align = 0x1 + struct String // size = 0, align = 1 ``` --- @@ -5486,7 +5493,7 @@ macro_rules! foo_macro { ``` ```rust - pub struct Foo // size = 0x0, align = 0x1 + pub struct Foo // size = 0, align = 1 ``` --- @@ -5511,7 +5518,7 @@ fn hover_intra_in_attr() { ``` ```rust - pub struct Foo // size = 0x4, align = 0x4 + pub struct Foo // size = 4, align = 4 ``` --- @@ -5610,7 +5617,7 @@ enum Enum { ``` ```rust - RecordV { field: u32 } // size = 0x4 + RecordV { field: u32 } // size = 4, align = 4 ``` "#]], ); @@ -5632,7 +5639,7 @@ enum Enum { ``` ```rust - field: u32 // size = 0x4, align = 0x4 + field: u32 // size = 4, align = 4 ``` "#]], ); @@ -6134,7 +6141,7 @@ fn test() { ``` ```rust - f: u32 // size = 0x4, align = 0x4, offset = 0x0 + f: u32 // size = 4, align = 4, offset = 0 ``` "#]], ); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index c02dbc60a33..87e769e4230 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -84,7 +84,10 @@ macro_rules! eprintln { file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, - hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, + hover::{ + HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, + MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + }, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index e7b223caab4..3e3d9f8f85c 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -138,7 +138,7 @@ fn add_file(&mut self, file_id: FileId) { }); let hover_config = HoverConfig { links_in_hover: true, - memory_layout: true, + memory_layout: None, documentation: true, keywords: true, format: crate::HoverDocFormat::Markdown, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 649ac90fc6e..6355c620f78 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -14,7 +14,7 @@ use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, - JoinLinesConfig, Snippet, SnippetScope, + JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -317,8 +317,16 @@ struct ConfigData { hover_documentation_keywords_enable: bool = "true", /// Use markdown syntax for links on hover. hover_links_enable: bool = "true", + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = "\"hexadecimal\"", /// Whether to show memory layout data on hover. hover_memoryLayout_enable: bool = "true", + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = "false", + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = "\"hexadecimal\"", + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = "\"both\"", /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = "false", @@ -1514,9 +1522,19 @@ pub fn highlighting_config(&self) -> HighlightConfig { } pub fn hover(&self) -> HoverConfig { + let mem_kind = |kind| match kind { + MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both, + MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal, + MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal, + }; HoverConfig { links_in_hover: self.data.hover_links_enable, - memory_layout: self.data.hover_memoryLayout_enable, + memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig { + size: self.data.hover_memoryLayout_size.map(mem_kind), + offset: self.data.hover_memoryLayout_offset.map(mem_kind), + alignment: self.data.hover_memoryLayout_alignment.map(mem_kind), + niches: self.data.hover_memoryLayout_niches.unwrap_or_default(), + }), documentation: self.data.hover_documentation_enable, format: { let is_markdown = try_or_def!(self @@ -1726,6 +1744,9 @@ mod de_unit_v { named_unit_variant!(reborrow); named_unit_variant!(fieldless); named_unit_variant!(with_block); + named_unit_variant!(decimal); + named_unit_variant!(hexadecimal); + named_unit_variant!(both); } #[derive(Deserialize, Debug, Clone, Copy)] @@ -1956,6 +1977,18 @@ enum WorkspaceSymbolSearchKindDef { AllSymbols, } +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +#[serde(untagged)] +pub enum MemoryLayoutHoverRenderKindDef { + #[serde(deserialize_with = "de_unit_v::decimal")] + Decimal, + #[serde(deserialize_with = "de_unit_v::hexadecimal")] + Hexadecimal, + #[serde(deserialize_with = "de_unit_v::both")] + Both, +} + macro_rules! _config_data { (struct $name:ident { $( @@ -2038,7 +2071,9 @@ fn get_field( None } }) - .unwrap_or_else(|| serde_json::from_str(default).unwrap()) + .unwrap_or_else(|| { + serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`")) + }) } fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value { @@ -2366,6 +2401,22 @@ macro_rules! set { "`hide`: Shows `...` for every closure type", ], }, + "Option" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": ["both", "decimal", "hexadecimal", ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ], + }, + ], + }, _ => panic!("missing entry for {ty}: {default}"), } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index dc97366eef1..ea00c9540ff 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -428,11 +428,31 @@ Whether to show keyword hover popups. Only applies when -- Use markdown syntax for links on hover. -- +[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: ++ +-- +How to render the align information in a memory layout hover. +-- [[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`):: + -- Whether to show memory layout data on hover. -- +[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`):: ++ +-- +How to render the niche information in a memory layout hover. +-- +[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`):: ++ +-- +How to render the offset information in a memory layout hover. +-- +[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`):: ++ +-- +How to render the size information in a memory layout hover. +-- [[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index aa63c40c0d2..ee1f832d323 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -966,11 +966,85 @@ "default": true, "type": "boolean" }, + "rust-analyzer.hover.memoryLayout.alignment": { + "markdownDescription": "How to render the align information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, "rust-analyzer.hover.memoryLayout.enable": { "markdownDescription": "Whether to show memory layout data on hover.", "default": true, "type": "boolean" }, + "rust-analyzer.hover.memoryLayout.niches": { + "markdownDescription": "How to render the niche information in a memory layout hover.", + "default": false, + "type": [ + "null", + "boolean" + ] + }, + "rust-analyzer.hover.memoryLayout.offset": { + "markdownDescription": "How to render the offset information in a memory layout hover.", + "default": "hexadecimal", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, + "rust-analyzer.hover.memoryLayout.size": { + "markdownDescription": "How to render the size information in a memory layout hover.", + "default": "both", + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "both", + "decimal", + "hexadecimal" + ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ] + } + ] + }, "rust-analyzer.imports.granularity.enforce": { "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", "default": false,