diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-27 10:08:34 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-27 10:08:34 +0000 |
commit | 1d6dcef5c584d0dffdf5386eec993e41daad0210 (patch) | |
tree | 1e67675b7031115b1811863d0f186ad1f98b1fce /crates/ra_hir | |
parent | 700165cf17290561dea511565278b9869ed61625 (diff) | |
parent | bc745a139674f289386f3081458793f756cab5b9 (diff) |
Merge #332
332: Struct types r=matklad a=flodiebold
Infer types for struct fields, and add basic field completions. There's also some code for enums, but I focused on getting structs working.
There's still ways to go before this becomes useful: There's no autoderef (or even reference types) and no inference for `self`, for example.
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/adt.rs | 194 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir/src/function.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 47 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/module.rs | 44 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 154 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres/tests.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/path.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/query_definitions.rs | 29 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 448 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 45 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0001_basics.txt | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0004_struct.txt | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0005_refs.txt | 23 | ||||
-rw-r--r-- | crates/ra_hir/src/type_ref.rs | 110 |
16 files changed, 900 insertions, 247 deletions
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs new file mode 100644 index 000000000..65c461148 --- /dev/null +++ b/crates/ra_hir/src/adt.rs | |||
@@ -0,0 +1,194 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; | ||
4 | |||
5 | use crate::{ | ||
6 | DefId, Cancelable, | ||
7 | db::{HirDatabase}, | ||
8 | type_ref::TypeRef, | ||
9 | }; | ||
10 | |||
11 | pub struct Struct { | ||
12 | def_id: DefId, | ||
13 | } | ||
14 | |||
15 | impl Struct { | ||
16 | pub(crate) fn new(def_id: DefId) -> Self { | ||
17 | Struct { def_id } | ||
18 | } | ||
19 | |||
20 | pub fn def_id(&self) -> DefId { | ||
21 | self.def_id | ||
22 | } | ||
23 | |||
24 | pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> { | ||
25 | Ok(db.struct_data(self.def_id)?.variant_data.clone()) | ||
26 | } | ||
27 | |||
28 | pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> { | ||
29 | Ok(db.struct_data(self.def_id)?) | ||
30 | } | ||
31 | |||
32 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { | ||
33 | Ok(db.struct_data(self.def_id)?.name.clone()) | ||
34 | } | ||
35 | } | ||
36 | |||
37 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
38 | pub struct StructData { | ||
39 | name: Option<SmolStr>, | ||
40 | variant_data: Arc<VariantData>, | ||
41 | } | ||
42 | |||
43 | impl StructData { | ||
44 | pub(crate) fn new(struct_def: ast::StructDef) -> StructData { | ||
45 | let name = struct_def.name().map(|n| n.text()); | ||
46 | let variant_data = VariantData::new(struct_def.flavor()); | ||
47 | let variant_data = Arc::new(variant_data); | ||
48 | StructData { name, variant_data } | ||
49 | } | ||
50 | |||
51 | pub fn name(&self) -> Option<&SmolStr> { | ||
52 | self.name.as_ref() | ||
53 | } | ||
54 | |||
55 | pub fn variant_data(&self) -> &Arc<VariantData> { | ||
56 | &self.variant_data | ||
57 | } | ||
58 | } | ||
59 | |||
60 | pub struct Enum { | ||
61 | def_id: DefId, | ||
62 | } | ||
63 | |||
64 | impl Enum { | ||
65 | pub(crate) fn new(def_id: DefId) -> Self { | ||
66 | Enum { def_id } | ||
67 | } | ||
68 | |||
69 | pub fn def_id(&self) -> DefId { | ||
70 | self.def_id | ||
71 | } | ||
72 | |||
73 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { | ||
74 | Ok(db.enum_data(self.def_id)?.name.clone()) | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
79 | pub struct EnumData { | ||
80 | name: Option<SmolStr>, | ||
81 | variants: Vec<(SmolStr, Arc<VariantData>)>, | ||
82 | } | ||
83 | |||
84 | impl EnumData { | ||
85 | pub(crate) fn new(enum_def: ast::EnumDef) -> Self { | ||
86 | let name = enum_def.name().map(|n| n.text()); | ||
87 | let variants = if let Some(evl) = enum_def.variant_list() { | ||
88 | evl.variants() | ||
89 | .map(|v| { | ||
90 | ( | ||
91 | v.name() | ||
92 | .map(|n| n.text()) | ||
93 | .unwrap_or_else(|| SmolStr::new("[error]")), | ||
94 | Arc::new(VariantData::new(v.flavor())), | ||
95 | ) | ||
96 | }) | ||
97 | .collect() | ||
98 | } else { | ||
99 | Vec::new() | ||
100 | }; | ||
101 | EnumData { name, variants } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /// A single field of an enum variant or struct | ||
106 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
107 | pub struct StructField { | ||
108 | name: SmolStr, | ||
109 | type_ref: TypeRef, | ||
110 | } | ||
111 | |||
112 | impl StructField { | ||
113 | pub fn name(&self) -> SmolStr { | ||
114 | self.name.clone() | ||
115 | } | ||
116 | pub fn type_ref(&self) -> &TypeRef { | ||
117 | &self.type_ref | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /// Fields of an enum variant or struct | ||
122 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
123 | pub enum VariantData { | ||
124 | Struct(Vec<StructField>), | ||
125 | Tuple(Vec<StructField>), | ||
126 | Unit, | ||
127 | } | ||
128 | |||
129 | impl VariantData { | ||
130 | pub fn new(flavor: StructFlavor) -> Self { | ||
131 | match flavor { | ||
132 | StructFlavor::Tuple(fl) => { | ||
133 | let fields = fl | ||
134 | .fields() | ||
135 | .enumerate() | ||
136 | .map(|(i, fd)| StructField { | ||
137 | name: SmolStr::new(i.to_string()), | ||
138 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
139 | }) | ||
140 | .collect(); | ||
141 | VariantData::Tuple(fields) | ||
142 | } | ||
143 | StructFlavor::Named(fl) => { | ||
144 | let fields = fl | ||
145 | .fields() | ||
146 | .map(|fd| StructField { | ||
147 | name: fd | ||
148 | .name() | ||
149 | .map(|n| n.text()) | ||
150 | .unwrap_or_else(|| SmolStr::new("[error]")), | ||
151 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
152 | }) | ||
153 | .collect(); | ||
154 | VariantData::Struct(fields) | ||
155 | } | ||
156 | StructFlavor::Unit => VariantData::Unit, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pub(crate) fn get_field_type_ref(&self, field_name: &str) -> Option<&TypeRef> { | ||
161 | self.fields() | ||
162 | .iter() | ||
163 | .find(|f| f.name == field_name) | ||
164 | .map(|f| &f.type_ref) | ||
165 | } | ||
166 | |||
167 | pub fn fields(&self) -> &[StructField] { | ||
168 | match *self { | ||
169 | VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields, | ||
170 | _ => &[], | ||
171 | } | ||
172 | } | ||
173 | pub fn is_struct(&self) -> bool { | ||
174 | if let VariantData::Struct(..) = *self { | ||
175 | true | ||
176 | } else { | ||
177 | false | ||
178 | } | ||
179 | } | ||
180 | pub fn is_tuple(&self) -> bool { | ||
181 | if let VariantData::Tuple(..) = *self { | ||
182 | true | ||
183 | } else { | ||
184 | false | ||
185 | } | ||
186 | } | ||
187 | pub fn is_unit(&self) -> bool { | ||
188 | if let VariantData::Unit = *self { | ||
189 | true | ||
190 | } else { | ||
191 | false | ||
192 | } | ||
193 | } | ||
194 | } | ||
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index d94f75857..e7f9afa77 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | SmolStr, | ||
4 | SyntaxNode, | 5 | SyntaxNode, |
5 | ast::FnDefNode, | 6 | ast::FnDefNode, |
6 | }; | 7 | }; |
@@ -15,6 +16,7 @@ use crate::{ | |||
15 | module::{ModuleId, ModuleTree, ModuleSource, | 16 | module::{ModuleId, ModuleTree, ModuleSource, |
16 | nameres::{ItemMap, InputModuleItems}}, | 17 | nameres::{ItemMap, InputModuleItems}}, |
17 | ty::{InferenceResult, Ty}, | 18 | ty::{InferenceResult, Ty}, |
19 | adt::{StructData, EnumData}, | ||
18 | }; | 20 | }; |
19 | 21 | ||
20 | salsa::query_group! { | 22 | salsa::query_group! { |
@@ -31,6 +33,16 @@ pub trait HirDatabase: SyntaxDatabase | |||
31 | use fn query_definitions::fn_syntax; | 33 | use fn query_definitions::fn_syntax; |
32 | } | 34 | } |
33 | 35 | ||
36 | fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> { | ||
37 | type StructDataQuery; | ||
38 | use fn query_definitions::struct_data; | ||
39 | } | ||
40 | |||
41 | fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> { | ||
42 | type EnumDataQuery; | ||
43 | use fn query_definitions::enum_data; | ||
44 | } | ||
45 | |||
34 | fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { | 46 | fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { |
35 | type InferQuery; | 47 | type InferQuery; |
36 | use fn query_definitions::infer; | 48 | use fn query_definitions::infer; |
@@ -41,6 +53,11 @@ pub trait HirDatabase: SyntaxDatabase | |||
41 | use fn query_definitions::type_for_def; | 53 | use fn query_definitions::type_for_def; |
42 | } | 54 | } |
43 | 55 | ||
56 | fn type_for_field(def_id: DefId, field: SmolStr) -> Cancelable<Ty> { | ||
57 | type TypeForFieldQuery; | ||
58 | use fn query_definitions::type_for_field; | ||
59 | } | ||
60 | |||
44 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | 61 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { |
45 | type SourceFileItemsQuery; | 62 | type SourceFileItemsQuery; |
46 | use fn query_definitions::file_items; | 63 | use fn query_definitions::file_items; |
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index d36477b48..01f0f3a66 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs | |||
@@ -46,8 +46,7 @@ impl Function { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { | 48 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { |
49 | let loc = self.fn_id.0.loc(db); | 49 | self.fn_id.0.module(db) |
50 | Module::new(db, loc.source_root_id, loc.module_id) | ||
51 | } | 50 | } |
52 | } | 51 | } |
53 | 52 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a0d99a84d..f1cc0ccd0 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -25,6 +25,8 @@ pub mod source_binder; | |||
25 | mod krate; | 25 | mod krate; |
26 | mod module; | 26 | mod module; |
27 | mod function; | 27 | mod function; |
28 | mod adt; | ||
29 | mod type_ref; | ||
28 | mod ty; | 30 | mod ty; |
29 | 31 | ||
30 | use std::ops::Index; | 32 | use std::ops::Index; |
@@ -40,8 +42,10 @@ use crate::{ | |||
40 | pub use self::{ | 42 | pub use self::{ |
41 | path::{Path, PathKind}, | 43 | path::{Path, PathKind}, |
42 | krate::Crate, | 44 | krate::Crate, |
43 | module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, | 45 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, |
44 | function::{Function, FnScopes}, | 46 | function::{Function, FnScopes}, |
47 | adt::{Struct, Enum}, | ||
48 | ty::Ty, | ||
45 | }; | 49 | }; |
46 | 50 | ||
47 | pub use self::function::FnSignatureInfo; | 51 | pub use self::function::FnSignatureInfo; |
@@ -56,7 +60,11 @@ ra_db::impl_numeric_id!(DefId); | |||
56 | pub(crate) enum DefKind { | 60 | pub(crate) enum DefKind { |
57 | Module, | 61 | Module, |
58 | Function, | 62 | Function, |
63 | Struct, | ||
64 | Enum, | ||
59 | Item, | 65 | Item, |
66 | |||
67 | StructCtor, | ||
60 | } | 68 | } |
61 | 69 | ||
62 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 70 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
@@ -68,18 +76,18 @@ pub struct DefLoc { | |||
68 | } | 76 | } |
69 | 77 | ||
70 | impl DefKind { | 78 | impl DefKind { |
71 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> { | 79 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { |
72 | match kind { | 80 | match kind { |
73 | SyntaxKind::FN_DEF => Some(DefKind::Function), | 81 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), |
74 | SyntaxKind::MODULE => Some(DefKind::Module), | 82 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), |
83 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
84 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
75 | // These define items, but don't have their own DefKinds yet: | 85 | // These define items, but don't have their own DefKinds yet: |
76 | SyntaxKind::STRUCT_DEF => Some(DefKind::Item), | 86 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), |
77 | SyntaxKind::ENUM_DEF => Some(DefKind::Item), | 87 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), |
78 | SyntaxKind::TRAIT_DEF => Some(DefKind::Item), | 88 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), |
79 | SyntaxKind::TYPE_DEF => Some(DefKind::Item), | 89 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), |
80 | SyntaxKind::CONST_DEF => Some(DefKind::Item), | 90 | _ => PerNs::none(), |
81 | SyntaxKind::STATIC_DEF => Some(DefKind::Item), | ||
82 | _ => None, | ||
83 | } | 91 | } |
84 | } | 92 | } |
85 | } | 93 | } |
@@ -99,6 +107,8 @@ impl DefLoc { | |||
99 | pub enum Def { | 107 | pub enum Def { |
100 | Module(Module), | 108 | Module(Module), |
101 | Function(Function), | 109 | Function(Function), |
110 | Struct(Struct), | ||
111 | Enum(Enum), | ||
102 | Item, | 112 | Item, |
103 | } | 113 | } |
104 | 114 | ||
@@ -114,10 +124,25 @@ impl DefId { | |||
114 | let function = Function::new(self); | 124 | let function = Function::new(self); |
115 | Def::Function(function) | 125 | Def::Function(function) |
116 | } | 126 | } |
127 | DefKind::Struct => { | ||
128 | let struct_def = Struct::new(self); | ||
129 | Def::Struct(struct_def) | ||
130 | } | ||
131 | DefKind::Enum => { | ||
132 | let enum_def = Enum::new(self); | ||
133 | Def::Enum(enum_def) | ||
134 | } | ||
135 | DefKind::StructCtor => Def::Item, | ||
117 | DefKind::Item => Def::Item, | 136 | DefKind::Item => Def::Item, |
118 | }; | 137 | }; |
119 | Ok(res) | 138 | Ok(res) |
120 | } | 139 | } |
140 | |||
141 | /// For a module, returns that module; for any other def, returns the containing module. | ||
142 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
143 | let loc = self.loc(db); | ||
144 | Module::new(db, loc.source_root_id, loc.module_id) | ||
145 | } | ||
121 | } | 146 | } |
122 | 147 | ||
123 | /// Identifier of item within a specific file. This is stable over reparses, so | 148 | /// Identifier of item within a specific file. This is stable over reparses, so |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index b5a997170..f6882cb77 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -193,6 +193,9 @@ salsa::database_storage! { | |||
193 | fn submodules() for db::SubmodulesQuery; | 193 | fn submodules() for db::SubmodulesQuery; |
194 | fn infer() for db::InferQuery; | 194 | fn infer() for db::InferQuery; |
195 | fn type_for_def() for db::TypeForDefQuery; | 195 | fn type_for_def() for db::TypeForDefQuery; |
196 | fn type_for_field() for db::TypeForFieldQuery; | ||
197 | fn struct_data() for db::StructDataQuery; | ||
198 | fn enum_data() for db::EnumDataQuery; | ||
196 | } | 199 | } |
197 | } | 200 | } |
198 | } | 201 | } |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 891119953..b9d36f01f 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | arena::{Arena, Id}, | 17 | arena::{Arena, Id}, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | pub use self::nameres::{ModuleScope, Resolution}; | 20 | pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs}; |
21 | 21 | ||
22 | /// `Module` is API entry point to get all the information | 22 | /// `Module` is API entry point to get all the information |
23 | /// about a particular module. | 23 | /// about a particular module. |
@@ -115,16 +115,29 @@ impl Module { | |||
115 | Ok(res) | 115 | Ok(res) |
116 | } | 116 | } |
117 | 117 | ||
118 | pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> { | 118 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> { |
119 | let mut curr = match path.kind { | 119 | let mut curr_per_ns = PerNs::types( |
120 | PathKind::Crate => self.crate_root(), | 120 | match path.kind { |
121 | PathKind::Self_ | PathKind::Plain => self.clone(), | 121 | PathKind::Crate => self.crate_root(), |
122 | PathKind::Super => ctry!(self.parent()), | 122 | PathKind::Self_ | PathKind::Plain => self.clone(), |
123 | } | 123 | PathKind::Super => { |
124 | .def_id(db); | 124 | if let Some(p) = self.parent() { |
125 | p | ||
126 | } else { | ||
127 | return Ok(PerNs::none()); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | .def_id(db), | ||
132 | ); | ||
125 | 133 | ||
126 | let segments = path.segments; | 134 | let segments = &path.segments; |
127 | for name in segments.iter() { | 135 | for name in segments.iter() { |
136 | let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) { | ||
137 | r | ||
138 | } else { | ||
139 | return Ok(PerNs::none()); | ||
140 | }; | ||
128 | let module = match curr.loc(db) { | 141 | let module = match curr.loc(db) { |
129 | DefLoc { | 142 | DefLoc { |
130 | kind: DefKind::Module, | 143 | kind: DefKind::Module, |
@@ -132,12 +145,17 @@ impl Module { | |||
132 | module_id, | 145 | module_id, |
133 | .. | 146 | .. |
134 | } => Module::new(db, source_root_id, module_id)?, | 147 | } => Module::new(db, source_root_id, module_id)?, |
135 | _ => return Ok(None), | 148 | // TODO here would be the place to handle enum variants... |
149 | _ => return Ok(PerNs::none()), | ||
136 | }; | 150 | }; |
137 | let scope = module.scope(db)?; | 151 | let scope = module.scope(db)?; |
138 | curr = ctry!(ctry!(scope.get(&name)).def_id); | 152 | curr_per_ns = if let Some(r) = scope.get(&name) { |
153 | r.def_id | ||
154 | } else { | ||
155 | return Ok(PerNs::none()); | ||
156 | }; | ||
139 | } | 157 | } |
140 | Ok(Some(curr)) | 158 | Ok(curr_per_ns) |
141 | } | 159 | } |
142 | 160 | ||
143 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { | 161 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
@@ -145,7 +163,7 @@ impl Module { | |||
145 | } | 163 | } |
146 | } | 164 | } |
147 | 165 | ||
148 | /// Phisically, rust source is organized as a set of files, but logically it is | 166 | /// Physically, rust source is organized as a set of files, but logically it is |
149 | /// organized as a tree of modules. Usually, a single file corresponds to a | 167 | /// organized as a tree of modules. Usually, a single file corresponds to a |
150 | /// single module, but it is not nessary the case. | 168 | /// single module, but it is not nessary the case. |
151 | /// | 169 | /// |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 0b152a406..98cd225dd 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -118,22 +118,96 @@ enum ImportKind { | |||
118 | #[derive(Debug, Clone, PartialEq, Eq)] | 118 | #[derive(Debug, Clone, PartialEq, Eq)] |
119 | pub struct Resolution { | 119 | pub struct Resolution { |
120 | /// None for unresolved | 120 | /// None for unresolved |
121 | pub def_id: Option<DefId>, | 121 | pub def_id: PerNs<DefId>, |
122 | /// ident by whitch this is imported into local scope. | 122 | /// ident by whitch this is imported into local scope. |
123 | pub import: Option<NamedImport>, | 123 | pub import: Option<NamedImport>, |
124 | } | 124 | } |
125 | 125 | ||
126 | // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 126 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
127 | // enum Namespace { | 127 | pub enum Namespace { |
128 | // Types, | 128 | Types, |
129 | // Values, | 129 | Values, |
130 | // } | 130 | } |
131 | |||
132 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
133 | pub struct PerNs<T> { | ||
134 | pub types: Option<T>, | ||
135 | pub values: Option<T>, | ||
136 | } | ||
137 | |||
138 | impl<T> PerNs<T> { | ||
139 | pub fn none() -> PerNs<T> { | ||
140 | PerNs { | ||
141 | types: None, | ||
142 | values: None, | ||
143 | } | ||
144 | } | ||
145 | |||
146 | pub fn values(t: T) -> PerNs<T> { | ||
147 | PerNs { | ||
148 | types: None, | ||
149 | values: Some(t), | ||
150 | } | ||
151 | } | ||
152 | |||
153 | pub fn types(t: T) -> PerNs<T> { | ||
154 | PerNs { | ||
155 | types: Some(t), | ||
156 | values: None, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
161 | PerNs { | ||
162 | types: Some(types), | ||
163 | values: Some(values), | ||
164 | } | ||
165 | } | ||
166 | |||
167 | pub fn is_none(&self) -> bool { | ||
168 | self.types.is_none() && self.values.is_none() | ||
169 | } | ||
170 | |||
171 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
172 | match namespace { | ||
173 | Namespace::Types => self.types, | ||
174 | Namespace::Values => self.values, | ||
175 | } | ||
176 | } | ||
177 | |||
178 | pub fn take_types(self) -> Option<T> { | ||
179 | self.types | ||
180 | } | ||
181 | |||
182 | pub fn take_values(self) -> Option<T> { | ||
183 | self.values | ||
184 | } | ||
131 | 185 | ||
132 | // #[derive(Debug)] | 186 | pub fn get(&self, namespace: Namespace) -> Option<&T> { |
133 | // struct PerNs<T> { | 187 | self.as_ref().take(namespace) |
134 | // types: Option<T>, | 188 | } |
135 | // values: Option<T>, | 189 | |
136 | // } | 190 | pub fn as_ref(&self) -> PerNs<&T> { |
191 | PerNs { | ||
192 | types: self.types.as_ref(), | ||
193 | values: self.values.as_ref(), | ||
194 | } | ||
195 | } | ||
196 | |||
197 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
198 | PerNs { | ||
199 | types: self.types.and_then(&f), | ||
200 | values: self.values.and_then(&f), | ||
201 | } | ||
202 | } | ||
203 | |||
204 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
205 | PerNs { | ||
206 | types: self.types.map(&f), | ||
207 | values: self.values.map(&f), | ||
208 | } | ||
209 | } | ||
210 | } | ||
137 | 211 | ||
138 | impl InputModuleItems { | 212 | impl InputModuleItems { |
139 | pub(crate) fn new<'a>( | 213 | pub(crate) fn new<'a>( |
@@ -254,7 +328,7 @@ where | |||
254 | for dep in krate.dependencies(self.db) { | 328 | for dep in krate.dependencies(self.db) { |
255 | if let Some(module) = dep.krate.root_module(self.db)? { | 329 | if let Some(module) = dep.krate.root_module(self.db)? { |
256 | let def_id = module.def_id(self.db); | 330 | let def_id = module.def_id(self.db); |
257 | self.add_module_item(&mut module_items, dep.name, def_id); | 331 | self.add_module_item(&mut module_items, dep.name, PerNs::types(def_id)); |
258 | } | 332 | } |
259 | } | 333 | } |
260 | }; | 334 | }; |
@@ -265,7 +339,7 @@ where | |||
265 | module_items.items.insert( | 339 | module_items.items.insert( |
266 | name.clone(), | 340 | name.clone(), |
267 | Resolution { | 341 | Resolution { |
268 | def_id: None, | 342 | def_id: PerNs::none(), |
269 | import: Some(import), | 343 | import: Some(import), |
270 | }, | 344 | }, |
271 | ); | 345 | ); |
@@ -277,18 +351,23 @@ where | |||
277 | if item.kind == MODULE { | 351 | if item.kind == MODULE { |
278 | continue; | 352 | continue; |
279 | } | 353 | } |
280 | let def_loc = DefLoc { | 354 | // depending on the item kind, the location can define something in |
281 | kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item), | 355 | // the values namespace, the types namespace, or both |
282 | source_root_id: self.source_root, | 356 | let kind = DefKind::for_syntax_kind(item.kind); |
283 | module_id, | 357 | let def_id = kind.map(|k| { |
284 | source_item_id: SourceItemId { | 358 | let def_loc = DefLoc { |
285 | file_id, | 359 | kind: k, |
286 | item_id: Some(item.id), | 360 | source_root_id: self.source_root, |
287 | }, | 361 | module_id, |
288 | }; | 362 | source_item_id: SourceItemId { |
289 | let def_id = def_loc.id(self.db); | 363 | file_id, |
364 | item_id: Some(item.id), | ||
365 | }, | ||
366 | }; | ||
367 | def_loc.id(self.db) | ||
368 | }); | ||
290 | let resolution = Resolution { | 369 | let resolution = Resolution { |
291 | def_id: Some(def_id), | 370 | def_id, |
292 | import: None, | 371 | import: None, |
293 | }; | 372 | }; |
294 | module_items.items.insert(item.name.clone(), resolution); | 373 | module_items.items.insert(item.name.clone(), resolution); |
@@ -303,16 +382,16 @@ where | |||
303 | source_item_id: module_id.source(&self.module_tree).0, | 382 | source_item_id: module_id.source(&self.module_tree).0, |
304 | }; | 383 | }; |
305 | let def_id = def_loc.id(self.db); | 384 | let def_id = def_loc.id(self.db); |
306 | self.add_module_item(&mut module_items, name, def_id); | 385 | self.add_module_item(&mut module_items, name, PerNs::types(def_id)); |
307 | } | 386 | } |
308 | 387 | ||
309 | self.result.per_module.insert(module_id, module_items); | 388 | self.result.per_module.insert(module_id, module_items); |
310 | Ok(()) | 389 | Ok(()) |
311 | } | 390 | } |
312 | 391 | ||
313 | fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: DefId) { | 392 | fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: PerNs<DefId>) { |
314 | let resolution = Resolution { | 393 | let resolution = Resolution { |
315 | def_id: Some(def_id), | 394 | def_id, |
316 | import: None, | 395 | import: None, |
317 | }; | 396 | }; |
318 | module_items.items.insert(name, resolution); | 397 | module_items.items.insert(name, resolution); |
@@ -347,15 +426,17 @@ where | |||
347 | let is_last = i == import.path.segments.len() - 1; | 426 | let is_last = i == import.path.segments.len() - 1; |
348 | 427 | ||
349 | let def_id = match self.result.per_module[&curr].items.get(name) { | 428 | let def_id = match self.result.per_module[&curr].items.get(name) { |
350 | None => return Ok(()), | 429 | Some(res) if !res.def_id.is_none() => res.def_id, |
351 | Some(res) => match res.def_id { | 430 | _ => return Ok(()), |
352 | Some(it) => it, | ||
353 | None => return Ok(()), | ||
354 | }, | ||
355 | }; | 431 | }; |
356 | 432 | ||
357 | if !is_last { | 433 | if !is_last { |
358 | curr = match def_id.loc(self.db) { | 434 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { |
435 | d | ||
436 | } else { | ||
437 | return Ok(()); | ||
438 | }; | ||
439 | curr = match type_def_id.loc(self.db) { | ||
359 | DefLoc { | 440 | DefLoc { |
360 | kind: DefKind::Module, | 441 | kind: DefKind::Module, |
361 | module_id: target_module_id, | 442 | module_id: target_module_id, |
@@ -370,10 +451,11 @@ where | |||
370 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | 451 | segments: import.path.segments[i + 1..].iter().cloned().collect(), |
371 | kind: PathKind::Crate, | 452 | kind: PathKind::Crate, |
372 | }; | 453 | }; |
373 | if let Some(def_id) = module.resolve_path(self.db, path)? { | 454 | let def_id = module.resolve_path(self.db, &path)?; |
455 | if !def_id.is_none() { | ||
374 | self.update(module_id, |items| { | 456 | self.update(module_id, |items| { |
375 | let res = Resolution { | 457 | let res = Resolution { |
376 | def_id: Some(def_id), | 458 | def_id: def_id, |
377 | import: Some(ptr), | 459 | import: Some(ptr), |
378 | }; | 460 | }; |
379 | items.items.insert(name.clone(), res); | 461 | items.items.insert(name.clone(), res); |
@@ -387,7 +469,7 @@ where | |||
387 | } else { | 469 | } else { |
388 | self.update(module_id, |items| { | 470 | self.update(module_id, |items| { |
389 | let res = Resolution { | 471 | let res = Resolution { |
390 | def_id: Some(def_id), | 472 | def_id: def_id, |
391 | import: Some(ptr), | 473 | import: Some(ptr), |
392 | }; | 474 | }; |
393 | items.items.insert(name.clone(), res); | 475 | items.items.insert(name.clone(), res); |
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index 3e29c3954..03ea5c1d6 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs | |||
@@ -40,7 +40,7 @@ fn item_map_smoke_test() { | |||
40 | ); | 40 | ); |
41 | let name = SmolStr::from("Baz"); | 41 | let name = SmolStr::from("Baz"); |
42 | let resolution = &item_map.per_module[&module_id].items[&name]; | 42 | let resolution = &item_map.per_module[&module_id].items[&name]; |
43 | assert!(resolution.def_id.is_some()); | 43 | assert!(resolution.def_id.take_types().is_some()); |
44 | } | 44 | } |
45 | 45 | ||
46 | #[test] | 46 | #[test] |
@@ -59,7 +59,7 @@ fn test_self() { | |||
59 | ); | 59 | ); |
60 | let name = SmolStr::from("Baz"); | 60 | let name = SmolStr::from("Baz"); |
61 | let resolution = &item_map.per_module[&module_id].items[&name]; | 61 | let resolution = &item_map.per_module[&module_id].items[&name]; |
62 | assert!(resolution.def_id.is_some()); | 62 | assert!(resolution.def_id.take_types().is_some()); |
63 | } | 63 | } |
64 | 64 | ||
65 | #[test] | 65 | #[test] |
@@ -92,7 +92,7 @@ fn item_map_across_crates() { | |||
92 | 92 | ||
93 | let name = SmolStr::from("Baz"); | 93 | let name = SmolStr::from("Baz"); |
94 | let resolution = &item_map.per_module[&module_id].items[&name]; | 94 | let resolution = &item_map.per_module[&module_id].items[&name]; |
95 | assert!(resolution.def_id.is_some()); | 95 | assert!(resolution.def_id.take_types().is_some()); |
96 | } | 96 | } |
97 | 97 | ||
98 | #[test] | 98 | #[test] |
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index e04d00900..0b260072c 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; | 1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; |
2 | 2 | ||
3 | #[derive(Debug, Clone, PartialEq, Eq)] | 3 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
4 | pub struct Path { | 4 | pub struct Path { |
5 | pub kind: PathKind, | 5 | pub kind: PathKind, |
6 | pub segments: Vec<SmolStr>, | 6 | pub segments: Vec<SmolStr>, |
7 | } | 7 | } |
8 | 8 | ||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
10 | pub enum PathKind { | 10 | pub enum PathKind { |
11 | Plain, | 11 | Plain, |
12 | Self_, | 12 | Self_, |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index b654af920..4a7958a12 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -19,7 +19,8 @@ use crate::{ | |||
19 | imp::Submodule, | 19 | imp::Submodule, |
20 | nameres::{InputModuleItems, ItemMap, Resolver}, | 20 | nameres::{InputModuleItems, ItemMap, Resolver}, |
21 | }, | 21 | }, |
22 | ty::{self, InferenceResult, Ty} | 22 | ty::{self, InferenceResult, Ty}, |
23 | adt::{StructData, EnumData}, | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | /// Resolve `FnId` to the corresponding `SyntaxNode` | 26 | /// Resolve `FnId` to the corresponding `SyntaxNode` |
@@ -45,6 +46,32 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T | |||
45 | ty::type_for_def(db, def_id) | 46 | ty::type_for_def(db, def_id) |
46 | } | 47 | } |
47 | 48 | ||
49 | pub(super) fn type_for_field( | ||
50 | db: &impl HirDatabase, | ||
51 | def_id: DefId, | ||
52 | field: SmolStr, | ||
53 | ) -> Cancelable<Ty> { | ||
54 | ty::type_for_field(db, def_id, field) | ||
55 | } | ||
56 | |||
57 | pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { | ||
58 | let def_loc = def_id.loc(db); | ||
59 | assert!(def_loc.kind == DefKind::Struct); | ||
60 | let syntax = db.file_item(def_loc.source_item_id); | ||
61 | let struct_def = | ||
62 | ast::StructDef::cast(syntax.borrowed()).expect("struct def should point to StructDef node"); | ||
63 | Ok(Arc::new(StructData::new(struct_def.borrowed()))) | ||
64 | } | ||
65 | |||
66 | pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> { | ||
67 | let def_loc = def_id.loc(db); | ||
68 | assert!(def_loc.kind == DefKind::Enum); | ||
69 | let syntax = db.file_item(def_loc.source_item_id); | ||
70 | let enum_def = | ||
71 | ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node"); | ||
72 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) | ||
73 | } | ||
74 | |||
48 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 75 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { |
49 | let mut res = SourceFileItems::new(file_id); | 76 | let mut res = SourceFileItems::new(file_id); |
50 | let source_file = db.source_file(file_id); | 77 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c759d4c8b..67b523c2c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -11,13 +11,18 @@ use rustc_hash::{FxHashMap}; | |||
11 | use ra_db::{LocalSyntaxPtr, Cancelable}; | 11 | use ra_db::{LocalSyntaxPtr, Cancelable}; |
12 | use ra_syntax::{ | 12 | use ra_syntax::{ |
13 | SmolStr, | 13 | SmolStr, |
14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner}, | 14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, |
15 | SyntaxNodeRef | 15 | SyntaxNodeRef |
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; | 18 | use crate::{ |
19 | Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, | ||
20 | db::HirDatabase, | ||
21 | adt::VariantData, | ||
22 | type_ref::{TypeRef, Mutability}, | ||
23 | }; | ||
19 | 24 | ||
20 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | 25 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
21 | pub enum Ty { | 26 | pub enum Ty { |
22 | /// The primitive boolean type. Written as `bool`. | 27 | /// The primitive boolean type. Written as `bool`. |
23 | Bool, | 28 | Bool, |
@@ -35,8 +40,15 @@ pub enum Ty { | |||
35 | /// A primitive floating-point type. For example, `f64`. | 40 | /// A primitive floating-point type. For example, `f64`. |
36 | Float(primitive::FloatTy), | 41 | Float(primitive::FloatTy), |
37 | 42 | ||
38 | // Structures, enumerations and unions. | 43 | /// Structures, enumerations and unions. |
39 | // Adt(AdtDef, Substs), | 44 | Adt { |
45 | /// The DefId of the struct/enum. | ||
46 | def_id: DefId, | ||
47 | /// The name, for displaying. | ||
48 | name: SmolStr, | ||
49 | // later we'll need generic substitutions here | ||
50 | }, | ||
51 | |||
40 | /// The pointee of a string slice. Written as `str`. | 52 | /// The pointee of a string slice. Written as `str`. |
41 | Str, | 53 | Str, |
42 | 54 | ||
@@ -45,12 +57,13 @@ pub enum Ty { | |||
45 | /// The pointee of an array slice. Written as `[T]`. | 57 | /// The pointee of an array slice. Written as `[T]`. |
46 | Slice(TyRef), | 58 | Slice(TyRef), |
47 | 59 | ||
48 | // A raw pointer. Written as `*mut T` or `*const T` | 60 | /// A raw pointer. Written as `*mut T` or `*const T` |
49 | // RawPtr(TypeAndMut<'tcx>), | 61 | RawPtr(TyRef, Mutability), |
62 | |||
63 | /// A reference; a pointer with an associated lifetime. Written as | ||
64 | /// `&'a mut T` or `&'a T`. | ||
65 | Ref(TyRef, Mutability), | ||
50 | 66 | ||
51 | // A reference; a pointer with an associated lifetime. Written as | ||
52 | // `&'a mut T` or `&'a T`. | ||
53 | // Ref(Ty<'tcx>, hir::Mutability), | ||
54 | /// A pointer to a function. Written as `fn() -> i32`. | 67 | /// A pointer to a function. Written as `fn() -> i32`. |
55 | /// | 68 | /// |
56 | /// For example the type of `bar` here: | 69 | /// For example the type of `bar` here: |
@@ -107,58 +120,104 @@ pub enum Ty { | |||
107 | 120 | ||
108 | type TyRef = Arc<Ty>; | 121 | type TyRef = Arc<Ty>; |
109 | 122 | ||
110 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | 123 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
111 | pub struct FnSig { | 124 | pub struct FnSig { |
112 | input: Vec<Ty>, | 125 | input: Vec<Ty>, |
113 | output: Ty, | 126 | output: Ty, |
114 | } | 127 | } |
115 | 128 | ||
116 | impl Ty { | 129 | impl Ty { |
117 | pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> { | 130 | pub(crate) fn from_hir( |
118 | use ra_syntax::ast::TypeRef::*; | 131 | db: &impl HirDatabase, |
119 | Ok(match node { | 132 | module: &Module, |
120 | ParenType(_inner) => Ty::Unknown, // TODO | 133 | type_ref: &TypeRef, |
121 | TupleType(_inner) => Ty::Unknown, // TODO | 134 | ) -> Cancelable<Self> { |
122 | NeverType(..) => Ty::Never, | 135 | Ok(match type_ref { |
123 | PathType(inner) => { | 136 | TypeRef::Never => Ty::Never, |
124 | let path = if let Some(p) = inner.path() { | 137 | TypeRef::Tuple(inner) => { |
125 | p | 138 | let inner_tys = inner |
126 | } else { | 139 | .iter() |
127 | return Ok(Ty::Unknown); | 140 | .map(|tr| Ty::from_hir(db, module, tr)) |
141 | .collect::<Cancelable<_>>()?; | ||
142 | Ty::Tuple(inner_tys) | ||
143 | } | ||
144 | TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, | ||
145 | TypeRef::RawPtr(inner, mutability) => { | ||
146 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
147 | Ty::RawPtr(Arc::new(inner_ty), *mutability) | ||
148 | } | ||
149 | TypeRef::Array(_inner) => Ty::Unknown, // TODO | ||
150 | TypeRef::Slice(inner) => { | ||
151 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
152 | Ty::Slice(Arc::new(inner_ty)) | ||
153 | } | ||
154 | TypeRef::Reference(inner, mutability) => { | ||
155 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
156 | Ty::Ref(Arc::new(inner_ty), *mutability) | ||
157 | } | ||
158 | TypeRef::Placeholder => Ty::Unknown, // TODO | ||
159 | TypeRef::Fn(params) => { | ||
160 | let mut inner_tys = params | ||
161 | .iter() | ||
162 | .map(|tr| Ty::from_hir(db, module, tr)) | ||
163 | .collect::<Cancelable<Vec<_>>>()?; | ||
164 | let return_ty = inner_tys | ||
165 | .pop() | ||
166 | .expect("TypeRef::Fn should always have at least return type"); | ||
167 | let sig = FnSig { | ||
168 | input: inner_tys, | ||
169 | output: return_ty, | ||
128 | }; | 170 | }; |
129 | if path.qualifier().is_none() { | 171 | Ty::FnPtr(Arc::new(sig)) |
130 | let name = path | ||
131 | .segment() | ||
132 | .and_then(|s| s.name_ref()) | ||
133 | .map(|n| n.text()) | ||
134 | .unwrap_or(SmolStr::new("")); | ||
135 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
136 | Ty::Int(int_ty) | ||
137 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
138 | Ty::Uint(uint_ty) | ||
139 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
140 | Ty::Float(float_ty) | ||
141 | } else { | ||
142 | // TODO | ||
143 | Ty::Unknown | ||
144 | } | ||
145 | } else { | ||
146 | // TODO | ||
147 | Ty::Unknown | ||
148 | } | ||
149 | } | 172 | } |
150 | PointerType(_inner) => Ty::Unknown, // TODO | 173 | TypeRef::Error => Ty::Unknown, |
151 | ArrayType(_inner) => Ty::Unknown, // TODO | ||
152 | SliceType(_inner) => Ty::Unknown, // TODO | ||
153 | ReferenceType(_inner) => Ty::Unknown, // TODO | ||
154 | PlaceholderType(_inner) => Ty::Unknown, // TODO | ||
155 | FnPointerType(_inner) => Ty::Unknown, // TODO | ||
156 | ForType(_inner) => Ty::Unknown, // TODO | ||
157 | ImplTraitType(_inner) => Ty::Unknown, // TODO | ||
158 | DynTraitType(_inner) => Ty::Unknown, // TODO | ||
159 | }) | 174 | }) |
160 | } | 175 | } |
161 | 176 | ||
177 | pub(crate) fn from_hir_path( | ||
178 | db: &impl HirDatabase, | ||
179 | module: &Module, | ||
180 | path: &Path, | ||
181 | ) -> Cancelable<Self> { | ||
182 | if path.is_ident() { | ||
183 | let name = &path.segments[0]; | ||
184 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
185 | return Ok(Ty::Int(int_ty)); | ||
186 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
187 | return Ok(Ty::Uint(uint_ty)); | ||
188 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
189 | return Ok(Ty::Float(float_ty)); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | // Resolve in module (in type namespace) | ||
194 | let resolved = if let Some(r) = module.resolve_path(db, path)?.take_types() { | ||
195 | r | ||
196 | } else { | ||
197 | return Ok(Ty::Unknown); | ||
198 | }; | ||
199 | let ty = db.type_for_def(resolved)?; | ||
200 | Ok(ty) | ||
201 | } | ||
202 | |||
203 | // TODO: These should not be necessary long-term, since everything will work on HIR | ||
204 | pub(crate) fn from_ast_opt( | ||
205 | db: &impl HirDatabase, | ||
206 | module: &Module, | ||
207 | node: Option<ast::TypeRef>, | ||
208 | ) -> Cancelable<Self> { | ||
209 | node.map(|n| Ty::from_ast(db, module, n)) | ||
210 | .unwrap_or(Ok(Ty::Unknown)) | ||
211 | } | ||
212 | |||
213 | pub(crate) fn from_ast( | ||
214 | db: &impl HirDatabase, | ||
215 | module: &Module, | ||
216 | node: ast::TypeRef, | ||
217 | ) -> Cancelable<Self> { | ||
218 | Ty::from_hir(db, module, &TypeRef::from_ast(node)) | ||
219 | } | ||
220 | |||
162 | pub fn unit() -> Self { | 221 | pub fn unit() -> Self { |
163 | Ty::Tuple(Vec::new()) | 222 | Ty::Tuple(Vec::new()) |
164 | } | 223 | } |
@@ -174,6 +233,8 @@ impl fmt::Display for Ty { | |||
174 | Ty::Float(t) => write!(f, "{}", t.ty_to_string()), | 233 | Ty::Float(t) => write!(f, "{}", t.ty_to_string()), |
175 | Ty::Str => write!(f, "str"), | 234 | Ty::Str => write!(f, "str"), |
176 | Ty::Slice(t) => write!(f, "[{}]", t), | 235 | Ty::Slice(t) => write!(f, "[{}]", t), |
236 | Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t), | ||
237 | Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t), | ||
177 | Ty::Never => write!(f, "!"), | 238 | Ty::Never => write!(f, "!"), |
178 | Ty::Tuple(ts) => { | 239 | Ty::Tuple(ts) => { |
179 | write!(f, "(")?; | 240 | write!(f, "(")?; |
@@ -189,6 +250,7 @@ impl fmt::Display for Ty { | |||
189 | } | 250 | } |
190 | write!(f, ") -> {}", sig.output) | 251 | write!(f, ") -> {}", sig.output) |
191 | } | 252 | } |
253 | Ty::Adt { name, .. } => write!(f, "{}", name), | ||
192 | Ty::Unknown => write!(f, "[unknown]"), | 254 | Ty::Unknown => write!(f, "[unknown]"), |
193 | } | 255 | } |
194 | } | 256 | } |
@@ -196,34 +258,40 @@ impl fmt::Display for Ty { | |||
196 | 258 | ||
197 | pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | 259 | pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { |
198 | let syntax = f.syntax(db); | 260 | let syntax = f.syntax(db); |
261 | let module = f.module(db)?; | ||
199 | let node = syntax.borrowed(); | 262 | let node = syntax.borrowed(); |
200 | // TODO we ignore type parameters for now | 263 | // TODO we ignore type parameters for now |
201 | let input = node | 264 | let input = node |
202 | .param_list() | 265 | .param_list() |
203 | .map(|pl| { | 266 | .map(|pl| { |
204 | pl.params() | 267 | pl.params() |
205 | .map(|p| { | 268 | .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) |
206 | p.type_ref() | ||
207 | .map(|t| Ty::new(db, t)) | ||
208 | .unwrap_or(Ok(Ty::Unknown)) | ||
209 | }) | ||
210 | .collect() | 269 | .collect() |
211 | }) | 270 | }) |
212 | .unwrap_or_else(|| Ok(Vec::new()))?; | 271 | .unwrap_or_else(|| Ok(Vec::new()))?; |
213 | let output = node | 272 | let output = Ty::from_ast_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; |
214 | .ret_type() | ||
215 | .and_then(|rt| rt.type_ref()) | ||
216 | .map(|t| Ty::new(db, t)) | ||
217 | .unwrap_or(Ok(Ty::Unknown))?; | ||
218 | let sig = FnSig { input, output }; | 273 | let sig = FnSig { input, output }; |
219 | Ok(Ty::FnPtr(Arc::new(sig))) | 274 | Ok(Ty::FnPtr(Arc::new(sig))) |
220 | } | 275 | } |
221 | 276 | ||
222 | // TODO this should probably be per namespace (i.e. types vs. values), since for | 277 | pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { |
223 | // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but | 278 | Ok(Ty::Adt { |
224 | // defines the struct type Foo when used in the type namespace. rustc has a | 279 | def_id: s.def_id(), |
225 | // separate DefId for the constructor, but with the current DefId approach, that | 280 | name: s |
226 | // seems complicated. | 281 | .name(db)? |
282 | .unwrap_or_else(|| SmolStr::new("[unnamed struct]")), | ||
283 | }) | ||
284 | } | ||
285 | |||
286 | pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { | ||
287 | Ok(Ty::Adt { | ||
288 | def_id: s.def_id(), | ||
289 | name: s | ||
290 | .name(db)? | ||
291 | .unwrap_or_else(|| SmolStr::new("[unnamed enum]")), | ||
292 | }) | ||
293 | } | ||
294 | |||
227 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | 295 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { |
228 | let def = def_id.resolve(db)?; | 296 | let def = def_id.resolve(db)?; |
229 | match def { | 297 | match def { |
@@ -232,6 +300,8 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | |||
232 | Ok(Ty::Unknown) | 300 | Ok(Ty::Unknown) |
233 | } | 301 | } |
234 | Def::Function(f) => type_for_fn(db, f), | 302 | Def::Function(f) => type_for_fn(db, f), |
303 | Def::Struct(s) => type_for_struct(db, s), | ||
304 | Def::Enum(e) => type_for_enum(db, e), | ||
235 | Def::Item => { | 305 | Def::Item => { |
236 | log::debug!("trying to get type for item of unknown type {:?}", def_id); | 306 | log::debug!("trying to get type for item of unknown type {:?}", def_id); |
237 | Ok(Ty::Unknown) | 307 | Ok(Ty::Unknown) |
@@ -239,6 +309,33 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | |||
239 | } | 309 | } |
240 | } | 310 | } |
241 | 311 | ||
312 | pub(super) fn type_for_field( | ||
313 | db: &impl HirDatabase, | ||
314 | def_id: DefId, | ||
315 | field: SmolStr, | ||
316 | ) -> Cancelable<Ty> { | ||
317 | let def = def_id.resolve(db)?; | ||
318 | let variant_data = match def { | ||
319 | Def::Struct(s) => { | ||
320 | let variant_data = s.variant_data(db)?; | ||
321 | variant_data | ||
322 | } | ||
323 | // TODO: unions | ||
324 | // TODO: enum variants | ||
325 | _ => panic!( | ||
326 | "trying to get type for field in non-struct/variant {:?}", | ||
327 | def_id | ||
328 | ), | ||
329 | }; | ||
330 | let module = def_id.module(db)?; | ||
331 | let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { | ||
332 | tr | ||
333 | } else { | ||
334 | return Ok(Ty::Unknown); | ||
335 | }; | ||
336 | Ty::from_hir(db, &module, &type_ref) | ||
337 | } | ||
338 | |||
242 | #[derive(Clone, PartialEq, Eq, Debug)] | 339 | #[derive(Clone, PartialEq, Eq, Debug)] |
243 | pub struct InferenceResult { | 340 | pub struct InferenceResult { |
244 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | 341 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, |
@@ -305,32 +402,54 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
305 | }; | 402 | }; |
306 | 403 | ||
307 | // resolve in module | 404 | // resolve in module |
308 | let resolved = ctry!(self.module.resolve_path(self.db, path)?); | 405 | let resolved = ctry!(self.module.resolve_path(self.db, &path)?.take_values()); |
309 | let ty = self.db.type_for_def(resolved)?; | 406 | let ty = self.db.type_for_def(resolved)?; |
310 | // TODO we will need to add type variables for type parameters etc. here | 407 | // TODO we will need to add type variables for type parameters etc. here |
311 | Ok(Some(ty)) | 408 | Ok(Some(ty)) |
312 | } | 409 | } |
313 | 410 | ||
411 | fn resolve_variant( | ||
412 | &self, | ||
413 | path: Option<ast::Path>, | ||
414 | ) -> Cancelable<(Ty, Option<Arc<VariantData>>)> { | ||
415 | let path = if let Some(path) = path.and_then(Path::from_ast) { | ||
416 | path | ||
417 | } else { | ||
418 | return Ok((Ty::Unknown, None)); | ||
419 | }; | ||
420 | let def_id = if let Some(def_id) = self.module.resolve_path(self.db, &path)?.take_types() { | ||
421 | def_id | ||
422 | } else { | ||
423 | return Ok((Ty::Unknown, None)); | ||
424 | }; | ||
425 | Ok(match def_id.resolve(self.db)? { | ||
426 | Def::Struct(s) => { | ||
427 | let struct_data = self.db.struct_data(def_id)?; | ||
428 | let ty = type_for_struct(self.db, s)?; | ||
429 | (ty, Some(struct_data.variant_data().clone())) | ||
430 | } | ||
431 | _ => (Ty::Unknown, None), | ||
432 | }) | ||
433 | } | ||
434 | |||
435 | fn infer_expr_opt(&mut self, expr: Option<ast::Expr>) -> Cancelable<Ty> { | ||
436 | if let Some(e) = expr { | ||
437 | self.infer_expr(e) | ||
438 | } else { | ||
439 | Ok(Ty::Unknown) | ||
440 | } | ||
441 | } | ||
442 | |||
314 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { | 443 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { |
315 | let ty = match expr { | 444 | let ty = match expr { |
316 | ast::Expr::IfExpr(e) => { | 445 | ast::Expr::IfExpr(e) => { |
317 | if let Some(condition) = e.condition() { | 446 | if let Some(condition) = e.condition() { |
318 | if let Some(e) = condition.expr() { | 447 | // TODO if no pat, this should be bool |
319 | // TODO if no pat, this should be bool | 448 | self.infer_expr_opt(condition.expr())?; |
320 | self.infer_expr(e)?; | ||
321 | } | ||
322 | // TODO write type for pat | 449 | // TODO write type for pat |
323 | }; | 450 | }; |
324 | let if_ty = if let Some(block) = e.then_branch() { | 451 | let if_ty = self.infer_block_opt(e.then_branch())?; |
325 | self.infer_block(block)? | 452 | let else_ty = self.infer_block_opt(e.else_branch())?; |
326 | } else { | ||
327 | Ty::Unknown | ||
328 | }; | ||
329 | let else_ty = if let Some(block) = e.else_branch() { | ||
330 | self.infer_block(block)? | ||
331 | } else { | ||
332 | Ty::Unknown | ||
333 | }; | ||
334 | if let Some(ty) = self.unify(&if_ty, &else_ty) { | 453 | if let Some(ty) = self.unify(&if_ty, &else_ty) { |
335 | ty | 454 | ty |
336 | } else { | 455 | } else { |
@@ -338,62 +457,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
338 | Ty::Unknown | 457 | Ty::Unknown |
339 | } | 458 | } |
340 | } | 459 | } |
341 | ast::Expr::BlockExpr(e) => { | 460 | ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block())?, |
342 | if let Some(block) = e.block() { | ||
343 | self.infer_block(block)? | ||
344 | } else { | ||
345 | Ty::Unknown | ||
346 | } | ||
347 | } | ||
348 | ast::Expr::LoopExpr(e) => { | 461 | ast::Expr::LoopExpr(e) => { |
349 | if let Some(block) = e.loop_body() { | 462 | self.infer_block_opt(e.loop_body())?; |
350 | self.infer_block(block)?; | ||
351 | }; | ||
352 | // TODO never, or the type of the break param | 463 | // TODO never, or the type of the break param |
353 | Ty::Unknown | 464 | Ty::Unknown |
354 | } | 465 | } |
355 | ast::Expr::WhileExpr(e) => { | 466 | ast::Expr::WhileExpr(e) => { |
356 | if let Some(condition) = e.condition() { | 467 | if let Some(condition) = e.condition() { |
357 | if let Some(e) = condition.expr() { | 468 | // TODO if no pat, this should be bool |
358 | // TODO if no pat, this should be bool | 469 | self.infer_expr_opt(condition.expr())?; |
359 | self.infer_expr(e)?; | ||
360 | } | ||
361 | // TODO write type for pat | 470 | // TODO write type for pat |
362 | }; | 471 | }; |
363 | if let Some(block) = e.loop_body() { | 472 | self.infer_block_opt(e.loop_body())?; |
364 | // TODO | ||
365 | self.infer_block(block)?; | ||
366 | }; | ||
367 | // TODO always unit? | 473 | // TODO always unit? |
368 | Ty::Unknown | 474 | Ty::Unknown |
369 | } | 475 | } |
370 | ast::Expr::ForExpr(e) => { | 476 | ast::Expr::ForExpr(e) => { |
371 | if let Some(expr) = e.iterable() { | 477 | let _iterable_ty = self.infer_expr_opt(e.iterable()); |
372 | self.infer_expr(expr)?; | ||
373 | } | ||
374 | if let Some(_pat) = e.pat() { | 478 | if let Some(_pat) = e.pat() { |
375 | // TODO write type for pat | 479 | // TODO write type for pat |
376 | } | 480 | } |
377 | if let Some(block) = e.loop_body() { | 481 | self.infer_block_opt(e.loop_body())?; |
378 | self.infer_block(block)?; | ||
379 | } | ||
380 | // TODO always unit? | 482 | // TODO always unit? |
381 | Ty::Unknown | 483 | Ty::Unknown |
382 | } | 484 | } |
383 | ast::Expr::LambdaExpr(e) => { | 485 | ast::Expr::LambdaExpr(e) => { |
384 | let _body_ty = if let Some(body) = e.body() { | 486 | let _body_ty = self.infer_expr_opt(e.body())?; |
385 | self.infer_expr(body)? | ||
386 | } else { | ||
387 | Ty::Unknown | ||
388 | }; | ||
389 | Ty::Unknown | 487 | Ty::Unknown |
390 | } | 488 | } |
391 | ast::Expr::CallExpr(e) => { | 489 | ast::Expr::CallExpr(e) => { |
392 | let callee_ty = if let Some(e) = e.expr() { | 490 | let callee_ty = self.infer_expr_opt(e.expr())?; |
393 | self.infer_expr(e)? | ||
394 | } else { | ||
395 | Ty::Unknown | ||
396 | }; | ||
397 | if let Some(arg_list) = e.arg_list() { | 491 | if let Some(arg_list) = e.arg_list() { |
398 | for arg in arg_list.args() { | 492 | for arg in arg_list.args() { |
399 | // TODO unify / expect argument type | 493 | // TODO unify / expect argument type |
@@ -410,11 +504,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
410 | } | 504 | } |
411 | } | 505 | } |
412 | ast::Expr::MethodCallExpr(e) => { | 506 | ast::Expr::MethodCallExpr(e) => { |
413 | let _receiver_ty = if let Some(e) = e.expr() { | 507 | let _receiver_ty = self.infer_expr_opt(e.expr())?; |
414 | self.infer_expr(e)? | ||
415 | } else { | ||
416 | Ty::Unknown | ||
417 | }; | ||
418 | if let Some(arg_list) = e.arg_list() { | 508 | if let Some(arg_list) = e.arg_list() { |
419 | for arg in arg_list.args() { | 509 | for arg in arg_list.args() { |
420 | // TODO unify / expect argument type | 510 | // TODO unify / expect argument type |
@@ -424,20 +514,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
424 | Ty::Unknown | 514 | Ty::Unknown |
425 | } | 515 | } |
426 | ast::Expr::MatchExpr(e) => { | 516 | ast::Expr::MatchExpr(e) => { |
427 | let _ty = if let Some(match_expr) = e.expr() { | 517 | let _ty = self.infer_expr_opt(e.expr())?; |
428 | self.infer_expr(match_expr)? | ||
429 | } else { | ||
430 | Ty::Unknown | ||
431 | }; | ||
432 | if let Some(match_arm_list) = e.match_arm_list() { | 518 | if let Some(match_arm_list) = e.match_arm_list() { |
433 | for arm in match_arm_list.arms() { | 519 | for arm in match_arm_list.arms() { |
434 | // TODO type the bindings in pat | 520 | // TODO type the bindings in pat |
435 | // TODO type the guard | 521 | // TODO type the guard |
436 | let _ty = if let Some(e) = arm.expr() { | 522 | let _ty = self.infer_expr_opt(arm.expr())?; |
437 | self.infer_expr(e)? | ||
438 | } else { | ||
439 | Ty::Unknown | ||
440 | }; | ||
441 | } | 523 | } |
442 | // TODO unify all the match arm types | 524 | // TODO unify all the match arm types |
443 | Ty::Unknown | 525 | Ty::Unknown |
@@ -450,68 +532,78 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
450 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), | 532 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), |
451 | ast::Expr::ContinueExpr(_e) => Ty::Never, | 533 | ast::Expr::ContinueExpr(_e) => Ty::Never, |
452 | ast::Expr::BreakExpr(_e) => Ty::Never, | 534 | ast::Expr::BreakExpr(_e) => Ty::Never, |
453 | ast::Expr::ParenExpr(e) => { | 535 | ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr())?, |
454 | if let Some(e) = e.expr() { | ||
455 | self.infer_expr(e)? | ||
456 | } else { | ||
457 | Ty::Unknown | ||
458 | } | ||
459 | } | ||
460 | ast::Expr::Label(_e) => Ty::Unknown, | 536 | ast::Expr::Label(_e) => Ty::Unknown, |
461 | ast::Expr::ReturnExpr(e) => { | 537 | ast::Expr::ReturnExpr(e) => { |
462 | if let Some(e) = e.expr() { | 538 | self.infer_expr_opt(e.expr())?; |
463 | // TODO unify with return type | ||
464 | self.infer_expr(e)?; | ||
465 | }; | ||
466 | Ty::Never | 539 | Ty::Never |
467 | } | 540 | } |
468 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | 541 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { |
469 | // Can this even occur outside of a match expression? | 542 | // Can this even occur outside of a match expression? |
470 | Ty::Unknown | 543 | Ty::Unknown |
471 | } | 544 | } |
472 | ast::Expr::StructLit(_e) => Ty::Unknown, | 545 | ast::Expr::StructLit(e) => { |
546 | let (ty, _variant_data) = self.resolve_variant(e.path())?; | ||
547 | if let Some(nfl) = e.named_field_list() { | ||
548 | for field in nfl.fields() { | ||
549 | // TODO unify with / expect field type | ||
550 | self.infer_expr_opt(field.expr())?; | ||
551 | } | ||
552 | } | ||
553 | ty | ||
554 | } | ||
473 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | 555 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { |
474 | // Can this even occur outside of a struct literal? | 556 | // Can this even occur outside of a struct literal? |
475 | Ty::Unknown | 557 | Ty::Unknown |
476 | } | 558 | } |
477 | ast::Expr::IndexExpr(_e) => Ty::Unknown, | 559 | ast::Expr::IndexExpr(_e) => Ty::Unknown, |
478 | ast::Expr::FieldExpr(_e) => Ty::Unknown, | 560 | ast::Expr::FieldExpr(e) => { |
479 | ast::Expr::TryExpr(e) => { | 561 | let receiver_ty = self.infer_expr_opt(e.expr())?; |
480 | let _inner_ty = if let Some(e) = e.expr() { | 562 | if let Some(nr) = e.name_ref() { |
481 | self.infer_expr(e)? | 563 | let text = nr.text(); |
564 | match receiver_ty { | ||
565 | Ty::Tuple(fields) => { | ||
566 | let i = text.parse::<usize>().ok(); | ||
567 | i.and_then(|i| fields.get(i).cloned()) | ||
568 | .unwrap_or(Ty::Unknown) | ||
569 | } | ||
570 | Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, text)?, | ||
571 | _ => Ty::Unknown, | ||
572 | } | ||
482 | } else { | 573 | } else { |
483 | Ty::Unknown | 574 | Ty::Unknown |
484 | }; | 575 | } |
576 | } | ||
577 | ast::Expr::TryExpr(e) => { | ||
578 | let _inner_ty = self.infer_expr_opt(e.expr())?; | ||
485 | Ty::Unknown | 579 | Ty::Unknown |
486 | } | 580 | } |
487 | ast::Expr::CastExpr(e) => { | 581 | ast::Expr::CastExpr(e) => { |
488 | let _inner_ty = if let Some(e) = e.expr() { | 582 | let _inner_ty = self.infer_expr_opt(e.expr())?; |
489 | self.infer_expr(e)? | 583 | let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; |
490 | } else { | ||
491 | Ty::Unknown | ||
492 | }; | ||
493 | let cast_ty = e | ||
494 | .type_ref() | ||
495 | .map(|t| Ty::new(self.db, t)) | ||
496 | .unwrap_or(Ok(Ty::Unknown))?; | ||
497 | // TODO do the coercion... | 584 | // TODO do the coercion... |
498 | cast_ty | 585 | cast_ty |
499 | } | 586 | } |
500 | ast::Expr::RefExpr(e) => { | 587 | ast::Expr::RefExpr(e) => { |
501 | let _inner_ty = if let Some(e) = e.expr() { | 588 | let inner_ty = self.infer_expr_opt(e.expr())?; |
502 | self.infer_expr(e)? | 589 | let m = Mutability::from_mutable(e.is_mut()); |
503 | } else { | 590 | // TODO reference coercions etc. |
504 | Ty::Unknown | 591 | Ty::Ref(Arc::new(inner_ty), m) |
505 | }; | ||
506 | Ty::Unknown | ||
507 | } | 592 | } |
508 | ast::Expr::PrefixExpr(e) => { | 593 | ast::Expr::PrefixExpr(e) => { |
509 | let _inner_ty = if let Some(e) = e.expr() { | 594 | let inner_ty = self.infer_expr_opt(e.expr())?; |
510 | self.infer_expr(e)? | 595 | match e.op() { |
511 | } else { | 596 | Some(PrefixOp::Deref) => { |
512 | Ty::Unknown | 597 | match inner_ty { |
513 | }; | 598 | // builtin deref: |
514 | Ty::Unknown | 599 | Ty::Ref(ref_inner, _) => (*ref_inner).clone(), |
600 | Ty::RawPtr(ptr_inner, _) => (*ptr_inner).clone(), | ||
601 | // TODO Deref::deref | ||
602 | _ => Ty::Unknown, | ||
603 | } | ||
604 | } | ||
605 | _ => Ty::Unknown, | ||
606 | } | ||
515 | } | 607 | } |
516 | ast::Expr::RangeExpr(_e) => Ty::Unknown, | 608 | ast::Expr::RangeExpr(_e) => Ty::Unknown, |
517 | ast::Expr::BinExpr(_e) => Ty::Unknown, | 609 | ast::Expr::BinExpr(_e) => Ty::Unknown, |
@@ -521,15 +613,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
521 | Ok(ty) | 613 | Ok(ty) |
522 | } | 614 | } |
523 | 615 | ||
616 | fn infer_block_opt(&mut self, node: Option<ast::Block>) -> Cancelable<Ty> { | ||
617 | if let Some(b) = node { | ||
618 | self.infer_block(b) | ||
619 | } else { | ||
620 | Ok(Ty::Unknown) | ||
621 | } | ||
622 | } | ||
623 | |||
524 | fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { | 624 | fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { |
525 | for stmt in node.statements() { | 625 | for stmt in node.statements() { |
526 | match stmt { | 626 | match stmt { |
527 | ast::Stmt::LetStmt(stmt) => { | 627 | ast::Stmt::LetStmt(stmt) => { |
528 | let decl_ty = if let Some(type_ref) = stmt.type_ref() { | 628 | let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; |
529 | Ty::new(self.db, type_ref)? | ||
530 | } else { | ||
531 | Ty::Unknown | ||
532 | }; | ||
533 | let ty = if let Some(expr) = stmt.initializer() { | 629 | let ty = if let Some(expr) = stmt.initializer() { |
534 | // TODO pass expectation | 630 | // TODO pass expectation |
535 | let expr_ty = self.infer_expr(expr)?; | 631 | let expr_ty = self.infer_expr(expr)?; |
@@ -544,9 +640,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
544 | }; | 640 | }; |
545 | } | 641 | } |
546 | ast::Stmt::ExprStmt(expr_stmt) => { | 642 | ast::Stmt::ExprStmt(expr_stmt) => { |
547 | if let Some(expr) = expr_stmt.expr() { | 643 | self.infer_expr_opt(expr_stmt.expr())?; |
548 | self.infer_expr(expr)?; | ||
549 | } | ||
550 | } | 644 | } |
551 | } | 645 | } |
552 | } | 646 | } |
@@ -576,7 +670,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR | |||
576 | continue; | 670 | continue; |
577 | }; | 671 | }; |
578 | if let Some(type_ref) = param.type_ref() { | 672 | if let Some(type_ref) = param.type_ref() { |
579 | let ty = Ty::new(db, type_ref)?; | 673 | let ty = Ty::from_ast(db, &ctx.module, type_ref)?; |
580 | ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); | 674 | ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); |
581 | } else { | 675 | } else { |
582 | // TODO self param | 676 | // TODO self param |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index b6c02cd80..a76925b58 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -68,6 +68,51 @@ fn test() { | |||
68 | ); | 68 | ); |
69 | } | 69 | } |
70 | 70 | ||
71 | #[test] | ||
72 | fn infer_struct() { | ||
73 | check_inference( | ||
74 | r#" | ||
75 | struct A { | ||
76 | b: B, | ||
77 | c: C, | ||
78 | } | ||
79 | struct B; | ||
80 | struct C(usize); | ||
81 | |||
82 | fn test() { | ||
83 | let c = C(1); | ||
84 | B; | ||
85 | let a: A = A { b: B, c: C(1) }; | ||
86 | a.b; | ||
87 | a.c; | ||
88 | } | ||
89 | "#, | ||
90 | "0004_struct.txt", | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | #[test] | ||
95 | fn infer_refs_and_ptrs() { | ||
96 | check_inference( | ||
97 | r#" | ||
98 | fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | ||
99 | a; | ||
100 | *a; | ||
101 | &a; | ||
102 | &mut a; | ||
103 | b; | ||
104 | *b; | ||
105 | &b; | ||
106 | c; | ||
107 | *c; | ||
108 | d; | ||
109 | *d; | ||
110 | } | ||
111 | "#, | ||
112 | "0005_refs.txt", | ||
113 | ); | ||
114 | } | ||
115 | |||
71 | fn infer(content: &str) -> String { | 116 | fn infer(content: &str) -> String { |
72 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 117 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
73 | let source_file = db.source_file(file_id); | 118 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty/tests/data/0001_basics.txt b/crates/ra_hir/src/ty/tests/data/0001_basics.txt index 0c46f243a..212e92e00 100644 --- a/crates/ra_hir/src/ty/tests/data/0001_basics.txt +++ b/crates/ra_hir/src/ty/tests/data/0001_basics.txt | |||
@@ -1,4 +1,4 @@ | |||
1 | [33; 34) 'd': [unknown] | 1 | [33; 34) 'd': &[unknown] |
2 | [88; 94) '1isize': [unknown] | 2 | [88; 94) '1isize': [unknown] |
3 | [48; 49) 'a': u32 | 3 | [48; 49) 'a': u32 |
4 | [55; 56) 'b': isize | 4 | [55; 56) 'b': isize |
@@ -10,4 +10,4 @@ | |||
10 | [17; 18) 'b': isize | 10 | [17; 18) 'b': isize |
11 | [100; 106) '"test"': [unknown] | 11 | [100; 106) '"test"': [unknown] |
12 | [42; 121) '{ ...f32; }': () | 12 | [42; 121) '{ ...f32; }': () |
13 | [69; 70) 'd': [unknown] | 13 | [69; 70) 'd': &[unknown] |
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt new file mode 100644 index 000000000..cc8f3665b --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt | |||
@@ -0,0 +1,16 @@ | |||
1 | [86; 90) 'C(1)': [unknown] | ||
2 | [121; 122) 'B': [unknown] | ||
3 | [86; 87) 'C': [unknown] | ||
4 | [129; 130) '1': [unknown] | ||
5 | [107; 108) 'a': A | ||
6 | [127; 128) 'C': [unknown] | ||
7 | [139; 142) 'a.b': B | ||
8 | [114; 133) 'A { b:...C(1) }': A | ||
9 | [148; 151) 'a.c': C | ||
10 | [148; 149) 'a': A | ||
11 | [139; 140) 'a': A | ||
12 | [72; 154) '{ ...a.c; }': () | ||
13 | [96; 97) 'B': [unknown] | ||
14 | [88; 89) '1': [unknown] | ||
15 | [82; 83) 'c': [unknown] | ||
16 | [127; 131) 'C(1)': [unknown] | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0005_refs.txt b/crates/ra_hir/src/ty/tests/data/0005_refs.txt new file mode 100644 index 000000000..296e955c1 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0005_refs.txt | |||
@@ -0,0 +1,23 @@ | |||
1 | [115; 117) '&b': &&mut u32 | ||
2 | [88; 94) '&mut a': &mut &u32 | ||
3 | [146; 147) 'd': *mut u32 | ||
4 | [145; 147) '*d': u32 | ||
5 | [65; 66) 'a': &u32 | ||
6 | [46; 47) 'd': *mut u32 | ||
7 | [59; 150) '{ ... *d; }': () | ||
8 | [116; 117) 'b': &mut u32 | ||
9 | [131; 132) 'c': *const u32 | ||
10 | [130; 132) '*c': u32 | ||
11 | [72; 74) '*a': u32 | ||
12 | [107; 109) '*b': u32 | ||
13 | [108; 109) 'b': &mut u32 | ||
14 | [9; 10) 'a': &u32 | ||
15 | [18; 19) 'b': &mut u32 | ||
16 | [93; 94) 'a': &u32 | ||
17 | [100; 101) 'b': &mut u32 | ||
18 | [81; 82) 'a': &u32 | ||
19 | [80; 82) '&a': &&u32 | ||
20 | [73; 74) 'a': &u32 | ||
21 | [123; 124) 'c': *const u32 | ||
22 | [31; 32) 'c': *const u32 | ||
23 | [138; 139) 'd': *mut u32 | ||
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs new file mode 100644 index 000000000..b36bb35d8 --- /dev/null +++ b/crates/ra_hir/src/type_ref.rs | |||
@@ -0,0 +1,110 @@ | |||
1 | //! HIR for references to types. Paths in these are not yet resolved. They can | ||
2 | //! be directly created from an ast::TypeRef, without further queries. | ||
3 | |||
4 | use ra_syntax::ast; | ||
5 | |||
6 | use crate::Path; | ||
7 | |||
8 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||
9 | pub enum Mutability { | ||
10 | Shared, | ||
11 | Mut, | ||
12 | } | ||
13 | |||
14 | impl Mutability { | ||
15 | pub fn from_mutable(mutable: bool) -> Mutability { | ||
16 | if mutable { | ||
17 | Mutability::Mut | ||
18 | } else { | ||
19 | Mutability::Shared | ||
20 | } | ||
21 | } | ||
22 | |||
23 | pub fn as_keyword_for_ref(self) -> &'static str { | ||
24 | match self { | ||
25 | Mutability::Shared => "", | ||
26 | Mutability::Mut => "mut ", | ||
27 | } | ||
28 | } | ||
29 | |||
30 | pub fn as_keyword_for_ptr(self) -> &'static str { | ||
31 | match self { | ||
32 | Mutability::Shared => "const ", | ||
33 | Mutability::Mut => "mut ", | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /// Compare ty::Ty | ||
39 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||
40 | pub enum TypeRef { | ||
41 | Never, | ||
42 | Placeholder, | ||
43 | Tuple(Vec<TypeRef>), | ||
44 | Path(Path), | ||
45 | RawPtr(Box<TypeRef>, Mutability), | ||
46 | Reference(Box<TypeRef>, Mutability), | ||
47 | Array(Box<TypeRef> /*, Expr*/), | ||
48 | Slice(Box<TypeRef>), | ||
49 | /// A fn pointer. Last element of the vector is the return type. | ||
50 | Fn(Vec<TypeRef>), | ||
51 | // For | ||
52 | // ImplTrait, | ||
53 | // DynTrait, | ||
54 | Error, | ||
55 | } | ||
56 | |||
57 | impl TypeRef { | ||
58 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. | ||
59 | pub(crate) fn from_ast(node: ast::TypeRef) -> Self { | ||
60 | use ra_syntax::ast::TypeRef::*; | ||
61 | match node { | ||
62 | ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()), | ||
63 | TupleType(inner) => TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect()), | ||
64 | NeverType(..) => TypeRef::Never, | ||
65 | PathType(inner) => inner | ||
66 | .path() | ||
67 | .and_then(Path::from_ast) | ||
68 | .map(TypeRef::Path) | ||
69 | .unwrap_or(TypeRef::Error), | ||
70 | PointerType(inner) => { | ||
71 | let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); | ||
72 | let mutability = Mutability::from_mutable(inner.is_mut()); | ||
73 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | ||
74 | } | ||
75 | ArrayType(inner) => TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), | ||
76 | SliceType(inner) => TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), | ||
77 | ReferenceType(inner) => { | ||
78 | let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); | ||
79 | let mutability = Mutability::from_mutable(inner.is_mut()); | ||
80 | TypeRef::Reference(Box::new(inner_ty), mutability) | ||
81 | } | ||
82 | PlaceholderType(_inner) => TypeRef::Placeholder, | ||
83 | FnPointerType(inner) => { | ||
84 | let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref())); | ||
85 | let mut params = if let Some(pl) = inner.param_list() { | ||
86 | pl.params() | ||
87 | .map(|p| p.type_ref()) | ||
88 | .map(TypeRef::from_ast_opt) | ||
89 | .collect() | ||
90 | } else { | ||
91 | Vec::new() | ||
92 | }; | ||
93 | params.push(ret_ty); | ||
94 | TypeRef::Fn(params) | ||
95 | } | ||
96 | // for types are close enough for our purposes to the inner type for now... | ||
97 | ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()), | ||
98 | ImplTraitType(_inner) => TypeRef::Error, | ||
99 | DynTraitType(_inner) => TypeRef::Error, | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self { | ||
104 | if let Some(node) = node { | ||
105 | TypeRef::from_ast(node) | ||
106 | } else { | ||
107 | TypeRef::Error | ||
108 | } | ||
109 | } | ||
110 | } | ||