Auto merge of #12966 - OleStrohm:master, r=Veykril

feat: Display the value of enum variant on hover

fixes #12955

This PR adds const eval support for enums, as well as showing their value on hover, just as consts currently have.

I developed these two things at the same time, but I've realized now that they are separate. However since the hover is just a 10 line change (not including tests), I figured I may as well put them in the same PR. Though if you want them split up into "enum const eval support"  and "show enum variant value on hover", I think that's reasonable too.

Since this adds const eval support for enums this also allows consts that reference enums to have their values computed now too.

The const evaluation itself is quite rudimentary, it doesn't keep track of the actual type of the enum, but it turns out that Rust doesn't actually either, and `E::A as u8` is valid regardless of the `repr` on `E`.

It also doesn't really care about what expression the enum variant contains, it could for example be a string, despite that not being allowed, but I guess it's up to the `cargo check` diagnostics to inform of such issues anyway?
This commit is contained in:
bors 2022-09-20 14:01:16 +00:00
commit 817a6a8609
16 changed files with 366 additions and 16 deletions

View File

@ -27,7 +27,7 @@ use crate::{
macro_id_to_def_id, macro_id_to_def_id,
nameres::DefMap, nameres::DefMap,
path::{ModPath, Path}, path::{ModPath, Path},
src::HasSource, src::{HasChildSource, HasSource},
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId, AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
UnresolvedMacro, UnresolvedMacro,
}; };
@ -324,6 +324,12 @@ impl Body {
let src = s.source(db); let src = s.source(db);
(src.file_id, s.module(db), src.value.body()) (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 expander = Expander::new(db, file_id, module);
let (mut body, source_map) = Body::new(db, expander, params, body); let (mut body, source_map) = Body::new(db, expander, params, body);

View File

@ -2,6 +2,8 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use syntax::ast::HasName;
use crate::{ use crate::{
expr::{Array, BindingAnnotation, Literal, Statement}, expr::{Array, BindingAnnotation, Literal, Statement},
pretty::{print_generic_args, print_path, print_type_ref}, pretty::{print_generic_args, print_path, print_type_ref},
@ -32,6 +34,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
}; };
format!("const {} = ", name) 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 }; let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };

View File

@ -474,16 +474,25 @@ pub enum DefWithBodyId {
FunctionId(FunctionId), FunctionId(FunctionId),
StaticId(StaticId), StaticId(StaticId),
ConstId(ConstId), ConstId(ConstId),
VariantId(EnumVariantId),
} }
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId); 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 { impl DefWithBodyId {
pub fn as_generic_def_id(self) -> Option<GenericDefId> { pub fn as_generic_def_id(self) -> Option<GenericDefId> {
match self { match self {
DefWithBodyId::FunctionId(f) => Some(f.into()), DefWithBodyId::FunctionId(f) => Some(f.into()),
DefWithBodyId::StaticId(_) => None, DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()), DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
} }
} }
} }
@ -681,6 +690,7 @@ impl HasModule for DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db), DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
DefWithBodyId::StaticId(it) => it.lookup(db).module(db), DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
DefWithBodyId::ConstId(it) => it.lookup(db).module(db), DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
} }
} }
} }
@ -691,6 +701,7 @@ impl DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(), DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::StaticId(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::ConstId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
} }
} }
} }

View File

@ -839,6 +839,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::ConstId(c) => c.resolver(db), DefWithBodyId::ConstId(c) => c.resolver(db),
DefWithBodyId::FunctionId(f) => f.resolver(db), DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db), DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
} }
} }
} }

View File

@ -7,14 +7,17 @@ use std::{
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar}; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
use hir_def::{ use hir_def::{
builtin_type::BuiltinInt,
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId}, expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
path::ModPath, path::ModPath,
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
src::HasChildSource,
type_ref::ConstScalar, type_ref::ConstScalar,
ConstId, DefWithBodyId, ConstId, DefWithBodyId, EnumVariantId, Lookup,
}; };
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx, RawIdx};
use stdx::never; use stdx::never;
use syntax::ast::HasName;
use crate::{ use crate::{
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx, db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
@ -77,6 +80,7 @@ pub enum ConstEvalError {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComputedExpr { pub enum ComputedExpr {
Literal(Literal), Literal(Literal),
Enum(String, EnumVariantId, Literal),
Tuple(Box<[ComputedExpr]>), Tuple(Box<[ComputedExpr]>),
} }
@ -104,6 +108,7 @@ impl Display for ComputedExpr {
Literal::String(x) => std::fmt::Debug::fmt(x, f), Literal::String(x) => std::fmt::Debug::fmt(x, f),
Literal::ByteString(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) => { ComputedExpr::Tuple(t) => {
f.write_char('(')?; f.write_char('(')?;
for x in &**t { for x in &**t {
@ -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( pub fn eval_const(
expr_id: ExprId, expr_id: ExprId,
ctx: &mut ConstEvalCtx<'_>, ctx: &mut ConstEvalCtx<'_>,
) -> Result<ComputedExpr, ConstEvalError> { ) -> Result<ComputedExpr, ConstEvalError> {
let expr = &ctx.exprs[expr_id]; let expr = &ctx.exprs[expr_id];
match expr { 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::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
&Expr::UnaryOp { expr, op } => { &Expr::UnaryOp { expr, op } => {
let ty = &ctx.expr_ty(expr); let ty = &ctx.expr_ty(expr);
@ -339,9 +378,21 @@ pub fn eval_const(
ValueNs::GenericParam(_) => { ValueNs::GenericParam(_) => {
Err(ConstEvalError::NotSupported("const generic without substitution")) 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")), _ => 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")), _ => Err(ConstEvalError::NotSupported("This kind of expression")),
} }
} }
@ -412,6 +463,14 @@ pub(crate) fn const_eval_recover(
Err(ConstEvalError::Loop) 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( pub(crate) fn const_eval_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
const_id: ConstId, const_id: ConstId,
@ -433,6 +492,26 @@ pub(crate) fn const_eval_query(
result 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>( pub(crate) fn eval_to_const<'a>(
expr: Idx<Expr>, expr: Idx<Expr>,
mode: ParamLoweringMode, mode: ParamLoweringMode,

View File

@ -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] #[test]
fn const_loop() { fn const_loop() {
check_fail( check_fail(

View File

@ -6,8 +6,9 @@ use std::sync::Arc;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{ use hir_def::{
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId, db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, Lookup, TypeOrConstParamId,
VariantId,
}; };
use la_arena::ArenaMap; use la_arena::ArenaMap;
@ -47,6 +48,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::consteval::const_eval_recover)] #[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>; 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)] #[salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
@ -188,6 +193,13 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
DefWithBodyId::ConstId(it) => { DefWithBodyId::ConstId(it) => {
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string() 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) db.infer_query(def)
} }

View File

@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def { let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(), 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 { if is_unsafe {
return res; return res;

View File

@ -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::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f), DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), 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(); ctx.infer_body();

View File

@ -16,7 +16,7 @@ use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect; use expect_test::Expect;
use hir_def::{ use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax}, body::{Body, BodySourceMap, SyntheticSyntax},
db::DefDatabase, db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId}, expr::{ExprId, PatId},
item_scope::ItemScope, item_scope::ItemScope,
nameres::DefMap, nameres::DefMap,
@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = it.lookup(&db); let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start() 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(); let mut unexpected_type_mismatches = String::new();
for def in defs { for def in defs {
@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = it.lookup(&db); let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start() 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 { for def in defs {
let (_body, source_map) = db.body_with_source_map(def); let (_body, source_map) = db.body_with_source_map(def);

View File

@ -140,6 +140,7 @@ impl From<DefWithBody> for DefWithBodyId {
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id), DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id), DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id), DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
} }
} }
} }
@ -150,6 +151,7 @@ impl From<DefWithBodyId> for DefWithBody {
DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()), DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()),
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()), DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()), DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
} }
} }
} }
@ -172,9 +174,7 @@ impl From<GenericDef> for GenericDefId {
GenericDef::Trait(it) => GenericDefId::TraitId(it.id), GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
GenericDef::Variant(it) => { GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id })
}
GenericDef::Const(it) => GenericDefId::ConstId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id),
} }
} }
@ -188,9 +188,7 @@ impl From<GenericDefId> for GenericDef {
GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
GenericDefId::EnumVariantId(it) => { GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
GenericDef::Variant(Variant { parent: it.parent.into(), id: it.local_id })
}
GenericDefId::ConstId(it) => GenericDef::Const(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
} }
} }

View File

