diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 22f5fb99266..484e2d7d7dd 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -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,13 @@ 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]; + // TODO(ole): Handle missing exprs (+1 to the prev) + (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); diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 32ebfda4fd9..4c44840e861 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -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 for DefWithBodyId { + fn from(id: EnumVariantId) -> Self { + DefWithBodyId::VariantId(id) + } +} + impl DefWithBodyId { pub fn as_generic_def_id(self) -> Option { 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(), } } } diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 8aa5973cac5..070f6837133 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -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), } } } diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 6ecb6e6fd17..e934fe1c323 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -11,7 +11,7 @@ path::ModPath, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, type_ref::ConstScalar, - ConstId, DefWithBodyId, + ConstId, DefWithBodyId, EnumVariantId, }; use la_arena::{Arena, Idx}; use stdx::never; @@ -339,6 +339,7 @@ pub fn eval_const( ValueNs::GenericParam(_) => { Err(ConstEvalError::NotSupported("const generic without substitution")) } + ValueNs::EnumVariantId(id) => ctx.db.const_eval_variant(id), // TODO(ole): Assuming this is all that has to happen? _ => Err(ConstEvalError::NotSupported("path that are not const or local")), } } @@ -412,6 +413,14 @@ pub(crate) fn const_eval_recover( Err(ConstEvalError::Loop) } +pub(crate) fn const_eval_recover_variant( + _: &dyn HirDatabase, + _: &[String], + _: &EnumVariantId, +) -> Result { + Err(ConstEvalError::Loop) +} + pub(crate) fn const_eval_query( db: &dyn HirDatabase, const_id: ConstId, @@ -433,6 +442,26 @@ pub(crate) fn const_eval_query( result } +pub(crate) fn const_eval_query_variant( + db: &dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result { + 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, mode: ParamLoweringMode, diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index dd5639f00d2..79c5c01ec65 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -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 { #[salsa::cycle(crate::consteval::const_eval_recover)] fn const_eval(&self, def: ConstId) -> Result; + #[salsa::invoke(crate::consteval::const_eval_query_variant)] + #[salsa::cycle(crate::consteval::const_eval_recover_variant)] + fn const_eval_variant(&self, def: EnumVariantId) -> Result; + #[salsa::invoke(crate::lower::impl_trait_query)] fn impl_trait(&self, def: ImplId) -> Option>; @@ -188,6 +193,13 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc 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) } diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 161b19a739c..431ab949b46 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { 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; diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index e37763e8ea7..63d0f1b01cf 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -67,6 +67,14 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc 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) => { + //let def = AttrDefId::EnumVariantId(v); + //let attrs = db.attrs(def); + //let repr = attrs.by_key("repr").attrs().next().unwrap(); + //let ident = repr.single_ident_value().unwrap().text; + // TODO(ole): Get the real type + ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build() + } } ctx.infer_body(); diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index d2f13e4351c..be5ece9c5c5 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -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); diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 9c7558d1918..f825a72c0f5 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -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()), } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7dd891c86e8..258224a7584 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -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, }; @@ -962,11 +962,16 @@ fn visibility(&self, db: &dyn HirDatabase) -> Visibility { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Variant { - pub(crate) parent: Enum, - pub(crate) id: LocalEnumVariantId, + pub parent: Enum, + pub id: LocalEnumVariantId, } impl Variant { + pub fn value(self, db: &dyn HirDatabase) -> Option { + // TODO(ole): Handle missing exprs (+1 to the prev) + self.source(db)?.value.expr() + } + pub fn module(self, db: &dyn HirDatabase) -> Module { self.parent.module(db) } @@ -1129,6 +1134,7 @@ pub enum DefWithBody { Function(Function), Static(Static), Const(Const), + Variant(Variant), } impl_from!(Function, Const, Static for DefWithBody); @@ -1138,6 +1144,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 +1153,7 @@ pub fn name(self, db: &dyn HirDatabase) -> Option { 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 +1163,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), } } @@ -1379,6 +1388,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { 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()) diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 616a406c727..8432f0e7e01 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -1,6 +1,7 @@ //! File symbol extraction. use base_db::FileRange; +use hir_def::db::DefDatabase; use hir_def::{ item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId, HasModule, ImplId, ItemContainerId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, @@ -244,6 +245,10 @@ fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { DefWithBodyId::ConstId(id) => Some( id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), ), + DefWithBodyId::VariantId(id) => Some({ + let up_db: &dyn DefDatabase = self.db.upcast(); + up_db.lookup_intern_enum(id.parent).source(up_db).value.name()?.text().into() + }), } } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 7deffe8e0f6..20ab474e802 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -236,6 +236,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)), diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index c5c50d88dd2..cd63131e7a7 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -2,7 +2,9 @@ use std::fmt::Display; use either::Either; -use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo}; +use hir::{ + db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo, +}; use ide_db::{ base_db::SourceDatabase, defs::Definition, @@ -346,7 +348,14 @@ 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| { + let hir_db: &dyn HirDatabase = db; + let body = hir_db.const_eval_variant(it.into()); + match body { + Ok(x) => Some(format!("{}", x)), + Err(_) => it.value(db).map(|s| format!("{}", s)), + } + }), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.eval(db); match body { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 4b8b47783d1..f24dec25b6b 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3527,6 +3527,86 @@ impl Foo {} ); } +#[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 + 1, +} +"#, + expect![[r#" + *B* + + ```rust + test::E + ``` + + ```rust + B = 2 + ``` + + --- + + This is a doc + "#]], + ); +} + #[test] fn hover_const_eval() { // show hex for <10 @@ -3820,6 +3900,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 "#]], );