From 2f84b6e2e51442755dfd9c8ae5cc5eb9020e52cb Mon Sep 17 00:00:00 2001 From: OleStrohm Date: Sat, 6 Aug 2022 22:12:33 +0200 Subject: [PATCH] Almost there --- crates/hir-def/src/body.rs | 4 +- crates/hir-ty/src/consteval.rs | 89 ++++++++++++++++++++++++++++++---- crates/hir-ty/src/infer.rs | 3 +- crates/ide/src/hover/render.rs | 14 +++--- crates/ide/src/hover/tests.rs | 53 +++++++++++++++++++- 5 files changed, 141 insertions(+), 22 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 484e2d7d7dd..be1a9d11773 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -28,8 +28,8 @@ nameres::DefMap, path::{ModPath, Path}, src::{HasChildSource, HasSource}, - AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId, - UnresolvedMacro, + AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, + ModuleId, UnresolvedMacro, }; pub use lower::LowerCtx; diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index b47d7308941..efb17c4780a 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -7,14 +7,17 @@ use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar}; use hir_def::{ + builtin_type::BuiltinInt, expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId}, path::ModPath, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, + src::HasChildSource, type_ref::ConstScalar, - ConstId, DefWithBodyId, EnumVariantId, + ConstId, DefWithBodyId, EnumVariantId, Lookup, }; -use la_arena::{Arena, Idx}; +use la_arena::{Arena, Idx, RawIdx}; use stdx::never; +use syntax::ast::HasName; use crate::{ db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx, @@ -77,6 +80,7 @@ pub enum ConstEvalError { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ComputedExpr { Literal(Literal), + Enum(String, EnumVariantId, Literal), Tuple(Box<[ComputedExpr]>), } @@ -104,6 +108,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Literal::String(x) => std::fmt::Debug::fmt(x, f), Literal::ByteString(x) => std::fmt::Debug::fmt(x, f), }, + ComputedExpr::Enum(name, _, _) => name.fmt(f), ComputedExpr::Tuple(t) => { f.write_char('(')?; for x in &**t { @@ -116,6 +121,15 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { } } +impl ComputedExpr { + pub fn enum_value(&self) -> Option { + match self { + ComputedExpr::Enum(_, _, lit) => Some(ComputedExpr::Literal(lit.clone())), + _ => None, + } + } +} + fn scalar_max(scalar: &Scalar) -> i128 { match scalar { Scalar::Bool => 1, @@ -148,17 +162,56 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool { } } +fn get_name(variant: EnumVariantId, ctx: &mut ConstEvalCtx<'_>) -> String { + let loc = variant.parent.lookup(ctx.db.upcast()); + let children = variant.parent.child_source(ctx.db.upcast()); + let item_tree = loc.id.item_tree(ctx.db.upcast()); + + let variant_name = children.value[variant.local_id].name(); + let enum_name = item_tree[loc.id.value].name.to_string(); + enum_name + "::" + &variant_name.unwrap().to_string() +} + pub fn eval_const( expr_id: ExprId, ctx: &mut ConstEvalCtx<'_>, + variant: Option, ) -> Result { let expr = &ctx.exprs[expr_id]; match expr { - Expr::Missing => Err(ConstEvalError::IncompleteExpr), + Expr::Missing => match variant { + Some(variant) => { + let prev_idx: u32 = variant.local_id.into_raw().into(); + let prev_idx = prev_idx.checked_sub(1).map(|idx| Idx::from_raw(RawIdx::from(idx))); + let value = match prev_idx { + Some(prev) => { + let prev_variant = EnumVariantId { local_id: prev, ..variant }; + 1 + match ctx.db.const_eval_variant(prev_variant)? { + ComputedExpr::Literal(Literal::Int(v, _)) => v, + ComputedExpr::Literal(Literal::Uint(v, _)) => v + .try_into() + .map_err(|_| ConstEvalError::NotSupported("too big u128"))?, + _ => { + return Err(ConstEvalError::NotSupported( + "Enum can't contain this kind of value", + )) + } + } + } + _ => 0, + }; + Ok(ComputedExpr::Enum( + get_name(variant, ctx), + variant, + Literal::Int(value + 1, Some(BuiltinInt::I128)), + )) + } + _ => Err(ConstEvalError::IncompleteExpr), + }, Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())), &Expr::UnaryOp { expr, op } => { let ty = &ctx.expr_ty(expr); - let ev = eval_const(expr, ctx)?; + let ev = eval_const(expr, ctx, None)?; match op { hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")), hir_def::expr::UnaryOp::Not => { @@ -214,8 +267,8 @@ pub fn eval_const( } &Expr::BinaryOp { lhs, rhs, op } => { let ty = &ctx.expr_ty(lhs); - let lhs = eval_const(lhs, ctx)?; - let rhs = eval_const(rhs, ctx)?; + let lhs = eval_const(lhs, ctx, None)?; + let rhs = eval_const(rhs, ctx, None)?; let op = op.ok_or(ConstEvalError::IncompleteExpr)?; let v1 = match lhs { ComputedExpr::Literal(Literal::Int(v, _)) => v, @@ -276,7 +329,7 @@ pub fn eval_const( } }; let value = match initializer { - Some(x) => eval_const(x, ctx)?, + Some(x) => eval_const(x, ctx, None)?, None => continue, }; if !prev_values.contains_key(&pat_id) { @@ -292,7 +345,7 @@ pub fn eval_const( } } let r = match tail { - &Some(x) => eval_const(x, ctx), + &Some(x) => eval_const(x, ctx, None), None => Ok(ComputedExpr::Tuple(Box::new([]))), }; // clean up local data, so caller will receive the exact map that passed to us @@ -339,10 +392,24 @@ pub fn eval_const( ValueNs::GenericParam(_) => { Err(ConstEvalError::NotSupported("const generic without substitution")) } - ValueNs::EnumVariantId(id) => ctx.db.const_eval_variant(id), + ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? { + ComputedExpr::Literal(lit) => { + Ok(ComputedExpr::Enum(get_name(id, ctx), id, lit)) + } + _ => Err(ConstEvalError::NotSupported( + "Enums can't evalute to anything but numbers", + )), + }, _ => Err(ConstEvalError::NotSupported("path that are not const or local")), } } + Expr::Cast { expr, .. } => match eval_const(*expr, ctx, None)? { + ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)), + expr => Err(ConstEvalError::NotSupported(Box::leak(Box::new(format!( + "Can't cast type: {:?}", + expr + ))))), + }, _ => Err(ConstEvalError::NotSupported("This kind of expression")), } } @@ -438,6 +505,7 @@ pub(crate) fn const_eval_query( local_data: HashMap::default(), infer, }, + None, ); result } @@ -459,6 +527,7 @@ pub(crate) fn const_eval_query_variant( local_data: HashMap::default(), infer, }, + Some(variant_id), ) } @@ -485,7 +554,7 @@ pub(crate) fn eval_to_const<'a>( local_data: HashMap::default(), infer: &ctx.result, }; - let computed_expr = eval_const(expr, &mut ctx); + let computed_expr = eval_const(expr, &mut ctx, None); let const_scalar = match computed_expr { Ok(ComputedExpr::Literal(literal)) => literal.into(), _ => ConstScalar::Unknown, diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index c821a3786b9..285ec7520f4 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -26,7 +26,7 @@ resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, - TraitId, TypeAliasId, VariantId + TraitId, TypeAliasId, VariantId, }; use hir_expand::name::{name, Name}; use itertools::Either; @@ -68,7 +68,6 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_fn(f), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), DefWithBodyId::VariantId(v) => { - // TODO(ole): Get the real type ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build() } } diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 8ac268f2438..44291a1a88b 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -3,7 +3,7 @@ use either::Either; use hir::{ - db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo, + db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, StructKind, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -348,12 +348,12 @@ pub(super) fn definition( Definition::Module(it) => label_and_docs(db, it), Definition::Function(it) => label_and_docs(db, it), Definition::Adt(it) => label_and_docs(db, it), - Definition::Variant(it) => label_value_and_docs(db, it, |&it| { - let body = it.eval(db); - match body { - Ok(x) => Some(format!("{}", x)), - Err(_) => it.value(db).map(|x| format!("{}", x)), - } + Definition::Variant(it) => label_value_and_docs(db, it, |&it| match it.kind(db) { + StructKind::Unit => match it.eval(db) { + Ok(x) => Some(format!("{}", x.enum_value().unwrap_or(x))), + Err(_) => it.value(db).map(|x| format!("{:?}", x)), + }, + _ => None, }), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.eval(db); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f24dec25b6b..b877e6e5c9f 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3529,6 +3529,31 @@ impl Foo {} #[test] fn hover_const_eval_variant() { + check( + r#" +#[repr(u8)] +enum E { + A = 4, + /// This is a doc + B$0 = E::A as u8 + 1, +} +"#, + expect![[r#" + *B* + + ```rust + test::E + ``` + + ```rust + B = 5 + ``` + + --- + + This is a doc + "#]], + ); // show hex for <10 check( r#" @@ -3586,7 +3611,7 @@ enum E { enum E { A = 1, /// This is a doc - B$0 = E::A + 1, + B$0 = E::A as u8 + 1, } "#, expect![[r#" @@ -3602,6 +3627,32 @@ enum E { --- + This is a doc + "#]], + ); + // unspecified variant should increment by one + check( + r#" +#[repr(u8)] +enum E { + A = 4, + /// This is a doc + B$0, +} +"#, + expect![[r#" + *B* + + ```rust + test::E + ``` + + ```rust + B = 5 + ``` + + --- + This is a doc "#]], );