mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Get basic struct pattern type inference working!
This commit is contained in:
parent
ab5deb7811
commit
3340807bd2
3 changed files with 123 additions and 80 deletions
|
@ -331,8 +331,8 @@ impl_arena_id!(PatId);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct FieldPat {
|
pub struct FieldPat {
|
||||||
name: Name,
|
pub(crate) name: Name,
|
||||||
pat: Option<PatId>,
|
pub(crate) pat: Option<PatId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close relative to rustc's hir::PatKind
|
/// Close relative to rustc's hir::PatKind
|
||||||
|
@ -392,7 +392,9 @@ impl Pat {
|
||||||
let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter());
|
let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter());
|
||||||
total_iter.map(|pat| *pat).for_each(f);
|
total_iter.map(|pat| *pat).for_each(f);
|
||||||
}
|
}
|
||||||
Pat::Struct { .. } => {} // TODO
|
Pat::Struct { args, .. } => {
|
||||||
|
args.iter().filter_map(|a| a.pat).for_each(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -814,23 +816,20 @@ impl ExprCollector {
|
||||||
ast::PatKind::PlaceholderPat(_) => Pat::Wild,
|
ast::PatKind::PlaceholderPat(_) => Pat::Wild,
|
||||||
ast::PatKind::StructPat(p) => {
|
ast::PatKind::StructPat(p) => {
|
||||||
let path = p.path().and_then(Path::from_ast);
|
let path = p.path().and_then(Path::from_ast);
|
||||||
|
let fields = p
|
||||||
|
.field_pat_list()
|
||||||
|
.expect("every struct should have a field list")
|
||||||
|
.field_pats()
|
||||||
|
.into_iter()
|
||||||
|
.map(|f| FieldPat {
|
||||||
|
name: Name::new(f.ident),
|
||||||
|
pat: f.pat.as_ref().map(|p| self.collect_pat(p)),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
if let Some(field_list) = p.field_pat_list() {
|
Pat::Struct {
|
||||||
let fields = field_list
|
path: path,
|
||||||
.field_pats()
|
args: fields,
|
||||||
.into_iter()
|
|
||||||
.map(|f| FieldPat {
|
|
||||||
name: Name::new(f.ident),
|
|
||||||
pat: f.pat.as_ref().map(|p| self.collect_pat(p)),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Pat::Struct {
|
|
||||||
path: path,
|
|
||||||
args: fields,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Pat::Missing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
type_ref::{TypeRef, Mutability},
|
type_ref::{TypeRef, Mutability},
|
||||||
name::KnownName,
|
name::KnownName,
|
||||||
expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement},
|
expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The ID of a type variable.
|
/// The ID of a type variable.
|
||||||
|
@ -872,6 +872,90 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec<crate::adt::StructField>)> {
|
||||||
|
let def = path
|
||||||
|
.and_then(|path| self.module.resolve_path(self.db, &path).take_types())
|
||||||
|
.map(|def_id| def_id.resolve(self.db));
|
||||||
|
|
||||||
|
let def = if let Some(def) = def {
|
||||||
|
def
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
match def {
|
||||||
|
Def::Struct(s) => {
|
||||||
|
let fields: Vec<_> = self
|
||||||
|
.db
|
||||||
|
.struct_data(s.def_id())
|
||||||
|
.variant_data
|
||||||
|
.fields()
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
Some((type_for_struct(self.db, s), fields))
|
||||||
|
}
|
||||||
|
Def::EnumVariant(ev) => {
|
||||||
|
let fields: Vec<_> = ev.variant_data(self.db).fields().iter().cloned().collect();
|
||||||
|
Some((type_for_enum_variant(self.db, ev), fields))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_tuple_struct(&mut self, path: Option<&Path>, sub_pats: &[PatId]) -> Ty {
|
||||||
|
let (ty, fields) = if let Some(x) = self.resolve_fields(path) {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
return Ty::Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
// walk subpats
|
||||||
|
if fields.len() != sub_pats.len() {
|
||||||
|
return Ty::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) {
|
||||||
|
let sub_ty = Ty::from_hir(
|
||||||
|
self.db,
|
||||||
|
&self.module,
|
||||||
|
self.impl_block.as_ref(),
|
||||||
|
&field.type_ref,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.infer_pat(sub_pat, &Expectation::has_type(sub_ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_struct(&mut self, path: Option<&Path>, sub_pats: &[FieldPat]) -> Ty {
|
||||||
|
let (ty, fields) = if let Some(x) = self.resolve_fields(path) {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
return Ty::Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
for sub_pat in sub_pats {
|
||||||
|
let tyref = fields
|
||||||
|
.iter()
|
||||||
|
.find(|field| field.name == sub_pat.name)
|
||||||
|
.map(|field| &field.type_ref);
|
||||||
|
|
||||||
|
if let Some(typeref) = tyref {
|
||||||
|
let sub_ty = Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), typeref);
|
||||||
|
|
||||||
|
if let Some(pat) = sub_pat.pat {
|
||||||
|
self.infer_pat(pat, &Expectation::has_type(sub_ty));
|
||||||
|
} else {
|
||||||
|
// TODO: deal with this case: S { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Expectation should probably contain a reference to a Ty instead of
|
// FIXME: Expectation should probably contain a reference to a Ty instead of
|
||||||
// a Ty itself
|
// a Ty itself
|
||||||
fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty {
|
fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty {
|
||||||
|
@ -900,54 +984,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
path: ref p,
|
path: ref p,
|
||||||
args: ref sub_pats,
|
args: ref sub_pats,
|
||||||
},
|
},
|
||||||
_expected,
|
_,
|
||||||
) => {
|
) => self.infer_tuple_struct(p.as_ref(), sub_pats),
|
||||||
let def = p
|
(
|
||||||
.as_ref()
|
&Pat::Struct {
|
||||||
.and_then(|path| self.module.resolve_path(self.db, &path).take_types())
|
path: ref p,
|
||||||
.map(|def_id| def_id.resolve(self.db));
|
args: ref fields,
|
||||||
|
},
|
||||||
if let Some(def) = def {
|
_,
|
||||||
let (ty, fields) = match def {
|
) => self.infer_struct(p.as_ref(), fields),
|
||||||
Def::Struct(s) => {
|
|
||||||
let fields: Vec<_> = self
|
|
||||||
.db
|
|
||||||
.struct_data(s.def_id())
|
|
||||||
.variant_data
|
|
||||||
.fields()
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
(type_for_struct(self.db, s), fields)
|
|
||||||
}
|
|
||||||
Def::EnumVariant(ev) => {
|
|
||||||
let fields: Vec<_> =
|
|
||||||
ev.variant_data(self.db).fields().iter().cloned().collect();
|
|
||||||
(type_for_enum_variant(self.db, ev), fields)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
// walk subpats
|
|
||||||
if fields.len() == sub_pats.len() {
|
|
||||||
for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) {
|
|
||||||
let sub_ty = Ty::from_hir(
|
|
||||||
self.db,
|
|
||||||
&self.module,
|
|
||||||
self.impl_block.as_ref(),
|
|
||||||
&field.type_ref,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.infer_pat(sub_pat, &Expectation::has_type(sub_ty));
|
|
||||||
}
|
|
||||||
|
|
||||||
ty
|
|
||||||
} else {
|
|
||||||
expected.ty.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expected.ty.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(_, ref _expected_ty) => expected.ty.clone(),
|
(_, ref _expected_ty) => expected.ty.clone(),
|
||||||
};
|
};
|
||||||
// use a new type variable if we got Ty::Unknown here
|
// use a new type variable if we got Ty::Unknown here
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
[49; 192) '{ ... }; }': ()
|
[68; 155) '{ ...= e; }': ()
|
||||||
[59; 60) 'e': E
|
[78; 79) 'e': E
|
||||||
[63; 76) 'E::A { x: 3 }': E
|
[82; 95) 'E::A { x: 3 }': E
|
||||||
[73; 74) '3': usize
|
[92; 93) '3': usize
|
||||||
[82; 124) 'if let... }': [unknown]
|
[106; 113) 'S(y, z)': S
|
||||||
[105; 106) 'e': E
|
[108; 109) 'y': u32
|
||||||
[107; 124) '{ ... }': [unknown]
|
[111; 112) 'z': E
|
||||||
[117; 118) 'x': [unknown]
|
[116; 119) 'foo': S
|
||||||
[130; 189) 'match ... }': [unknown]
|
[129; 148) 'E::A {..._var }': E
|
||||||
[136; 137) 'e': E
|
[139; 146) 'new_var': usize
|
||||||
[162; 163) 'x': [unknown]
|
[151; 152) 'e': E
|
||||||
[181; 182) '1': i32
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue