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:
commit
817a6a8609
@ -27,7 +27,7 @@
|
||||
macro_id_to_def_id,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, Path},
|
||||
src::HasSource,
|
||||
src::{HasChildSource, HasSource},
|
||||
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
|
||||
UnresolvedMacro,
|
||||
};
|
||||
@ -324,6 +324,12 @@ pub(crate) fn body_with_source_map_query(
|
||||
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);
|
||||
|
@ -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},
|
||||
@ -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 };
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -681,6 +690,7 @@ fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -691,6 +701,7 @@ pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -839,6 +839,7 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
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 {
|
||||
@ -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);
|
||||
@ -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")),
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -6,8 +6,9 @@
|
||||
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;
|
||||
|
||||
@ -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>>;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -16,7 +16,7 @@
|
||||
use expect_test::Expect;
|
||||
use hir_def::{
|
||||
body::{Body, BodySourceMap, SyntheticSyntax},
|
||||
db::DefDatabase,
|
||||
db::{DefDatabase, InternDatabase},
|
||||
expr::{ExprId, PatId},
|
||||
item_scope::ItemScope,
|
||||
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);
|
||||
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 {
|
||||
@ -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);
|
||||
|
@ -140,6 +140,7 @@ fn from(def: DefWithBody) -> Self {
|
||||
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
|
||||
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
|
||||
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
|
||||
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,6 +151,7 @@ fn from(def: DefWithBodyId) -> Self {
|
||||
DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()),
|
||||
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
|
||||
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
|
||||
DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,9 +174,7 @@ fn from(def: GenericDef) -> Self {
|
||||
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
|
||||
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
|
||||
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
|
||||
GenericDef::Variant(it) => {
|
||||
GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id })
|
||||
}
|
||||
GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
|
||||
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
|
||||
}
|
||||
}
|
||||
@ -188,9 +188,7 @@ fn from(def: GenericDefId) -> Self {
|
||||
GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
|
||||
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
|
||||
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
|
||||
GenericDefId::EnumVariantId(it) => {
|
||||
GenericDef::Variant(Variant { parent: it.parent.into(), id: it.local_id })
|
||||
}
|
||||
GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
|
||||
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs as _, HasDocComments, HasName},
|
||||
ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
|
||||
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
|
||||
};
|
||||
|
||||
@ -952,6 +952,10 @@ pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
|
||||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||
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 {
|
||||
@ -960,6 +964,12 @@ fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Variant> for DefWithBodyId {
|
||||
fn from(&v: &Variant) -> Self {
|
||||
DefWithBodyId::VariantId(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Variant {
|
||||
pub(crate) parent: Enum,
|
||||
@ -994,6 +1004,14 @@ pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
|
||||
pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
||||
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.
|
||||
@ -1129,6 +1147,7 @@ pub enum DefWithBody {
|
||||
Function(Function),
|
||||
Static(Static),
|
||||
Const(Const),
|
||||
Variant(Variant),
|
||||
}
|
||||
impl_from!(Function, Const, Static for DefWithBody);
|
||||
|
||||
@ -1138,6 +1157,7 @@ pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||
DefWithBody::Const(c) => c.module(db),
|
||||
DefWithBody::Function(f) => f.module(db),
|
||||
DefWithBody::Static(s) => s.module(db),
|
||||
DefWithBody::Variant(v) => v.module(db),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1146,6 +1166,7 @@ pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
|
||||
DefWithBody::Function(f) => Some(f.name(db)),
|
||||
DefWithBody::Static(s) => Some(s.name(db)),
|
||||
DefWithBody::Const(c) => c.name(db),
|
||||
DefWithBody::Variant(v) => Some(v.name(db)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1155,6 +1176,7 @@ pub fn body_type(self, db: &dyn HirDatabase) -> Type {
|
||||
DefWithBody::Function(it) => it.ret_type(db),
|
||||
DefWithBody::Static(it) => it.ty(db),
|
||||
DefWithBody::Const(it) => it.ty(db),
|
||||
DefWithBody::Variant(it) => it.parent.ty(db),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1163,6 +1185,7 @@ fn id(&self) -> DefWithBodyId {
|
||||
DefWithBody::Function(it) => it.id.into(),
|
||||
DefWithBody::Static(it) => it.id.into(),
|
||||
DefWithBody::Const(it) => it.id.into(),
|
||||
DefWithBody::Variant(it) => it.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1379,6 +1402,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||
DefWithBody::Function(it) => it.into(),
|
||||
DefWithBody::Static(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()) {
|
||||
acc.push(diag.into())
|
||||
|
@ -244,6 +244,10 @@ fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
|
||||
DefWithBodyId::ConstId(id) => Some(
|
||||
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()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,6 +239,7 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
|
||||
DefWithBody::Function(f) => f.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::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
|
||||
};
|
||||
return match def {
|
||||
Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
|
||||
|
@ -346,7 +346,16 @@ 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_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| {
|
||||
let body = it.eval(db);
|
||||
match body {
|
||||
|
@ -698,6 +698,7 @@ fn hover_enum_variant() {
|
||||
check(
|
||||
r#"
|
||||
enum Option<T> {
|
||||
Some(T)
|
||||
/// The None variant
|
||||
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]
|
||||
fn hover_const_eval() {
|
||||
// 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
|
||||
"#]],
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user