diff options
author | Florian Diebold <[email protected]> | 2018-12-24 20:00:14 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2018-12-25 14:16:42 +0000 |
commit | 6fcd38cc81bdcc9921da767872dfce65ee7d2d27 (patch) | |
tree | 5d5ae0f57cc25d0bf8b0613a5e05de1f773b42ef /crates/ra_hir | |
parent | 4ff161852016c6c15954d6f30bd637834a2b2b68 (diff) |
Infer result of struct literals, and recurse into their child expressions
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/adt.rs | 27 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 119 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0004_struct.txt | 12 |
4 files changed, 117 insertions, 43 deletions
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index a2d228593..ee270ac45 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
1 | use ra_syntax::{SmolStr, ast::{self, NameOwner}}; | 3 | use ra_syntax::{SmolStr, ast::{self, NameOwner}}; |
2 | 4 | ||
3 | use crate::{ | 5 | use crate::{ |
@@ -15,6 +17,14 @@ impl Struct { | |||
15 | Struct { def_id } | 17 | Struct { def_id } |
16 | } | 18 | } |
17 | 19 | ||
20 | pub fn def_id(&self) -> DefId { | ||
21 | self.def_id | ||
22 | } | ||
23 | |||
24 | pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> { | ||
25 | Ok(db.struct_data(self.def_id)?) | ||
26 | } | ||
27 | |||
18 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> { | 28 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> { |
19 | Ok(db.struct_data(self.def_id)?.name.clone()) | 29 | Ok(db.struct_data(self.def_id)?.name.clone()) |
20 | } | 30 | } |
@@ -23,7 +33,7 @@ impl Struct { | |||
23 | #[derive(Debug, Clone, PartialEq, Eq)] | 33 | #[derive(Debug, Clone, PartialEq, Eq)] |
24 | pub struct StructData { | 34 | pub struct StructData { |
25 | name: SmolStr, | 35 | name: SmolStr, |
26 | variant_data: VariantData, | 36 | variant_data: Arc<VariantData>, |
27 | } | 37 | } |
28 | 38 | ||
29 | impl StructData { | 39 | impl StructData { |
@@ -33,8 +43,17 @@ impl StructData { | |||
33 | .map(|n| n.text()) | 43 | .map(|n| n.text()) |
34 | .unwrap_or(SmolStr::new("[error]")); | 44 | .unwrap_or(SmolStr::new("[error]")); |
35 | let variant_data = VariantData::Unit; // TODO implement this | 45 | let variant_data = VariantData::Unit; // TODO implement this |
46 | let variant_data = Arc::new(variant_data); | ||
36 | StructData { name, variant_data } | 47 | StructData { name, variant_data } |
37 | } | 48 | } |
49 | |||
50 | pub fn name(&self) -> &SmolStr { | ||
51 | &self.name | ||
52 | } | ||
53 | |||
54 | pub fn variant_data(&self) -> &Arc<VariantData> { | ||
55 | &self.variant_data | ||
56 | } | ||
38 | } | 57 | } |
39 | 58 | ||
40 | pub struct Enum { | 59 | pub struct Enum { |
@@ -46,6 +65,10 @@ impl Enum { | |||
46 | Enum { def_id } | 65 | Enum { def_id } |
47 | } | 66 | } |
48 | 67 | ||
68 | pub fn def_id(&self) -> DefId { | ||
69 | self.def_id | ||
70 | } | ||
71 | |||
49 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> { | 72 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> { |
50 | Ok(db.enum_data(self.def_id)?.name.clone()) | 73 | Ok(db.enum_data(self.def_id)?.name.clone()) |
51 | } | 74 | } |
@@ -54,7 +77,7 @@ impl Enum { | |||
54 | #[derive(Debug, Clone, PartialEq, Eq)] | 77 | #[derive(Debug, Clone, PartialEq, Eq)] |
55 | pub struct EnumData { | 78 | pub struct EnumData { |
56 | name: SmolStr, | 79 | name: SmolStr, |
57 | variants: Vec<(SmolStr, VariantData)>, | 80 | variants: Vec<(SmolStr, Arc<VariantData>)>, |
58 | } | 81 | } |
59 | 82 | ||
60 | impl EnumData { | 83 | impl EnumData { |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 429292cfc..386af8120 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -16,9 +16,9 @@ use ra_syntax::{ | |||
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | Def, DefId, FnScopes, Module, Function, | 19 | Def, DefId, FnScopes, Module, Function, Struct, Path, |
20 | Path, db::HirDatabase, | 20 | db::HirDatabase, |
21 | module::nameres::Namespace | 21 | adt::VariantData, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 24 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
@@ -125,6 +125,37 @@ pub struct FnSig { | |||
125 | } | 125 | } |
126 | 126 | ||
127 | impl Ty { | 127 | impl Ty { |
128 | pub(crate) fn new_from_ast_path( | ||
129 | db: &impl HirDatabase, | ||
130 | module: &Module, | ||
131 | path: ast::Path, | ||
132 | ) -> Cancelable<Self> { | ||
133 | let path = if let Some(p) = Path::from_ast(path) { | ||
134 | p | ||
135 | } else { | ||
136 | return Ok(Ty::Unknown); | ||
137 | }; | ||
138 | if path.is_ident() { | ||
139 | let name = &path.segments[0]; | ||
140 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
141 | return Ok(Ty::Int(int_ty)); | ||
142 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
143 | return Ok(Ty::Uint(uint_ty)); | ||
144 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
145 | return Ok(Ty::Float(float_ty)); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Resolve in module (in type namespace) | ||
150 | let resolved = if let Some(r) = module.resolve_path(db, path)?.take_types() { | ||
151 | r | ||
152 | } else { | ||
153 | return Ok(Ty::Unknown); | ||
154 | }; | ||
155 | let ty = db.type_for_def(resolved)?; | ||
156 | Ok(ty) | ||
157 | } | ||
158 | |||
128 | pub(crate) fn new( | 159 | pub(crate) fn new( |
129 | db: &impl HirDatabase, | 160 | db: &impl HirDatabase, |
130 | module: &Module, | 161 | module: &Module, |
@@ -136,31 +167,11 @@ impl Ty { | |||
136 | TupleType(_inner) => Ty::Unknown, // TODO | 167 | TupleType(_inner) => Ty::Unknown, // TODO |
137 | NeverType(..) => Ty::Never, | 168 | NeverType(..) => Ty::Never, |
138 | PathType(inner) => { | 169 | PathType(inner) => { |
139 | let path = if let Some(p) = inner.path().and_then(Path::from_ast) { | 170 | if let Some(path) = inner.path() { |
140 | p | 171 | Ty::new_from_ast_path(db, module, path)? |
141 | } else { | 172 | } else { |
142 | return Ok(Ty::Unknown); | 173 | Ty::Unknown |
143 | }; | ||
144 | if path.is_ident() { | ||
145 | let name = &path.segments[0]; | ||
146 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
147 | return Ok(Ty::Int(int_ty)); | ||
148 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
149 | return Ok(Ty::Uint(uint_ty)); | ||
150 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
151 | return Ok(Ty::Float(float_ty)); | ||
152 | } | ||
153 | } | 174 | } |
154 | |||
155 | // Resolve in module (in type namespace) | ||
156 | let resolved = | ||
157 | if let Some(r) = module.resolve_path(db, path)?.take(Namespace::Types) { | ||
158 | r | ||
159 | } else { | ||
160 | return Ok(Ty::Unknown); | ||
161 | }; | ||
162 | let ty = db.type_for_def(resolved)?; | ||
163 | ty | ||
164 | } | 175 | } |
165 | PointerType(_inner) => Ty::Unknown, // TODO | 176 | PointerType(_inner) => Ty::Unknown, // TODO |
166 | ArrayType(_inner) => Ty::Unknown, // TODO | 177 | ArrayType(_inner) => Ty::Unknown, // TODO |
@@ -236,6 +247,13 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | |||
236 | Ok(Ty::FnPtr(Arc::new(sig))) | 247 | Ok(Ty::FnPtr(Arc::new(sig))) |
237 | } | 248 | } |
238 | 249 | ||
250 | pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { | ||
251 | Ok(Ty::Adt { | ||
252 | def_id: s.def_id(), | ||
253 | name: s.name(db)?, | ||
254 | }) | ||
255 | } | ||
256 | |||
239 | // TODO this should probably be per namespace (i.e. types vs. values), since for | 257 | // TODO this should probably be per namespace (i.e. types vs. values), since for |
240 | // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but | 258 | // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but |
241 | // defines the struct type Foo when used in the type namespace. rustc has a | 259 | // defines the struct type Foo when used in the type namespace. rustc has a |
@@ -249,10 +267,7 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | |||
249 | Ok(Ty::Unknown) | 267 | Ok(Ty::Unknown) |
250 | } | 268 | } |
251 | Def::Function(f) => type_for_fn(db, f), | 269 | Def::Function(f) => type_for_fn(db, f), |
252 | Def::Struct(s) => Ok(Ty::Adt { | 270 | Def::Struct(s) => type_for_struct(db, s), |
253 | def_id, | ||
254 | name: s.name(db)?, | ||
255 | }), | ||
256 | Def::Enum(e) => Ok(Ty::Adt { | 271 | Def::Enum(e) => Ok(Ty::Adt { |
257 | def_id, | 272 | def_id, |
258 | name: e.name(db)?, | 273 | name: e.name(db)?, |
@@ -330,15 +345,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
330 | }; | 345 | }; |
331 | 346 | ||
332 | // resolve in module | 347 | // resolve in module |
333 | let resolved = ctry!(self | 348 | let resolved = ctry!(self.module.resolve_path(self.db, path)?.take_values()); |
334 | .module | ||
335 | .resolve_path(self.db, path)? | ||
336 | .take(Namespace::Values)); | ||
337 | let ty = self.db.type_for_def(resolved)?; | 349 | let ty = self.db.type_for_def(resolved)?; |
338 | // TODO we will need to add type variables for type parameters etc. here | 350 | // TODO we will need to add type variables for type parameters etc. here |
339 | Ok(Some(ty)) | 351 | Ok(Some(ty)) |
340 | } | 352 | } |
341 | 353 | ||
354 | fn resolve_variant( | ||
355 | &self, | ||
356 | path: Option<ast::Path>, | ||
357 | ) -> Cancelable<(Ty, Option<Arc<VariantData>>)> { | ||
358 | let path = if let Some(path) = path.and_then(Path::from_ast) { | ||
359 | path | ||
360 | } else { | ||
361 | return Ok((Ty::Unknown, None)); | ||
362 | }; | ||
363 | let def_id = if let Some(def_id) = self.module.resolve_path(self.db, path)?.take_types() { | ||
364 | def_id | ||
365 | } else { | ||
366 | return Ok((Ty::Unknown, None)); | ||
367 | }; | ||
368 | Ok(match def_id.resolve(self.db)? { | ||
369 | Def::Struct(s) => { | ||
370 | let struct_data = self.db.struct_data(def_id)?; | ||
371 | let ty = type_for_struct(self.db, s)?; | ||
372 | (ty, Some(struct_data.variant_data().clone())) | ||
373 | } | ||
374 | _ => (Ty::Unknown, None), | ||
375 | }) | ||
376 | } | ||
377 | |||
342 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { | 378 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { |
343 | let ty = match expr { | 379 | let ty = match expr { |
344 | ast::Expr::IfExpr(e) => { | 380 | ast::Expr::IfExpr(e) => { |
@@ -488,7 +524,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
488 | ast::Expr::Label(_e) => Ty::Unknown, | 524 | ast::Expr::Label(_e) => Ty::Unknown, |
489 | ast::Expr::ReturnExpr(e) => { | 525 | ast::Expr::ReturnExpr(e) => { |
490 | if let Some(e) = e.expr() { | 526 | if let Some(e) = e.expr() { |
491 | // TODO unify with return type | 527 | // TODO unify with / expect return type |
492 | self.infer_expr(e)?; | 528 | self.infer_expr(e)?; |
493 | }; | 529 | }; |
494 | Ty::Never | 530 | Ty::Never |
@@ -497,7 +533,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
497 | // Can this even occur outside of a match expression? | 533 | // Can this even occur outside of a match expression? |
498 | Ty::Unknown | 534 | Ty::Unknown |
499 | } | 535 | } |
500 | ast::Expr::StructLit(_e) => Ty::Unknown, | 536 | ast::Expr::StructLit(e) => { |
537 | let (ty, variant_data) = self.resolve_variant(e.path())?; | ||
538 | if let Some(nfl) = e.named_field_list() { | ||
539 | for field in nfl.fields() { | ||
540 | if let Some(e) = field.expr() { | ||
541 | // TODO unify with / expect field type | ||
542 | self.infer_expr(e)?; | ||
543 | } | ||
544 | } | ||
545 | } | ||
546 | ty | ||
547 | } | ||
501 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | 548 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { |
502 | // Can this even occur outside of a struct literal? | 549 | // Can this even occur outside of a struct literal? |
503 | Ty::Unknown | 550 | Ty::Unknown |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 170eef147..9bb58ec85 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -82,7 +82,7 @@ struct C(usize); | |||
82 | fn test() { | 82 | fn test() { |
83 | let c = C(1); | 83 | let c = C(1); |
84 | B; | 84 | B; |
85 | let a: A = A { b: B, c: C() }; | 85 | let a: A = A { b: B, c: C(1) }; |
86 | a.b; | 86 | a.b; |
87 | a.c; | 87 | a.c; |
88 | } | 88 | } |
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt index a4371c5a5..41357749f 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt | |||
@@ -1,10 +1,14 @@ | |||
1 | [86; 90) 'C(1)': [unknown] | 1 | [86; 90) 'C(1)': [unknown] |
2 | [72; 153) '{ ...a.c; }': () | 2 | [121; 122) 'B': [unknown] |
3 | [86; 87) 'C': [unknown] | 3 | [86; 87) 'C': [unknown] |
4 | [129; 130) '1': [unknown] | ||
4 | [107; 108) 'a': A | 5 | [107; 108) 'a': A |
5 | [114; 132) 'A { b:... C() }': [unknown] | 6 | [127; 128) 'C': [unknown] |
6 | [138; 141) 'a.b': [unknown] | 7 | [139; 142) 'a.b': [unknown] |
7 | [147; 150) 'a.c': [unknown] | 8 | [114; 133) 'A { b:...C(1) }': A |
9 | [148; 151) 'a.c': [unknown] | ||
10 | [72; 154) '{ ...a.c; }': () | ||
8 | [96; 97) 'B': [unknown] | 11 | [96; 97) 'B': [unknown] |
9 | [88; 89) '1': [unknown] | 12 | [88; 89) '1': [unknown] |
10 | [82; 83) 'c': [unknown] | 13 | [82; 83) 'c': [unknown] |
14 | [127; 131) 'C(1)': [unknown] | ||