1
1
mirror of https://github.com/oxalica/nil.git synced 2024-11-23 03:57:06 +03:00

Track Attr source info in Ty

This commit is contained in:
oxalica 2022-11-14 20:17:34 +08:00
parent dfffc45c35
commit 3fa46cc956
4 changed files with 67 additions and 37 deletions

View File

@ -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<BuiltinKind> for CompletionItemKind {
}
}
impl TryFrom<NameKind> for CompletionItemKind {
type Error = ();
fn try_from(k: NameKind) -> Result<Self, Self::Error> {
impl From<NameKind> 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 }",

View File

@ -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 {

View File

@ -52,7 +52,7 @@ impl TyKind {
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Attrset(BTreeMap<SmolStr, Ty>);
pub struct Attrset(BTreeMap<SmolStr, (Ty, AttrSource)>);
impl Attrset {
pub fn is_empty(&self) -> bool {
@ -64,11 +64,33 @@ impl Attrset {
}
pub fn get(&self, field: &str) -> Option<Ty> {
self.0.get(field).copied()
Some(self.0.get(field)?.0)
}
pub fn iter(&self) -> impl Iterator<Item = (&SmolStr, Ty)> + '_ {
self.0.iter().map(|(k, &v)| (k, v))
pub fn get_src(&self, field: &str) -> Option<AttrSource> {
Some(self.0.get(field)?.1)
}
pub fn iter(&self) -> impl Iterator<Item = (&SmolStr, Ty, AttrSource)> + '_ {
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);
}
}

View File

@ -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 {