about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOleStrohm <strohm99@gmail.com>2022-08-06 22:12:33 +0200
committerOleStrohm <strohm99@gmail.com>2022-09-12 20:20:22 +0100
commit2f84b6e2e51442755dfd9c8ae5cc5eb9020e52cb (patch)
tree6b559b2196e2b9f46ebc04a7ebd786dcac4d3a4b
parentb63234e20be04ae0e7eca20e0bf49dca8d7d3718 (diff)
downloadrust-2f84b6e2e51442755dfd9c8ae5cc5eb9020e52cb.tar.gz
rust-2f84b6e2e51442755dfd9c8ae5cc5eb9020e52cb.zip
Almost there
-rw-r--r--crates/hir-def/src/body.rs4
-rw-r--r--crates/hir-ty/src/consteval.rs89
-rw-r--r--crates/hir-ty/src/infer.rs3
-rw-r--r--crates/ide/src/hover/render.rs14
-rw-r--r--crates/ide/src/hover/tests.rs53
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 @@ use crate::{
     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 std::{
 
 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 @@ impl Display for ComputedExpr {
                 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 @@ impl Display for ComputedExpr {
     }
 }
 
+impl ComputedExpr {
+    pub fn enum_value(&self) -> Option<ComputedExpr> {
+        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<EnumVariantId>,
 ) -> Result<ComputedExpr, ConstEvalError> {
     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 @@ use hir_def::{
     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<Infer
         DefWithBodyId::FunctionId(f) => 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 std::fmt::Display;
 
 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<const LEN: usize> Foo<LEN$0> {}
 
 #[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#"
@@ -3605,6 +3630,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
+        "#]],
+    );
 }
 
 #[test]