diff --git a/crates/ide/src/ide/completion.rs b/crates/ide/src/ide/completion.rs index 95fb875..10d7245 100644 --- a/crates/ide/src/ide/completion.rs +++ b/crates/ide/src/ide/completion.rs @@ -1,4 +1,5 @@ use crate::def::{AstPtr, BindingValue, Expr, NameKind}; +use crate::ty::AttrSource; use crate::{FileId, FilePos, TyDatabase}; use builtin::{BuiltinKind, ALL_BUILTINS}; use either::Either::{Left, Right}; @@ -61,14 +62,13 @@ impl From for CompletionItemKind { } } -impl TryFrom for CompletionItemKind { - type Error = (); - fn try_from(k: NameKind) -> Result { +impl From for CompletionItemKind { + fn from(k: NameKind) -> Self { match k { - NameKind::LetIn => Ok(Self::LetBinding), - NameKind::RecAttrset => Ok(Self::Field), - NameKind::Param | NameKind::PatField => Ok(Self::Param), - NameKind::PlainAttrset => Err(()), + NameKind::LetIn => Self::LetBinding, + NameKind::RecAttrset => Self::Field, + NameKind::Param | NameKind::PatField => Self::Param, + NameKind::PlainAttrset => Self::Field, } } } @@ -203,10 +203,7 @@ fn complete_expr( label: text.clone(), source_range, replace: text.clone(), - kind: module[*name] - .kind - .try_into() - .expect("NonRecAttrset names are not definitions"), + kind: module[*name].kind.into(), brief: None, doc: None, }) @@ -342,12 +339,15 @@ fn complete_attrpath( set.iter() // We should not report current incomplete definition. // This is covered by `no_incomplete_field`. - .filter(|(name, _)| **name != current_input) - .map(|(name, ty)| CompletionItem { + .filter(|(name, _, _)| **name != current_input) + .map(|(name, ty, src)| CompletionItem { label: name.clone(), source_range, replace: name.clone(), - kind: CompletionItemKind::Field, + kind: match src { + AttrSource::Unknown => CompletionItemKind::Field, + AttrSource::Name(name) => module[name].kind.into(), + }, brief: Some(infer.display_ty(ty).to_string()), doc: None, }), @@ -506,7 +506,7 @@ mod tests { check( "{ foo }@b: b.f$0", "foo", - expect!["(Field) { foo }@b: b.foo"], + expect!["(Param) { foo }@b: b.foo"], ); } @@ -619,12 +619,12 @@ mod tests { check( "let f = { foo }: foo.bar; in f { f$0 }", "foo", - expect!["(Field) let f = { foo }: foo.bar; in f { foo }"], + expect!["(Param) let f = { foo }: foo.bar; in f { foo }"], ); check( "let f = { foo }: foo.bar; in f { f$0.bar }", "foo", - expect!["(Field) let f = { foo }: foo.bar; in f { foo.bar }"], + expect!["(Param) let f = { foo }: foo.bar; in f { foo.bar }"], ); check( "let f = { foo }: foo.bar; in f { foo.b$0 }", diff --git a/crates/ide/src/ty/fmt.rs b/crates/ide/src/ty/fmt.rs index 93a5061..0e5cd3e 100644 --- a/crates/ide/src/ty/fmt.rs +++ b/crates/ide/src/ty/fmt.rs @@ -78,7 +78,7 @@ impl fmt::Display for TyDisplay<'_> { } else { "{".fmt(f)?; let mut first = true; - for (name, ty) in set.iter().take(MAX_FIELD_CNT) { + for (name, ty, _src) in set.iter().take(MAX_FIELD_CNT) { if first { first = false; } else { diff --git a/crates/ide/src/ty/infer.rs b/crates/ide/src/ty/infer.rs index f146cc3..44edee5 100644 --- a/crates/ide/src/ty/infer.rs +++ b/crates/ide/src/ty/infer.rs @@ -52,7 +52,7 @@ impl TyKind { } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct Attrset(BTreeMap); +pub struct Attrset(BTreeMap); impl Attrset { pub fn is_empty(&self) -> bool { @@ -64,11 +64,33 @@ impl Attrset { } pub fn get(&self, field: &str) -> Option { - self.0.get(field).copied() + Some(self.0.get(field)?.0) } - pub fn iter(&self) -> impl Iterator + '_ { - self.0.iter().map(|(k, &v)| (k, v)) + pub fn get_src(&self, field: &str) -> Option { + Some(self.0.get(field)?.1) + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().map(|(k, (ty, src))| (k, *ty, *src)) + } +} + +/// The source of an Attr. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AttrSource { + /// Unknown source, possibly generated or referenced. + Unknown, + /// Defined by a name. + Name(NameId), + // TODO: Builtins. +} + +impl AttrSource { + fn unify(&mut self, rhs: Self) { + if *self == Self::Unknown { + *self = rhs; + } } } @@ -191,7 +213,8 @@ impl<'db> InferCtx<'db> { self.unify(name_ty, default_ty); } let field_text = self.module[name].text.clone(); - let param_field_ty = self.infer_set_field(param_ty, field_text); + let param_field_ty = + self.infer_set_field(param_ty, field_text, AttrSource::Name(name)); self.unify(param_field_ty, name_ty); } } @@ -298,7 +321,7 @@ impl<'db> InferCtx<'db> { self.unify_kind(attr_ty, TyKind::String); match &self.module[attr] { Expr::Literal(Literal::String(key)) => { - self.infer_set_field(set_ty, key.clone()) + self.infer_set_field(set_ty, key.clone(), AttrSource::Unknown) } _ => { self.unify_kind(set_ty, TyKind::Attrset(Attrset::default())); @@ -348,7 +371,7 @@ impl<'db> InferCtx<'db> { Expr::LetAttrset(bindings) => { let set = self.infer_bindings(bindings); let set_ty = TyKind::Attrset(set).intern(self); - self.infer_set_field(set_ty, "body".into()) + self.infer_set_field(set_ty, "body".into(), AttrSource::Unknown) } } } @@ -366,11 +389,12 @@ impl<'db> InferCtx<'db> { BindingValue::Inherit(e) | BindingValue::Expr(e) => self.infer_expr(e), BindingValue::InheritFrom(from_expr) => { let from_ty = self.ty_for_expr(from_expr); - self.infer_set_field(from_ty, name_text.clone()) + self.infer_set_field(from_ty, name_text.clone(), AttrSource::Name(name)) } }; self.unify(name_ty, value_ty); - fields.insert(name_text, value_ty); + let src = AttrSource::Name(name); + fields.insert(name_text, (value_ty, src)); } for &(k, v) in bindings.dynamics.iter() { @@ -382,17 +406,21 @@ impl<'db> InferCtx<'db> { Attrset(fields) } - fn infer_set_field(&mut self, set_ty: Ty, field: SmolStr) -> Ty { + fn infer_set_field(&mut self, set_ty: Ty, field: SmolStr, src: AttrSource) -> Ty { let next_ty = Ty(self.table.len() as u32); match self.table.get_mut(set_ty.0) { TyKind::Attrset(set) => match set.0.entry(field) { - Entry::Occupied(ent) => return *ent.get(), + Entry::Occupied(mut ent) => { + let (ty, prev_src) = ent.get_mut(); + prev_src.unify(src); + return *ty; + } Entry::Vacant(ent) => { - ent.insert(next_ty); + ent.insert((next_ty, src)); } }, k @ TyKind::Unknown => { - *k = TyKind::Attrset(Attrset([(field, next_ty)].into_iter().collect())); + *k = TyKind::Attrset(Attrset([(field, (next_ty, src))].into_iter().collect())); } TyKind::Bool | TyKind::Int @@ -438,13 +466,15 @@ impl<'db> InferCtx<'db> { self.unify(a2, b2); } (TyKind::Attrset(a), TyKind::Attrset(b)) => { - for (field, ty) in b.0 { + for (field, (ty2, src2)) in b.0 { match a.0.entry(field) { Entry::Vacant(ent) => { - ent.insert(ty); + ent.insert((ty2, src2)); } - Entry::Occupied(ent) => { - self.unify(*ent.get(), ty); + Entry::Occupied(mut ent) => { + let (ty1, src1) = ent.get_mut(); + src1.unify(src2); + self.unify(*ty1, ty2); } } } @@ -533,7 +563,7 @@ impl<'a> TableCompresser<'a> { *b = self.intern(*b); } TyKind::Attrset(set) => { - for ty in set.0.values_mut() { + for (ty, _) in set.0.values_mut() { *ty = self.intern(*ty); } } diff --git a/crates/ide/src/ty/mod.rs b/crates/ide/src/ty/mod.rs index 8c7afc1..cceb4ff 100644 --- a/crates/ide/src/ty/mod.rs +++ b/crates/ide/src/ty/mod.rs @@ -9,7 +9,7 @@ use crate::{DefDatabase, FileId}; use std::sync::Arc; pub use fmt::TyDisplay; -pub use infer::{Attrset, InferenceResult, Ty, TyKind}; +pub use infer::{AttrSource, Attrset, InferenceResult, Ty, TyKind}; #[salsa::query_group(TyDatabaseStorage)] pub trait TyDatabase: DefDatabase {