Skip to content

feat: Display the value of enum variant on hover #12966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion crates/hir-def/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
src::HasSource,
src::{HasChildSource, HasSource},
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
UnresolvedMacro,
};
Expand Down Expand Up @@ -324,6 +324,12 @@ impl Body {
let src = s.source(db);
(src.file_id, s.module(db), src.value.body())
}
DefWithBodyId::VariantId(v) => {
let e = v.parent.lookup(db);
let src = v.parent.child_source(db);
let variant = &src.value[v.local_id];
(src.file_id, e.container, variant.expr())
}
};
let expander = Expander::new(db, file_id, module);
let (mut body, source_map) = Body::new(db, expander, params, body);
Expand Down
12 changes: 12 additions & 0 deletions crates/hir-def/src/body/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use std::fmt::{self, Write};

use syntax::ast::HasName;

use crate::{
expr::{Array, BindingAnnotation, Literal, Statement},
pretty::{print_generic_args, print_path, print_type_ref},
Expand Down Expand Up @@ -32,6 +34,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
};
format!("const {} = ", name)
}
DefWithBodyId::VariantId(it) => {
needs_semi = false;
let src = it.parent.child_source(db);
let variant = &src.value[it.local_id];
let name = match &variant.name() {
Some(name) => name.to_string(),
None => "_".to_string(),
};
format!("{}", name)
}
};

let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
Expand Down
11 changes: 11 additions & 0 deletions crates/hir-def/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,16 +474,25 @@ pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
VariantId(EnumVariantId),
}

impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);

// FIXME: Rename EnumVariantId to VariantId so that the macro above can be used
impl From<EnumVariantId> for DefWithBodyId {
fn from(id: EnumVariantId) -> Self {
DefWithBodyId::VariantId(id)
}
}

impl DefWithBodyId {
pub fn as_generic_def_id(self) -> Option<GenericDefId> {
match self {
DefWithBodyId::FunctionId(f) => Some(f.into()),
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
}
}
}
Expand Down Expand Up @@ -681,6 +690,7 @@ impl HasModule for DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
}
}
}
Expand All @@ -691,6 +701,7 @@ impl DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/hir-def/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::ConstId(c) => c.resolver(db),
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
}
}
}
Expand Down
85 changes: 82 additions & 3 deletions crates/hir-ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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,
Expand Down Expand Up @@ -77,6 +80,7 @@ pub enum ConstEvalError {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComputedExpr {
Literal(Literal),
Enum(String, EnumVariantId, Literal),
Tuple(Box<[ComputedExpr]>),
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -148,13 +153,47 @@ 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<'_>,
) -> Result<ComputedExpr, ConstEvalError> {
let expr = &ctx.exprs[expr_id];
match expr {
Expr::Missing => Err(ConstEvalError::IncompleteExpr),
Expr::Missing => match ctx.owner {
DefWithBodyId::VariantId(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::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
}
_ => Err(ConstEvalError::IncompleteExpr),
},
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
&Expr::UnaryOp { expr, op } => {
let ty = &ctx.expr_ty(expr);
Expand Down Expand Up @@ -339,9 +378,21 @@ pub fn eval_const(
ValueNs::GenericParam(_) => {
Err(ConstEvalError::NotSupported("const generic without substitution"))
}
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)? {
ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
_ => Err(ConstEvalError::NotSupported("Can't cast these types")),
},
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
}
}
Expand Down Expand Up @@ -412,6 +463,14 @@ pub(crate) fn const_eval_recover(
Err(ConstEvalError::Loop)
}

pub(crate) fn const_eval_recover_variant(
_: &dyn HirDatabase,
_: &[String],
_: &EnumVariantId,
) -> Result<ComputedExpr, ConstEvalError> {
Err(ConstEvalError::Loop)
}

pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
const_id: ConstId,
Expand All @@ -433,6 +492,26 @@ pub(crate) fn const_eval_query(
result
}

pub(crate) fn const_eval_query_variant(
db: &dyn HirDatabase,
variant_id: EnumVariantId,
) -> Result<ComputedExpr, ConstEvalError> {
let def = variant_id.into();
let body = db.body(def);
let infer = &db.infer(def);
eval_const(
body.body_expr,
&mut ConstEvalCtx {
db,
owner: def,
exprs: &body.exprs,
pats: &body.pats,
local_data: HashMap::default(),
infer,
},
)
}

pub(crate) fn eval_to_const<'a>(
expr: Idx<Expr>,
mode: ParamLoweringMode,
Expand Down
43 changes: 43 additions & 0 deletions crates/hir-ty/src/consteval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,49 @@ fn consts() {
);
}

#[test]
fn enums() {
check_number(
r#"
enum E {
F1 = 1,
F2 = 2 * E::F1 as u8,
F3 = 3 * E::F2 as u8,
}
const GOAL: i32 = E::F3 as u8;
"#,
6,
);
check_number(
r#"
enum E { F1 = 1, F2, }
const GOAL: i32 = E::F2 as u8;
"#,
2,
);
check_number(
r#"
enum E { F1, }
const GOAL: i32 = E::F1 as u8;
"#,
0,
);
let r = eval_goal(
r#"
enum E { A = 1, }
const GOAL: E = E::A;
"#,
)
.unwrap();
match r {
ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
assert_eq!(name, "E::A");
assert_eq!(val, 1);
}
x => panic!("Expected enum but found {:?}", x),
}
}

#[test]
fn const_loop() {
check_fail(
Expand Down
16 changes: 14 additions & 2 deletions crates/hir-ty/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use std::sync::Arc;
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, Lookup, TypeOrConstParamId,
VariantId,
};
use la_arena::ArenaMap;

Expand Down Expand Up @@ -47,6 +48,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;

#[salsa::invoke(crate::consteval::const_eval_query_variant)]
#[salsa::cycle(crate::consteval::const_eval_recover_variant)]
fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;

#[salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;

Expand Down Expand Up @@ -188,6 +193,13 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
DefWithBodyId::ConstId(it) => {
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
}
DefWithBodyId::VariantId(it) => {
let up_db: &dyn DefDatabase = db.upcast();
let loc = it.parent.lookup(up_db);
let item_tree = loc.id.item_tree(up_db);
let konst = &item_tree[loc.id.value];
konst.name.to_string()
}
});
db.infer_query(def)
}
Expand Down
4 changes: 3 additions & 1 deletion crates/hir-ty/src/diagnostics/unsafe_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {

let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
false
}
};
if is_unsafe {
return res;
Expand Down
4 changes: 4 additions & 0 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => {
// FIXME: This should return the `repr(...)` type of the enum
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
}
}

ctx.infer_body();
Expand Down
10 changes: 9 additions & 1 deletion crates/hir-ty/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
db::DefDatabase,
db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId},
item_scope::ItemScope,
nameres::DefMap,
Expand Down Expand Up @@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
DefWithBodyId::VariantId(it) => {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
Expand Down Expand Up @@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
DefWithBodyId::VariantId(it) => {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
});
for def in defs {
let (_body, source_map) = db.body_with_source_map(def);
Expand Down
Loading