470: Type inference for enum variants r=flodiebold a=marcusklaas

Opened a new PR instead of https://github.com/rust-analyzer/rust-analyzer/pull/461. Totally botched that one.

I think I resolved all the issues mentioned there.

Co-authored-by: Marcus Klaas de Vries <mail@marcusklaas.nl>
This commit is contained in:
bors[bot] 2019-01-10 19:12:40 +00:00
commit dc2a8d5acc
12 changed files with 205 additions and 46 deletions

View file

@ -1,10 +1,15 @@
use std::sync::Arc; use std::sync::Arc;
use ra_db::Cancelable; use ra_db::Cancelable;
use ra_syntax::ast::{self, NameOwner, StructFlavor, AstNode}; use ra_syntax::{
SyntaxNode,
ast::{self, NameOwner, StructFlavor, AstNode}
};
use crate::{ use crate::{
DefId, Name, AsName, Struct, Enum, HirDatabase, DefKind, DefId, DefLoc, Name, AsName, Struct, Enum, EnumVariant,
HirDatabase, DefKind,
SourceItemId,
type_ref::TypeRef, type_ref::TypeRef,
}; };
@ -45,33 +50,37 @@ impl StructData {
} }
} }
impl Enum { fn get_def_id(
pub(crate) fn new(def_id: DefId) -> Self { db: &impl HirDatabase,
Enum { def_id } same_file_loc: &DefLoc,
} node: &SyntaxNode,
expected_kind: DefKind,
) -> DefId {
let file_id = same_file_loc.source_item_id.file_id;
let file_items = db.file_items(file_id);
let item_id = file_items.id_of(file_id, node);
let source_item_id = SourceItemId {
item_id: Some(item_id),
..same_file_loc.source_item_id
};
let loc = DefLoc {
kind: expected_kind,
source_item_id: source_item_id,
..*same_file_loc
};
loc.id(db)
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData { pub struct EnumData {
pub(crate) name: Option<Name>, pub(crate) name: Option<Name>,
pub(crate) variants: Vec<(Name, Arc<VariantData>)>, pub(crate) variants: Vec<(Name, EnumVariant)>,
} }
impl EnumData { impl EnumData {
fn new(enum_def: &ast::EnumDef) -> Self { fn new(enum_def: &ast::EnumDef, variants: Vec<(Name, EnumVariant)>) -> Self {
let name = enum_def.name().map(|n| n.as_name()); let name = enum_def.name().map(|n| n.as_name());
let variants = if let Some(evl) = enum_def.variant_list() {
evl.variants()
.map(|v| {
(
v.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
Arc::new(VariantData::new(v.flavor())),
)
})
.collect()
} else {
Vec::new()
};
EnumData { name, variants } EnumData { name, variants }
} }
@ -83,7 +92,64 @@ impl EnumData {
assert!(def_loc.kind == DefKind::Enum); assert!(def_loc.kind == DefKind::Enum);
let syntax = db.file_item(def_loc.source_item_id); let syntax = db.file_item(def_loc.source_item_id);
let enum_def = ast::EnumDef::cast(&syntax).expect("enum def should point to EnumDef node"); let enum_def = ast::EnumDef::cast(&syntax).expect("enum def should point to EnumDef node");
Ok(Arc::new(EnumData::new(enum_def))) let variants = if let Some(vl) = enum_def.variant_list() {
vl.variants()
.filter_map(|variant_def| {
let name = variant_def.name().map(|n| n.as_name());
name.map(|n| {
let def_id =
get_def_id(db, &def_loc, variant_def.syntax(), DefKind::EnumVariant);
(n, EnumVariant::new(def_id))
})
})
.collect()
} else {
Vec::new()
};
Ok(Arc::new(EnumData::new(enum_def, variants)))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariantData {
pub(crate) name: Option<Name>,
pub(crate) variant_data: Arc<VariantData>,
pub(crate) parent_enum: Enum,
}
impl EnumVariantData {
fn new(variant_def: &ast::EnumVariant, parent_enum: Enum) -> EnumVariantData {
let name = variant_def.name().map(|n| n.as_name());
let variant_data = VariantData::new(variant_def.flavor());
let variant_data = Arc::new(variant_data);
EnumVariantData {
name,
variant_data,
parent_enum,
}
}
pub(crate) fn enum_variant_data_query(
db: &impl HirDatabase,
def_id: DefId,
) -> Cancelable<Arc<EnumVariantData>> {
let def_loc = def_id.loc(db);
assert!(def_loc.kind == DefKind::EnumVariant);
let syntax = db.file_item(def_loc.source_item_id);
let variant_def = ast::EnumVariant::cast(&syntax)
.expect("enum variant def should point to EnumVariant node");
let enum_node = syntax
.parent()
.expect("enum variant should have enum variant list ancestor")
.parent()
.expect("enum variant list should have enum ancestor");
let enum_def_id = get_def_id(db, &def_loc, enum_node, DefKind::Enum);
Ok(Arc::new(EnumVariantData::new(
variant_def,
Enum::new(enum_def_id),
)))
} }
} }

View file

@ -44,6 +44,7 @@ pub enum Def {
Module(Module), Module(Module),
Struct(Struct), Struct(Struct),
Enum(Enum), Enum(Enum),
EnumVariant(EnumVariant),
Function(Function), Function(Function),
Item, Item,
} }
@ -188,6 +189,10 @@ pub struct Enum {
} }
impl Enum { impl Enum {
pub(crate) fn new(def_id: DefId) -> Self {
Enum { def_id }
}
pub fn def_id(&self) -> DefId { pub fn def_id(&self) -> DefId {
self.def_id self.def_id
} }
@ -196,11 +201,38 @@ impl Enum {
Ok(db.enum_data(self.def_id)?.name.clone()) Ok(db.enum_data(self.def_id)?.name.clone())
} }
pub fn variants(&self, db: &impl HirDatabase) -> Cancelable<Vec<(Name, Arc<VariantData>)>> { pub fn variants(&self, db: &impl HirDatabase) -> Cancelable<Vec<(Name, EnumVariant)>> {
Ok(db.enum_data(self.def_id)?.variants.clone()) Ok(db.enum_data(self.def_id)?.variants.clone())
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EnumVariant {
pub(crate) def_id: DefId,
}
impl EnumVariant {
pub(crate) fn new(def_id: DefId) -> Self {
EnumVariant { def_id }
}
pub fn def_id(&self) -> DefId {
self.def_id
}
pub fn parent_enum(&self, db: &impl HirDatabase) -> Cancelable<Enum> {
Ok(db.enum_variant_data(self.def_id)?.parent_enum.clone())
}
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
Ok(db.enum_variant_data(self.def_id)?.name.clone())
}
pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
Ok(db.enum_variant_data(self.def_id)?.variant_data.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Function { pub struct Function {
pub(crate) def_id: DefId, pub(crate) def_id: DefId,

View file

@ -13,6 +13,7 @@ impl Module {
pub(crate) fn new(def_id: DefId) -> Self { pub(crate) fn new(def_id: DefId) -> Self {
crate::code_model_api::Module { def_id } crate::code_model_api::Module { def_id }
} }
pub(crate) fn from_module_id( pub(crate) fn from_module_id(
db: &impl HirDatabase, db: &impl HirDatabase,
source_root_id: SourceRootId, source_root_id: SourceRootId,
@ -85,6 +86,7 @@ impl Module {
let module_id = loc.module_id.crate_root(&module_tree); let module_id = loc.module_id.crate_root(&module_tree);
Module::from_module_id(db, loc.source_root_id, module_id) Module::from_module_id(db, loc.source_root_id, module_id)
} }
/// Finds a child module with the specified name. /// Finds a child module with the specified name.
pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db); let loc = self.def_id.loc(db);
@ -92,12 +94,14 @@ impl Module {
let child_id = ctry!(loc.module_id.child(&module_tree, name)); let child_id = ctry!(loc.module_id.child(&module_tree, name));
Module::from_module_id(db, loc.source_root_id, child_id).map(Some) Module::from_module_id(db, loc.source_root_id, child_id).map(Some)
} }
pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> { pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db); let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?; let module_tree = db.module_tree(loc.source_root_id)?;
let parent_id = ctry!(loc.module_id.parent(&module_tree)); let parent_id = ctry!(loc.module_id.parent(&module_tree));
Module::from_module_id(db, loc.source_root_id, parent_id).map(Some) Module::from_module_id(db, loc.source_root_id, parent_id).map(Some)
} }
/// Returns a `ModuleScope`: a set of items, visible in this module. /// Returns a `ModuleScope`: a set of items, visible in this module.
pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> { pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
let loc = self.def_id.loc(db); let loc = self.def_id.loc(db);
@ -105,6 +109,7 @@ impl Module {
let res = item_map.per_module[&loc.module_id].clone(); let res = item_map.per_module[&loc.module_id].clone();
Ok(res) Ok(res)
} }
pub fn resolve_path_impl( pub fn resolve_path_impl(
&self, &self,
db: &impl HirDatabase, db: &impl HirDatabase,
@ -126,7 +131,7 @@ impl Module {
); );
let segments = &path.segments; let segments = &path.segments;
for name in segments.iter() { for (idx, name) in segments.iter().enumerate() {
let curr = if let Some(r) = curr_per_ns.as_ref().take_types() { let curr = if let Some(r) = curr_per_ns.as_ref().take_types() {
r r
} else { } else {
@ -134,7 +139,25 @@ impl Module {
}; };
let module = match curr.resolve(db)? { let module = match curr.resolve(db)? {
Def::Module(it) => it, Def::Module(it) => it,
// TODO here would be the place to handle enum variants... Def::Enum(e) => {
if segments.len() == idx + 1 {
// enum variant
let matching_variant =
e.variants(db)?.into_iter().find(|(n, _variant)| n == name);
if let Some((_n, variant)) = matching_variant {
return Ok(PerNs::both(variant.def_id(), e.def_id()));
} else {
return Ok(PerNs::none());
}
} else if segments.len() == idx {
// enum
return Ok(PerNs::types(e.def_id()));
} else {
// malformed enum?
return Ok(PerNs::none());
}
}
_ => return Ok(PerNs::none()), _ => return Ok(PerNs::none()),
}; };
let scope = module.scope(db)?; let scope = module.scope(db)?;
@ -146,6 +169,7 @@ impl Module {
} }
Ok(curr_per_ns) Ok(curr_per_ns)
} }
pub fn problems_impl( pub fn problems_impl(
&self, &self,
db: &impl HirDatabase, db: &impl HirDatabase,

View file

@ -12,7 +12,7 @@ use crate::{
module_tree::{ModuleId, ModuleTree}, module_tree::{ModuleId, ModuleTree},
nameres::{ItemMap, InputModuleItems}, nameres::{ItemMap, InputModuleItems},
ty::{InferenceResult, Ty}, ty::{InferenceResult, Ty},
adt::{StructData, EnumData}, adt::{StructData, EnumData, EnumVariantData},
impl_block::ModuleImplBlocks, impl_block::ModuleImplBlocks,
}; };
@ -47,6 +47,11 @@ pub trait HirDatabase: SyntaxDatabase
use fn crate::adt::EnumData::enum_data_query; use fn crate::adt::EnumData::enum_data_query;
} }
fn enum_variant_data(def_id: DefId) -> Cancelable<Arc<EnumVariantData>> {
type EnumVariantDataQuery;
use fn crate::adt::EnumVariantData::enum_variant_data_query;
}
fn infer(def_id: DefId) -> Cancelable<Arc<InferenceResult>> { fn infer(def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
type InferQuery; type InferQuery;
use fn crate::ty::infer; use fn crate::ty::infer;

View file

@ -3,7 +3,7 @@ use ra_syntax::{TreePtr, SyntaxKind, SyntaxNode, SourceFile, AstNode, ast};
use ra_arena::{Arena, RawId, impl_arena_id}; use ra_arena::{Arena, RawId, impl_arena_id};
use crate::{ use crate::{
HirDatabase, PerNs, Def, Function, Struct, Enum, ImplBlock, Crate, HirDatabase, PerNs, Def, Function, Struct, Enum, EnumVariant, ImplBlock, Crate,
module_tree::ModuleId, module_tree::ModuleId,
}; };
@ -145,6 +145,7 @@ pub(crate) enum DefKind {
Function, Function,
Struct, Struct,
Enum, Enum,
EnumVariant,
Item, Item,
StructCtor, StructCtor,
@ -170,10 +171,8 @@ impl DefId {
let struct_def = Struct::new(self); let struct_def = Struct::new(self);
Def::Struct(struct_def) Def::Struct(struct_def)
} }
DefKind::Enum => { DefKind::Enum => Def::Enum(Enum::new(self)),
let enum_def = Enum::new(self); DefKind::EnumVariant => Def::EnumVariant(EnumVariant::new(self)),
Def::Enum(enum_def)
}
DefKind::StructCtor => Def::Item, DefKind::StructCtor => Def::Item,
DefKind::Item => Def::Item, DefKind::Item => Def::Item,
}; };
@ -258,7 +257,9 @@ impl SourceFileItems {
// change parent's id. This means that, say, adding a new function to a // change parent's id. This means that, say, adding a new function to a
// trait does not chage ids of top-level items, which helps caching. // trait does not chage ids of top-level items, which helps caching.
bfs(source_file.syntax(), |it| { bfs(source_file.syntax(), |it| {
if let Some(module_item) = ast::ModuleItem::cast(it) { if let Some(enum_variant) = ast::EnumVariant::cast(it) {
self.alloc(enum_variant.syntax().to_owned());
} else if let Some(module_item) = ast::ModuleItem::cast(it) {
self.alloc(module_item.syntax().to_owned()); self.alloc(module_item.syntax().to_owned());
} else if let Some(macro_call) = ast::MacroCall::cast(it) { } else if let Some(macro_call) = ast::MacroCall::cast(it) {
self.alloc(macro_call.syntax().to_owned()); self.alloc(macro_call.syntax().to_owned());

View file

@ -58,6 +58,6 @@ pub use self::code_model_api::{
Crate, CrateDependency, Crate, CrateDependency,
Def, Def,
Module, ModuleSource, Problem, Module, ModuleSource, Problem,
Struct, Enum, Struct, Enum, EnumVariant,
Function, FnSignature, Function, FnSignature,
}; };

View file

@ -233,6 +233,7 @@ salsa::database_storage! {
fn type_for_field() for db::TypeForFieldQuery; fn type_for_field() for db::TypeForFieldQuery;
fn struct_data() for db::StructDataQuery; fn struct_data() for db::StructDataQuery;
fn enum_data() for db::EnumDataQuery; fn enum_data() for db::EnumDataQuery;
fn enum_variant_data() for db::EnumVariantDataQuery;
fn impls_in_module() for db::ImplsInModuleQuery; fn impls_in_module() for db::ImplsInModuleQuery;
fn body_hir() for db::BodyHirQuery; fn body_hir() for db::BodyHirQuery;
fn body_syntax_mapping() for db::BodySyntaxMappingQuery; fn body_syntax_mapping() for db::BodySyntaxMappingQuery;

View file

@ -30,7 +30,7 @@ use join_to_string::join;
use ra_db::Cancelable; use ra_db::Cancelable;
use crate::{ use crate::{
Def, DefId, Module, Function, Struct, Enum, Path, Name, ImplBlock, Def, DefId, Module, Function, Struct, Enum, EnumVariant, Path, Name, ImplBlock,
FnSignature, FnScopes, FnSignature, FnScopes,
db::HirDatabase, db::HirDatabase,
type_ref::{TypeRef, Mutability}, type_ref::{TypeRef, Mutability},
@ -453,6 +453,12 @@ pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> {
}) })
} }
pub fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> Cancelable<Ty> {
let enum_parent = ev.parent_enum(db)?;
type_for_enum(db, enum_parent)
}
pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
let def = def_id.resolve(db)?; let def = def_id.resolve(db)?;
match def { match def {
@ -463,6 +469,7 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
Def::Function(f) => type_for_fn(db, f), Def::Function(f) => type_for_fn(db, f),
Def::Struct(s) => type_for_struct(db, s), Def::Struct(s) => type_for_struct(db, s),
Def::Enum(e) => type_for_enum(db, e), Def::Enum(e) => type_for_enum(db, e),
Def::EnumVariant(ev) => type_for_enum_variant(db, ev),
Def::Item => { Def::Item => {
log::debug!("trying to get type for item of unknown type {:?}", def_id); log::debug!("trying to get type for item of unknown type {:?}", def_id);
Ok(Ty::Unknown) Ok(Ty::Unknown)
@ -477,12 +484,9 @@ pub(super) fn type_for_field(
) -> Cancelable<Option<Ty>> { ) -> Cancelable<Option<Ty>> {
let def = def_id.resolve(db)?; let def = def_id.resolve(db)?;
let variant_data = match def { let variant_data = match def {
Def::Struct(s) => { Def::Struct(s) => s.variant_data(db)?,
let variant_data = s.variant_data(db)?; Def::EnumVariant(ev) => ev.variant_data(db)?,
variant_data
}
// TODO: unions // TODO: unions
// TODO: enum variants
_ => panic!( _ => panic!(
"trying to get type for field in non-struct/variant {:?}", "trying to get type for field in non-struct/variant {:?}",
def_id def_id
@ -788,6 +792,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let ty = type_for_struct(self.db, s)?; let ty = type_for_struct(self.db, s)?;
(ty, Some(def_id)) (ty, Some(def_id))
} }
Def::EnumVariant(ev) => {
let ty = type_for_enum_variant(self.db, ev)?;
(ty, Some(def_id))
}
_ => (Ty::Unknown, None), _ => (Ty::Unknown, None),
}) })
} }

View file

@ -94,6 +94,22 @@ fn test() {
); );
} }
#[test]
fn infer_enum() {
check_inference(
r#"
enum E {
V1 { field: u32 },
V2
}
fn test() {
E::V1 { field: 1 };
E::V2;
}"#,
"enum.txt",
);
}
#[test] #[test]
fn infer_refs() { fn infer_refs() {
check_inference( check_inference(

View file

@ -0,0 +1,4 @@
[48; 82) '{ E:...:V2; }': ()
[52; 70) 'E::V1 ...d: 1 }': E
[67; 68) '1': u32
[74; 79) 'E::V2': E

View file

@ -21,14 +21,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
.add_to(acc) .add_to(acc)
}); });
} }
hir::Def::Enum(e) => e hir::Def::Enum(e) => {
.variants(ctx.db)? e.variants(ctx.db)?
.into_iter() .into_iter()
.for_each(|(name, _variant)| { .for_each(|(variant_name, _variant)| {
CompletionItem::new(CompletionKind::Reference, name.to_string()) CompletionItem::new(CompletionKind::Reference, variant_name.to_string())
.kind(CompletionItemKind::EnumVariant) .kind(CompletionItemKind::EnumVariant)
.add_to(acc) .add_to(acc)
}), });
}
_ => return Ok(()), _ => return Ok(()),
}; };
Ok(()) Ok(())

View file

@ -122,6 +122,7 @@ salsa::database_storage! {
fn type_for_field() for hir::db::TypeForFieldQuery; fn type_for_field() for hir::db::TypeForFieldQuery;
fn struct_data() for hir::db::StructDataQuery; fn struct_data() for hir::db::StructDataQuery;
fn enum_data() for hir::db::EnumDataQuery; fn enum_data() for hir::db::EnumDataQuery;
fn enum_variant_data() for hir::db::EnumVariantDataQuery;
fn impls_in_module() for hir::db::ImplsInModuleQuery; fn impls_in_module() for hir::db::ImplsInModuleQuery;
fn body_hir() for hir::db::BodyHirQuery; fn body_hir() for hir::db::BodyHirQuery;
fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery; fn body_syntax_mapping() for hir::db::BodySyntaxMappingQuery;