@ -73,7 +73,7 @@ use once_cell::unsync::Lazy;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use stdx::{impl_from, never}; use stdx::{impl_from, never};
use syntax::{ use syntax::{
ast::{self, HasAttrs as _, HasDocComments, HasName}, ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
}; };
@ -952,6 +952,10 @@ impl Enum {
pub fn ty(self, db: &dyn HirDatabase) -> Type { pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::from_def(db, self.id) Type::from_def(db, self.id)
} }
pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool {
self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
}
} }
impl HasVisibility for Enum { impl HasVisibility for Enum {
@ -960,6 +964,12 @@ impl HasVisibility for Enum {
} }
} }
impl From<&Variant> for DefWithBodyId {
fn from(&v: &Variant) -> Self {
DefWithBodyId::VariantId(v.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Variant { pub struct Variant {
pub(crate) parent: Enum, pub(crate) parent: Enum,
@ -994,6 +1004,14 @@ impl Variant {
pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.enum_data(self.parent.id).variants[self.id].variant_data.clone() db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
} }
pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> {
self.source(db)?.value.expr()
}
pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
db.const_eval_variant(self.into())
}
} }
/// Variants inherit visibility from the parent enum. /// Variants inherit visibility from the parent enum.
@ -1129,6 +1147,7 @@ pub enum DefWithBody {
Function(Function), Function(Function),
Static(Static), Static(Static),
Const(Const), Const(Const),
Variant(Variant),
} }
impl_from!(Function, Const, Static for DefWithBody); impl_from!(Function, Const, Static for DefWithBody);
@ -1138,6 +1157,7 @@ impl DefWithBody {
DefWithBody::Const(c) => c.module(db), DefWithBody::Const(c) => c.module(db),
DefWithBody::Function(f) => f.module(db), DefWithBody::Function(f) => f.module(db),
DefWithBody::Static(s) => s.module(db), DefWithBody::Static(s) => s.module(db),
DefWithBody::Variant(v) => v.module(db),
} }
} }
@ -1146,6 +1166,7 @@ impl DefWithBody {
DefWithBody::Function(f) => Some(f.name(db)), DefWithBody::Function(f) => Some(f.name(db)),
DefWithBody::Static(s) => Some(s.name(db)), DefWithBody::Static(s) => Some(s.name(db)),
DefWithBody::Const(c) => c.name(db), DefWithBody::Const(c) => c.name(db),
DefWithBody::Variant(v) => Some(v.name(db)),
} }
} }
@ -1155,6 +1176,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.ret_type(db), DefWithBody::Function(it) => it.ret_type(db),
DefWithBody::Static(it) => it.ty(db), DefWithBody::Static(it) => it.ty(db),
DefWithBody::Const(it) => it.ty(db), DefWithBody::Const(it) => it.ty(db),
DefWithBody::Variant(it) => it.parent.ty(db),
} }
} }
@ -1163,6 +1185,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.id.into(), DefWithBody::Function(it) => it.id.into(),
DefWithBody::Static(it) => it.id.into(), DefWithBody::Static(it) => it.id.into(),
DefWithBody::Const(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(),
DefWithBody::Variant(it) => it.into(),
} }
} }
@ -1379,6 +1402,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.into(), DefWithBody::Function(it) => it.into(),
DefWithBody::Static(it) => it.into(), DefWithBody::Static(it) => it.into(),
DefWithBody::Const(it) => it.into(), DefWithBody::Const(it) => it.into(),
DefWithBody::Variant(it) => it.into(),
}; };
for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) { for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
acc.push(diag.into()) acc.push(diag.into())

View File

@ -244,6 +244,10 @@ impl<'a> SymbolCollector<'a> {
DefWithBodyId::ConstId(id) => Some( DefWithBodyId::ConstId(id) => Some(
id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
), ),
DefWithBodyId::VariantId(id) => Some({
let db = self.db.upcast();
id.parent.lookup(db).source(db).value.name()?.text().into()
}),
} }
} }

View File

@ -239,6 +239,7 @@ impl Definition {
DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()), DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()), DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()), DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
}; };
return match def { return match def {
Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)), Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),

View File

@ -346,7 +346,16 @@ pub(super) fn definition(
Definition::Module(it) => label_and_docs(db, it), Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it), Definition::Function(it) => label_and_docs(db, it),
Definition::Adt(it) => label_and_docs(db, it), Definition::Adt(it) => label_and_docs(db, it),
Definition::Variant(it) => label_and_docs(db, it), Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
Ok(x) => Some(format!("{}", x)),
Err(_) => it.value(db).map(|x| format!("{:?}", x)),
}
} else {
None
}
}),
Definition::Const(it) => label_value_and_docs(db, it, |it| { Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.eval(db); let body = it.eval(db);
match body { match body {

View File

@ -698,6 +698,7 @@ fn hover_enum_variant() {
check( check(
r#" r#"
enum Option<T> { enum Option<T> {
Some(T)
/// The None variant /// The None variant
Non$0e Non$0e
} }
@ -3527,6 +3528,112 @@ impl<const LEN: usize> Foo<LEN$0> {}
); );
} }
#[test]
fn hover_const_eval_variant() {
// show hex for <10
check(
r#"
#[repr(u8)]
enum E {
/// This is a doc
A$0 = 1 << 3,
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 8
```
---
This is a doc
"#]],
);
// show hex for >10
check(
r#"
#[repr(u8)]
enum E {
/// This is a doc
A$0 = (1 << 3) + (1 << 2),
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 12 (0xC)
```
---
This is a doc
"#]],
);
// enums in const eval
check(
r#"
#[repr(u8)]
enum E {
A = 1,
/// This is a doc
B$0 = E::A as u8 + 1,
}
"#,
expect![[r#"
*B*
```rust
test::E
```
```rust
B = 2
```
---
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] #[test]
fn hover_const_eval() { fn hover_const_eval() {
// show hex for <10 // show hex for <10
@ -3820,6 +3927,35 @@ fn foo() {
--- ---
This is a doc
"#]],
);
check(
r#"
enum E {
/// This is a doc
A = 3,
}
fn foo(e: E) {
match e {
E::A$0 => (),
_ => ()
}
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 3
```
---
This is a doc This is a doc
"#]], "#]],
); );