diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 058d5059b169..a401c49ed21d 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -16,21 +16,26 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, + EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use intern::{Internable, Interned}; use itertools::Itertools; +use la_arena::ArenaMap; use smallvec::SmallVec; use stdx::never; use crate::{ + consteval::try_const_usize, db::HirDatabase, - from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, + from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, + layout::Layout, + lt_from_placeholder_idx, mapping::from_chalk, mir::pad16, primitive, to_assoc_type_id, - utils::{self, generics, ClosureSubst}, + utils::{self, detect_variant_from_bytes, generics, ClosureSubst}, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, @@ -469,7 +474,7 @@ fn render_const_scalar( // infrastructure and have it here as a field on `f`. let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); match ty.kind(Interner) { - chalk_ir::TyKind::Scalar(s) => match s { + TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), Scalar::Char => { let x = u128::from_le_bytes(pad16(b, false)) as u32; @@ -497,17 +502,54 @@ fn render_const_scalar( } }, }, - chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) { - chalk_ir::TyKind::Str => { + TyKind::Ref(_, _, t) => match t.kind(Interner) { + TyKind::Str => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); - let bytes = memory_map.memory.get(&addr).map(|x| &**x).unwrap_or(&[]); - let s = std::str::from_utf8(bytes).unwrap_or(""); + let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + let s = std::str::from_utf8(&bytes).unwrap_or(""); write!(f, "{s:?}") } - _ => f.write_str(""), + TyKind::Slice(ty) => { + let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); + let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size_one * count) else { + return f.write_str(""); + }; + f.write_str("&[")?; + let mut first = true; + for i in 0..count { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + _ => { + let addr = usize::from_le_bytes(b.try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + f.write_str("&")?; + render_const_scalar(f, bytes, memory_map, t) + } }, - chalk_ir::TyKind::Tuple(_, subst) => { - let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else { + TyKind::Tuple(_, subst) => { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { return f.write_str(""); }; f.write_str("(")?; @@ -529,69 +571,144 @@ fn render_const_scalar( } f.write_str(")") } - chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { - hir_def::AdtId::StructId(s) => { - let data = f.db.struct_data(s); - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { - return f.write_str(""); - }; - match data.variant_data.as_ref() { - VariantData::Record(fields) | VariantData::Tuple(fields) => { - let field_types = f.db.field_types(s.into()); - let krate = adt.0.module(f.db.upcast()).krate(); - let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { - let offset = layout - .fields - .offset(u32::from(id.into_raw()) as usize) - .bytes_usize(); - let ty = field_types[id].clone().substitute(Interner, subst); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { - return f.write_str(""); - }; - let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) - }; - let mut it = fields.iter(); - if matches!(data.variant_data.as_ref(), VariantData::Record(_)) { - write!(f, "{} {{", data.name.display(f.db.upcast()))?; - if let Some((id, data)) = it.next() { - write!(f, " {}: ", data.name.display(f.db.upcast()))?; - render_field(f, id)?; - } - for (id, data) in it { - write!(f, ", {}: ", data.name.display(f.db.upcast()))?; - render_field(f, id)?; - } - write!(f, " }}")?; - } else { - let mut it = it.map(|x| x.0); - write!(f, "{}(", data.name.display(f.db.upcast()))?; - if let Some(id) = it.next() { - render_field(f, id)?; - } - for id in it { - write!(f, ", ")?; - render_field(f, id)?; - } - write!(f, ")")?; - } - return Ok(()); - } - VariantData::Unit => write!(f, "{}", data.name.display(f.db.upcast())), + TyKind::Adt(adt, subst) => { + let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { + return f.write_str(""); + }; + match adt.0 { + hir_def::AdtId::StructId(s) => { + let data = f.db.struct_data(s); + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = f.db.field_types(s.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &layout, + subst, + b, + memory_map, + ) + } + hir_def::AdtId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) + } + hir_def::AdtId::EnumId(e) => { + let Some((var_id, var_layout)) = + detect_variant_from_bytes(&layout, f.db, krate, b, e) else { + return f.write_str(""); + }; + let data = &f.db.enum_data(e).variants[var_id]; + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = + f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &var_layout, + subst, + b, + memory_map, + ) } } - hir_def::AdtId::UnionId(u) => { - write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) - } - hir_def::AdtId::EnumId(_) => f.write_str(""), - }, - chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f), - chalk_ir::TyKind::Raw(_, _) => { + } + TyKind::FnDef(..) => ty.hir_fmt(f), + TyKind::Function(_) | TyKind::Raw(_, _) => { let x = u128::from_le_bytes(pad16(b, false)); write!(f, "{:#X} as ", x)?; ty.hir_fmt(f) } - _ => f.write_str(""), + TyKind::Array(ty, len) => { + let Some(len) = try_const_usize(f.db, len) else { + return f.write_str(""); + }; + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + f.write_str("[")?; + let mut first = true; + for i in 0..len as usize { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + TyKind::Never => f.write_str("!"), + TyKind::Closure(_, _) => f.write_str(""), + TyKind::Generator(_, _) => f.write_str(""), + TyKind::GeneratorWitness(_, _) => f.write_str(""), + // The below arms are unreachable, since const eval will bail out before here. + TyKind::Foreign(_) => f.write_str(""), + TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Alias(_) + | TyKind::AssociatedType(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => f.write_str(""), + // The below arms are unreachable, since we handled them in ref case. + TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str(""), + } +} + +fn render_variant_after_name( + data: &VariantData, + f: &mut HirFormatter<'_>, + field_types: &ArenaMap>, + krate: CrateId, + layout: &Layout, + subst: &Substitution, + b: &[u8], + memory_map: &MemoryMap, +) -> Result<(), HirDisplayError> { + match data { + VariantData::Record(fields) | VariantData::Tuple(fields) => { + let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { + let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); + let ty = field_types[id].clone().substitute(Interner, subst); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) + }; + let mut it = fields.iter(); + if matches!(data, VariantData::Record(_)) { + write!(f, " {{")?; + if let Some((id, data)) = it.next() { + write!(f, " {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + for (id, data) in it { + write!(f, ", {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + write!(f, " }}")?; + } else { + let mut it = it.map(|x| x.0); + write!(f, "(")?; + if let Some(id) = it.next() { + render_field(f, id)?; + } + for id in it { + write!(f, ", ")?; + render_field(f, id)?; + } + write!(f, ")")?; + } + return Ok(()); + } + VariantData::Unit => Ok(()), } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 55803960e1a0..1a4d003bf5e9 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -35,7 +35,10 @@ mod tests; #[cfg(test)] mod test_db; -use std::{collections::HashMap, hash::Hash}; +use std::{ + collections::{hash_map::Entry, HashMap}, + hash::Hash, +}; use chalk_ir::{ fold::{Shift, TypeFoldable}, @@ -160,7 +163,16 @@ pub struct MemoryMap { impl MemoryMap { fn insert(&mut self, addr: usize, x: Vec) { - self.memory.insert(addr, x); + match self.memory.entry(addr) { + Entry::Occupied(mut e) => { + if e.get().len() < x.len() { + e.insert(x); + } + } + Entry::Vacant(e) => { + e.insert(x); + } + } } /// This functions convert each address by a function `f` which gets the byte intervals and assign an address @@ -172,6 +184,14 @@ impl MemoryMap { ) -> Result, MirEvalError> { self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() } + + fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> { + if size == 0 { + Some(&[]) + } else { + self.memory.get(&addr)?.get(0..size) + } + } } /// A concrete constant value diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 28e7759db39a..6e26d1f22aad 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -30,7 +30,7 @@ use crate::{ method_resolution::{is_dyn_method, lookup_impl_method}, name, static_lifetime, traits::FnTrait, - utils::ClosureSubst, + utils::{detect_variant_from_bytes, ClosureSubst}, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; @@ -1536,36 +1536,99 @@ impl Evaluator<'_> { } fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { - // FIXME: support indirect references - let mut mm = MemoryMap::default(); - match ty.kind(Interner) { - TyKind::Ref(_, _, t) => { - let size = self.size_align_of(t, locals)?; - match size { - Some((size, _)) => { - let addr_usize = from_bytes!(usize, bytes); - mm.insert( - addr_usize, - self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), - ) - } - None => { - let element_size = match t.kind(Interner) { - TyKind::Str => 1, - TyKind::Slice(t) => { - self.size_of_sized(t, locals, "slice inner type")? + fn rec( + this: &Evaluator<'_>, + bytes: &[u8], + ty: &Ty, + locals: &Locals<'_>, + mm: &mut MemoryMap, + ) -> Result<()> { + match ty.kind(Interner) { + TyKind::Ref(_, _, t) => { + let size = this.size_align_of(t, locals)?; + match size { + Some((size, _)) => { + let addr_usize = from_bytes!(usize, bytes); + mm.insert( + addr_usize, + this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), + ) + } + None => { + let mut check_inner = None; + let element_size = match t.kind(Interner) { + TyKind::Str => 1, + TyKind::Slice(t) => { + check_inner = Some(t); + this.size_of_sized(t, locals, "slice inner type")? + } + _ => return Ok(()), // FIXME: support other kind of unsized types + }; + let (addr, meta) = bytes.split_at(bytes.len() / 2); + let count = from_bytes!(usize, meta); + let size = element_size * count; + let addr = Address::from_bytes(addr)?; + let b = this.read_memory(addr, size)?; + mm.insert(addr.to_usize(), b.to_vec()); + if let Some(ty) = check_inner { + for i in 0..count { + let offset = element_size * i; + rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + } } - _ => return Ok(mm), // FIXME: support other kind of unsized types - }; - let (addr, meta) = bytes.split_at(bytes.len() / 2); - let size = element_size * from_bytes!(usize, meta); - let addr = Address::from_bytes(addr)?; - mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec()); + } + } + } + chalk_ir::TyKind::Tuple(_, subst) => { + let layout = this.layout(ty)?; + for (id, ty) in subst.iter(Interner).enumerate() { + let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + let offset = layout.fields.offset(id).bytes_usize(); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; } } + chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { + AdtId::StructId(s) => { + let data = this.db.struct_data(s); + let layout = this.layout(ty)?; + let field_types = this.db.field_types(s.into()); + for (f, _) in data.variant_data.fields().iter() { + let offset = layout + .fields + .offset(u32::from(f.into_raw()) as usize) + .bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + AdtId::EnumId(e) => { + let layout = this.layout(ty)?; + if let Some((v, l)) = + detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e) + { + let data = &this.db.enum_data(e).variants[v].variant_data; + let field_types = this + .db + .field_types(EnumVariantId { parent: e, local_id: v }.into()); + for (f, _) in data.fields().iter() { + let offset = + l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + } + AdtId::UnionId(_) => (), + }, + _ => (), } - _ => (), + Ok(()) } + let mut mm = MemoryMap::default(); + rec(self, bytes, ty, locals, &mut mm)?; Ok(mm) } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 6fe157f45cfe..5ed95133356c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1855,7 +1855,7 @@ pub fn lower_to_mir( } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local - ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() }); + ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) }); let binding_picker = |b: BindingId| { if root_expr == body.body_expr { body[b].owner.is_none() diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index f60b4607f224..8f36188b78a3 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -20,8 +20,8 @@ use hir_def::{ lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, - TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, + LocalEnumVariantId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -30,8 +30,12 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use crate::{ - consteval::unknown_const, db::HirDatabase, ChalkTraitId, Const, ConstScalar, GenericArg, - Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, + consteval::unknown_const, + db::HirDatabase, + layout::{Layout, TagEncoding}, + mir::pad16, + ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, + Ty, TyExt, WhereClause, }; pub(crate) fn fn_traits( @@ -440,3 +444,41 @@ impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { Ok(constant) } } + +pub(crate) fn detect_variant_from_bytes<'a>( + layout: &'a Layout, + db: &dyn HirDatabase, + krate: CrateId, + b: &[u8], + e: EnumId, +) -> Option<(LocalEnumVariantId, &'a Layout)> { + let (var_id, var_layout) = match &layout.variants { + hir_def::layout::Variants::Single { index } => (index.0, &*layout), + hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => { + let target_data_layout = db.target_data_layout(krate)?; + let size = tag.size(&*target_data_layout).bytes_usize(); + let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false)); + match tag_encoding { + TagEncoding::Direct => { + let x = variants.iter_enumerated().find(|x| { + db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 }) + == Ok(tag) + })?; + (x.0 .0, x.1) + } + TagEncoding::Niche { untagged_variant, niche_start, .. } => { + let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize; + let variant = variants + .iter_enumerated() + .map(|(x, _)| x) + .filter(|x| x != untagged_variant) + .nth(candidate_tag) + .unwrap_or(*untagged_variant); + (variant.0, &variants[variant]) + } + } + } + }; + Some((var_id, var_layout)) +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7c432197a60a..9e85690ec569 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2107,14 +2107,6 @@ impl Const { pub fn render_eval(self, db: &dyn HirDatabase) -> Result { let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; let r = format!("{}", HexifiedConst(c).display(db)); - // We want to see things like `` and `` as they are probably bug in our - // implementation, but there is no need to show things like `` or `` to - // the user. - if r.contains("not-supported>") { - return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported( - "rendering complex constants".to_string(), - ))); - } return Ok(r); } } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f2ee79a23e6f..d2c035c471e4 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -4337,6 +4337,211 @@ const FOO$0: f64 = 1.0f64; ); } +#[test] +fn hover_const_eval_enum() { + check( + r#" +enum Enum { + V1, + V2, +} + +const VX: Enum = Enum::V1; + +const FOO$0: Enum = VX; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Enum = V1 + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option = Some(2); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option = Some(2) + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option<&i32> = Some(2).as_ref(); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option<&i32> = Some(&2) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_slice() { + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32] = &[1, 2, 3 + 4]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32] = &[1, 2, 7] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32; 5] = &[12; 5]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32; 5] = &[12, 12, 12, 12, 12] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +const FOO$0: (&i32, &[i32], &i32) = { + let a: &[i32] = &[1, 2, 3]; + (&a[0], a, &a[0]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1) + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +struct Tree(&[Tree]); + +const FOO$0: Tree = { + let x = &[Tree(&[]), Tree(&[Tree(&[])])]; + Tree(&[Tree(x), Tree(x)]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])]) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_str() { + check( + r#" +const FOO$0: &str = "foo"; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &str = "foo" + ``` + "#]], + ); + check( + r#" +struct X { + a: &'static str, + b: &'static str, +} +const FOO$0: X = X { + a: "axiom", + b: "buy N large", +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: X = X { a: "axiom", b: "buy N large" } + ``` + "#]], + ); + check( + r#" +const FOO$0: (&str, &str) = { + let x = "foo"; + (x, x) +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&str, &str) = ("foo", "foo") + ``` + "#]], + ); +} + #[test] fn hover_const_eval_in_generic_trait() { // Doesn't compile, but we shouldn't crash. diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 9f4934492495..ce1e03a069b2 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -474,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -487,7 +487,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", @@ -511,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -524,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", @@ -548,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 9165..9173, + range: 9332..9340, }, ), tooltip: "", @@ -561,7 +561,7 @@ fn main() { file_id: FileId( 1, ), - range: 9197..9201, + range: 9364..9368, }, ), tooltip: "", diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 7f4838888bd7..c9e85e36870f 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -941,6 +941,13 @@ pub mod option { } } + pub const fn as_ref(&self) -> Option<&T> { + match self { + Some(x) => Some(x), + None => None, + } + } + pub fn and(self, optb: Option) -> Option { loop {} }