aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/Cargo.toml3
-rw-r--r--crates/ra_hir/src/db.rs11
-rw-r--r--crates/ra_hir/src/function.rs18
-rw-r--r--crates/ra_hir/src/lib.rs20
-rw-r--r--crates/ra_hir/src/mock.rs13
-rw-r--r--crates/ra_hir/src/module.rs1
-rw-r--r--crates/ra_hir/src/module/nameres.rs4
-rw-r--r--crates/ra_hir/src/query_definitions.rs12
-rw-r--r--crates/ra_hir/src/ty.rs601
-rw-r--r--crates/ra_hir/src/ty/primitive.rs130
-rw-r--r--crates/ra_hir/src/ty/tests.rs134
-rw-r--r--crates/ra_hir/src/ty/tests/data/0001_basics.txt13
-rw-r--r--crates/ra_hir/src/ty/tests/data/0002_let.txt7
-rw-r--r--crates/ra_hir/src/ty/tests/data/0003_paths.txt9
14 files changed, 969 insertions, 7 deletions
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 61650cee9..594176337 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -16,3 +16,6 @@ ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 16ra_editor = { path = "../ra_editor" }
17ra_db = { path = "../ra_db" } 17ra_db = { path = "../ra_db" }
18test_utils = { path = "../test_utils" } 18test_utils = { path = "../test_utils" }
19
20[dev-dependencies]
21flexi_logger = "0.10.0"
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 62cf9ab17..d94f75857 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -14,6 +14,7 @@ use crate::{
14 function::FnId, 14 function::FnId,
15 module::{ModuleId, ModuleTree, ModuleSource, 15 module::{ModuleId, ModuleTree, ModuleSource,
16 nameres::{ItemMap, InputModuleItems}}, 16 nameres::{ItemMap, InputModuleItems}},
17 ty::{InferenceResult, Ty},
17}; 18};
18 19
19salsa::query_group! { 20salsa::query_group! {
@@ -30,6 +31,16 @@ pub trait HirDatabase: SyntaxDatabase
30 use fn query_definitions::fn_syntax; 31 use fn query_definitions::fn_syntax;
31 } 32 }
32 33
34 fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
35 type InferQuery;
36 use fn query_definitions::infer;
37 }
38
39 fn type_for_def(def_id: DefId) -> Cancelable<Ty> {
40 type TypeForDefQuery;
41 use fn query_definitions::type_for_def;
42 }
43
33 fn file_items(file_id: FileId) -> Arc<SourceFileItems> { 44 fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
34 type SourceFileItemsQuery; 45 type SourceFileItemsQuery;
35 use fn query_definitions::file_items; 46 use fn query_definitions::file_items;
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs
index 2925beb16..d36477b48 100644
--- a/crates/ra_hir/src/function.rs
+++ b/crates/ra_hir/src/function.rs
@@ -5,12 +5,13 @@ use std::{
5 sync::Arc, 5 sync::Arc,
6}; 6};
7 7
8use ra_db::Cancelable;
8use ra_syntax::{ 9use ra_syntax::{
9 TextRange, TextUnit, 10 TextRange, TextUnit,
10 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 11 ast::{self, AstNode, DocCommentsOwner, NameOwner},
11}; 12};
12 13
13use crate::{ DefId, HirDatabase }; 14use crate::{ DefId, HirDatabase, ty::InferenceResult, Module };
14 15
15pub use self::scope::FnScopes; 16pub use self::scope::FnScopes;
16 17
@@ -18,7 +19,7 @@ pub use self::scope::FnScopes;
18pub struct FnId(pub(crate) DefId); 19pub struct FnId(pub(crate) DefId);
19 20
20pub struct Function { 21pub struct Function {
21 fn_id: FnId, 22 pub(crate) fn_id: FnId,
22} 23}
23 24
24impl Function { 25impl Function {
@@ -27,6 +28,10 @@ impl Function {
27 Function { fn_id } 28 Function { fn_id }
28 } 29 }
29 30
31 pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
32 db.fn_syntax(self.fn_id)
33 }
34
30 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> { 35 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
31 db.fn_scopes(self.fn_id) 36 db.fn_scopes(self.fn_id)
32 } 37 }
@@ -35,6 +40,15 @@ impl Function {
35 let syntax = db.fn_syntax(self.fn_id); 40 let syntax = db.fn_syntax(self.fn_id);
36 FnSignatureInfo::new(syntax.borrowed()) 41 FnSignatureInfo::new(syntax.borrowed())
37 } 42 }
43
44 pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> {
45 db.infer(self.fn_id)
46 }
47
48 pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
49 let loc = self.fn_id.0.loc(db);
50 Module::new(db, loc.source_root_id, loc.module_id)
51 }
38} 52}
39 53
40#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index f56214b47..a0d99a84d 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -25,10 +25,11 @@ pub mod source_binder;
25mod krate; 25mod krate;
26mod module; 26mod module;
27mod function; 27mod function;
28mod ty;
28 29
29use std::ops::Index; 30use std::ops::Index;
30 31
31use ra_syntax::{SyntaxNodeRef, SyntaxNode}; 32use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
32use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; 33use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
33 34
34use crate::{ 35use crate::{
@@ -66,6 +67,23 @@ pub struct DefLoc {
66 source_item_id: SourceItemId, 67 source_item_id: SourceItemId,
67} 68}
68 69
70impl DefKind {
71 pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> {
72 match kind {
73 SyntaxKind::FN_DEF => Some(DefKind::Function),
74 SyntaxKind::MODULE => Some(DefKind::Module),
75 // These define items, but don't have their own DefKinds yet:
76 SyntaxKind::STRUCT_DEF => Some(DefKind::Item),
77 SyntaxKind::ENUM_DEF => Some(DefKind::Item),
78 SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
79 SyntaxKind::TYPE_DEF => Some(DefKind::Item),
80 SyntaxKind::CONST_DEF => Some(DefKind::Item),
81 SyntaxKind::STATIC_DEF => Some(DefKind::Item),
82 _ => None,
83 }
84 }
85}
86
69impl DefId { 87impl DefId {
70 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { 88 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
71 db.as_ref().id2loc(self) 89 db.as_ref().id2loc(self)
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index 9423e6571..b5a997170 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -8,7 +8,7 @@ use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8 8
9use crate::{db, DefId, DefLoc}; 9use crate::{db, DefId, DefLoc};
10 10
11const WORKSPACE: SourceRootId = SourceRootId(0); 11pub const WORKSPACE: SourceRootId = SourceRootId(0);
12 12
13#[derive(Debug)] 13#[derive(Debug)]
14pub(crate) struct MockDatabase { 14pub(crate) struct MockDatabase {
@@ -24,6 +24,15 @@ impl MockDatabase {
24 (db, source_root) 24 (db, source_root)
25 } 25 }
26 26
27 pub(crate) fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) {
28 let mut db = MockDatabase::default();
29 let mut source_root = SourceRoot::default();
30 let file_id = db.add_file(&mut source_root, "/main.rs", text);
31 db.query_mut(ra_db::SourceRootQuery)
32 .set(WORKSPACE, Arc::new(source_root.clone()));
33 (db, source_root, file_id)
34 }
35
27 pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { 36 pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
28 let (db, _, position) = MockDatabase::from_fixture(fixture); 37 let (db, _, position) = MockDatabase::from_fixture(fixture);
29 let position = position.expect("expected a marker ( <|> )"); 38 let position = position.expect("expected a marker ( <|> )");
@@ -182,6 +191,8 @@ salsa::database_storage! {
182 fn item_map() for db::ItemMapQuery; 191 fn item_map() for db::ItemMapQuery;
183 fn fn_syntax() for db::FnSyntaxQuery; 192 fn fn_syntax() for db::FnSyntaxQuery;
184 fn submodules() for db::SubmodulesQuery; 193 fn submodules() for db::SubmodulesQuery;
194 fn infer() for db::InferQuery;
195 fn type_for_def() for db::TypeForDefQuery;
185 } 196 }
186 } 197 }
187} 198}
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index cd31e8cfe..891119953 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -2,6 +2,7 @@ pub(super) mod imp;
2pub(super) mod nameres; 2pub(super) mod nameres;
3 3
4use std::sync::Arc; 4use std::sync::Arc;
5use log;
5 6
6use ra_syntax::{ 7use ra_syntax::{
7 algo::generate, 8 algo::generate,
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 39e891cda..0b152a406 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -272,13 +272,13 @@ where
272 } 272 }
273 } 273 }
274 } 274 }
275 // Populate explicitelly declared items, except modules 275 // Populate explicitly declared items, except modules
276 for item in input.items.iter() { 276 for item in input.items.iter() {
277 if item.kind == MODULE { 277 if item.kind == MODULE {
278 continue; 278 continue;
279 } 279 }
280 let def_loc = DefLoc { 280 let def_loc = DefLoc {
281 kind: DefKind::Item, 281 kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item),
282 source_root_id: self.source_root, 282 source_root_id: self.source_root,
283 module_id, 283 module_id,
284 source_item_id: SourceItemId { 284 source_item_id: SourceItemId {
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index efaeb1525..b654af920 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use ra_db::{SourceRootId, FileId, Cancelable,}; 11use ra_db::{SourceRootId, FileId, Cancelable,};
12 12
13use crate::{ 13use crate::{
14 SourceFileItems, SourceItemId, DefKind, 14 SourceFileItems, SourceItemId, DefKind, Function, DefId,
15 db::HirDatabase, 15 db::HirDatabase,
16 function::{FnScopes, FnId}, 16 function::{FnScopes, FnId},
17 module::{ 17 module::{
@@ -19,6 +19,7 @@ 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}; 23};
23 24
24/// Resolve `FnId` to the corresponding `SyntaxNode` 25/// Resolve `FnId` to the corresponding `SyntaxNode`
@@ -35,6 +36,15 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
35 Arc::new(res) 36 Arc::new(res)
36} 37}
37 38
39pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
40 let function = Function { fn_id };
41 ty::infer(db, function).map(Arc::new)
42}
43
44pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
45 ty::type_for_def(db, def_id)
46}
47
38pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 48pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
39 let mut res = SourceFileItems::new(file_id); 49 let mut res = SourceFileItems::new(file_id);
40 let source_file = db.source_file(file_id); 50 let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
new file mode 100644
index 000000000..c759d4c8b
--- /dev/null
+++ b/crates/ra_hir/src/ty.rs
@@ -0,0 +1,601 @@
1mod primitive;
2#[cfg(test)]
3mod tests;
4
5use std::sync::Arc;
6use std::fmt;
7
8use log;
9use rustc_hash::{FxHashMap};
10
11use ra_db::{LocalSyntaxPtr, Cancelable};
12use ra_syntax::{
13 SmolStr,
14 ast::{self, AstNode, LoopBodyOwner, ArgListOwner},
15 SyntaxNodeRef
16};
17
18use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
19
20#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
21pub enum Ty {
22 /// The primitive boolean type. Written as `bool`.
23 Bool,
24
25 /// The primitive character type; holds a Unicode scalar value
26 /// (a non-surrogate code point). Written as `char`.
27 Char,
28
29 /// A primitive signed integer type. For example, `i32`.
30 Int(primitive::IntTy),
31
32 /// A primitive unsigned integer type. For example, `u32`.
33 Uint(primitive::UintTy),
34
35 /// A primitive floating-point type. For example, `f64`.
36 Float(primitive::FloatTy),
37
38 // Structures, enumerations and unions.
39 // Adt(AdtDef, Substs),
40 /// The pointee of a string slice. Written as `str`.
41 Str,
42
43 // An array with the given length. Written as `[T; n]`.
44 // Array(Ty, ty::Const),
45 /// The pointee of an array slice. Written as `[T]`.
46 Slice(TyRef),
47
48 // A raw pointer. Written as `*mut T` or `*const T`
49 // RawPtr(TypeAndMut<'tcx>),
50
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`.
55 ///
56 /// For example the type of `bar` here:
57 ///
58 /// ```rust
59 /// fn foo() -> i32 { 1 }
60 /// let bar: fn() -> i32 = foo;
61 /// ```
62 FnPtr(Arc<FnSig>),
63
64 // A trait, defined with `dyn trait`.
65 // Dynamic(),
66 /// The anonymous type of a closure. Used to represent the type of
67 /// `|a| a`.
68 // Closure(DefId, ClosureSubsts<'tcx>),
69
70 /// The anonymous type of a generator. Used to represent the type of
71 /// `|a| yield a`.
72 // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),
73
74 /// A type representin the types stored inside a generator.
75 /// This should only appear in GeneratorInteriors.
76 // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
77
78 /// The never type `!`
79 Never,
80
81 /// A tuple type. For example, `(i32, bool)`.
82 Tuple(Vec<Ty>),
83
84 // The projection of an associated type. For example,
85 // `<T as Trait<..>>::N`.
86 // Projection(ProjectionTy),
87
88 // Opaque (`impl Trait`) type found in a return type.
89 // The `DefId` comes either from
90 // * the `impl Trait` ast::Ty node,
91 // * or the `existential type` declaration
92 // The substitutions are for the generics of the function in question.
93 // Opaque(DefId, Substs),
94
95 // A type parameter; for example, `T` in `fn f<T>(x: T) {}
96 // Param(ParamTy),
97
98 // A placeholder type - universally quantified higher-ranked type.
99 // Placeholder(ty::PlaceholderType),
100
101 // A type variable used during type checking.
102 // Infer(InferTy),
103 /// A placeholder for a type which could not be computed; this is
104 /// propagated to avoid useless error messages.
105 Unknown,
106}
107
108type TyRef = Arc<Ty>;
109
110#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
111pub struct FnSig {
112 input: Vec<Ty>,
113 output: Ty,
114}
115
116impl Ty {
117 pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> {
118 use ra_syntax::ast::TypeRef::*;
119 Ok(match node {
120 ParenType(_inner) => Ty::Unknown, // TODO
121 TupleType(_inner) => Ty::Unknown, // TODO
122 NeverType(..) => Ty::Never,
123 PathType(inner) => {
124 let path = if let Some(p) = inner.path() {
125 p
126 } else {
127 return Ok(Ty::Unknown);
128 };
129 if path.qualifier().is_none() {
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 }
150 PointerType(_inner) => Ty::Unknown, // TODO
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 })
160 }
161
162 pub fn unit() -> Self {
163 Ty::Tuple(Vec::new())
164 }
165}
166
167impl fmt::Display for Ty {
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 match self {
170 Ty::Bool => write!(f, "bool"),
171 Ty::Char => write!(f, "char"),
172 Ty::Int(t) => write!(f, "{}", t.ty_to_string()),
173 Ty::Uint(t) => write!(f, "{}", t.ty_to_string()),
174 Ty::Float(t) => write!(f, "{}", t.ty_to_string()),
175 Ty::Str => write!(f, "str"),
176 Ty::Slice(t) => write!(f, "[{}]", t),
177 Ty::Never => write!(f, "!"),
178 Ty::Tuple(ts) => {
179 write!(f, "(")?;
180 for t in ts {
181 write!(f, "{},", t)?;
182 }
183 write!(f, ")")
184 }
185 Ty::FnPtr(sig) => {
186 write!(f, "fn(")?;
187 for t in &sig.input {
188 write!(f, "{},", t)?;
189 }
190 write!(f, ") -> {}", sig.output)
191 }
192 Ty::Unknown => write!(f, "[unknown]"),
193 }
194 }
195}
196
197pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
198 let syntax = f.syntax(db);
199 let node = syntax.borrowed();
200 // TODO we ignore type parameters for now
201 let input = node
202 .param_list()
203 .map(|pl| {
204 pl.params()
205 .map(|p| {
206 p.type_ref()
207 .map(|t| Ty::new(db, t))
208 .unwrap_or(Ok(Ty::Unknown))
209 })
210 .collect()
211 })
212 .unwrap_or_else(|| Ok(Vec::new()))?;
213 let output = node
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 };
219 Ok(Ty::FnPtr(Arc::new(sig)))
220}
221
222// TODO this should probably be per namespace (i.e. types vs. values), since for
223// a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but
224// defines the struct type Foo when used in the type namespace. rustc has a
225// separate DefId for the constructor, but with the current DefId approach, that
226// seems complicated.
227pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
228 let def = def_id.resolve(db)?;
229 match def {
230 Def::Module(..) => {
231 log::debug!("trying to get type for module {:?}", def_id);
232 Ok(Ty::Unknown)
233 }
234 Def::Function(f) => type_for_fn(db, f),
235 Def::Item => {
236 log::debug!("trying to get type for item of unknown type {:?}", def_id);
237 Ok(Ty::Unknown)
238 }
239 }
240}
241
242#[derive(Clone, PartialEq, Eq, Debug)]
243pub struct InferenceResult {
244 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
245}
246
247impl InferenceResult {
248 pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> {
249 self.type_of.get(&LocalSyntaxPtr::new(node)).cloned()
250 }
251}
252
253#[derive(Clone, Debug)]
254pub struct InferenceContext<'a, D: HirDatabase> {
255 db: &'a D,
256 scopes: Arc<FnScopes>,
257 module: Module,
258 // TODO unification tables...
259 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
260}
261
262impl<'a, D: HirDatabase> InferenceContext<'a, D> {
263 fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self {
264 InferenceContext {
265 type_of: FxHashMap::default(),
266 db,
267 scopes,
268 module,
269 }
270 }
271
272 fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) {
273 self.type_of.insert(LocalSyntaxPtr::new(node), ty);
274 }
275
276 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> {
277 if *ty1 == Ty::Unknown {
278 return Some(ty2.clone());
279 }
280 if *ty2 == Ty::Unknown {
281 return Some(ty1.clone());
282 }
283 if ty1 == ty2 {
284 return Some(ty1.clone());
285 }
286 // TODO implement actual unification
287 return None;
288 }
289
290 fn unify_with_coercion(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> {
291 // TODO implement coercion
292 self.unify(ty1, ty2)
293 }
294
295 fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> {
296 let ast_path = ctry!(expr.path());
297 let path = ctry!(Path::from_ast(ast_path));
298 if path.is_ident() {
299 // resolve locally
300 let name = ctry!(ast_path.segment().and_then(|s| s.name_ref()));
301 if let Some(scope_entry) = self.scopes.resolve_local_name(name) {
302 let ty = ctry!(self.type_of.get(&scope_entry.ptr()));
303 return Ok(Some(ty.clone()));
304 };
305 };
306
307 // resolve in module
308 let resolved = ctry!(self.module.resolve_path(self.db, path)?);
309 let ty = self.db.type_for_def(resolved)?;
310 // TODO we will need to add type variables for type parameters etc. here
311 Ok(Some(ty))
312 }
313
314 fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> {
315 let ty = match expr {
316 ast::Expr::IfExpr(e) => {
317 if let Some(condition) = e.condition() {
318 if let Some(e) = condition.expr() {
319 // TODO if no pat, this should be bool
320 self.infer_expr(e)?;
321 }
322 // TODO write type for pat
323 };
324 let if_ty = if let Some(block) = e.then_branch() {
325 self.infer_block(block)?
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) {
335 ty
336 } else {
337 // TODO report diagnostic
338 Ty::Unknown
339 }
340 }
341 ast::Expr::BlockExpr(e) => {
342 if let Some(block) = e.block() {
343 self.infer_block(block)?
344 } else {
345 Ty::Unknown
346 }
347 }
348 ast::Expr::LoopExpr(e) => {
349 if let Some(block) = e.loop_body() {
350 self.infer_block(block)?;
351 };
352 // TODO never, or the type of the break param
353 Ty::Unknown
354 }
355 ast::Expr::WhileExpr(e) => {
356 if let Some(condition) = e.condition() {
357 if let Some(e) = condition.expr() {
358 // TODO if no pat, this should be bool
359 self.infer_expr(e)?;
360 }
361 // TODO write type for pat
362 };
363 if let Some(block) = e.loop_body() {
364 // TODO
365 self.infer_block(block)?;
366 };
367 // TODO always unit?
368 Ty::Unknown
369 }
370 ast::Expr::ForExpr(e) => {
371 if let Some(expr) = e.iterable() {
372 self.infer_expr(expr)?;
373 }
374 if let Some(_pat) = e.pat() {
375 // TODO write type for pat
376 }
377 if let Some(block) = e.loop_body() {
378 self.infer_block(block)?;
379 }
380 // TODO always unit?
381 Ty::Unknown
382 }
383 ast::Expr::LambdaExpr(e) => {
384 let _body_ty = if let Some(body) = e.body() {
385 self.infer_expr(body)?
386 } else {
387 Ty::Unknown
388 };
389 Ty::Unknown
390 }
391 ast::Expr::CallExpr(e) => {
392 let callee_ty = if let Some(e) = e.expr() {
393 self.infer_expr(e)?
394 } else {
395 Ty::Unknown
396 };
397 if let Some(arg_list) = e.arg_list() {
398 for arg in arg_list.args() {
399 // TODO unify / expect argument type
400 self.infer_expr(arg)?;
401 }
402 }
403 match callee_ty {
404 Ty::FnPtr(sig) => sig.output.clone(),
405 _ => {
406 // not callable
407 // TODO report an error?
408 Ty::Unknown
409 }
410 }
411 }
412 ast::Expr::MethodCallExpr(e) => {
413 let _receiver_ty = if let Some(e) = e.expr() {
414 self.infer_expr(e)?
415 } else {
416 Ty::Unknown
417 };
418 if let Some(arg_list) = e.arg_list() {
419 for arg in arg_list.args() {
420 // TODO unify / expect argument type
421 self.infer_expr(arg)?;
422 }
423 }
424 Ty::Unknown
425 }
426 ast::Expr::MatchExpr(e) => {
427 let _ty = if let Some(match_expr) = 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() {
433 for arm in match_arm_list.arms() {
434 // TODO type the bindings in pat
435 // TODO type the guard
436 let _ty = if let Some(e) = arm.expr() {
437 self.infer_expr(e)?
438 } else {
439 Ty::Unknown
440 };
441 }
442 // TODO unify all the match arm types
443 Ty::Unknown
444 } else {
445 Ty::Unknown
446 }
447 }
448 ast::Expr::TupleExpr(_e) => Ty::Unknown,
449 ast::Expr::ArrayExpr(_e) => Ty::Unknown,
450 ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown),
451 ast::Expr::ContinueExpr(_e) => Ty::Never,
452 ast::Expr::BreakExpr(_e) => Ty::Never,
453 ast::Expr::ParenExpr(e) => {
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,
461 ast::Expr::ReturnExpr(e) => {
462 if let Some(e) = e.expr() {
463 // TODO unify with return type
464 self.infer_expr(e)?;
465 };
466 Ty::Never
467 }
468 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
469 // Can this even occur outside of a match expression?
470 Ty::Unknown
471 }
472 ast::Expr::StructLit(_e) => Ty::Unknown,
473 ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
474 // Can this even occur outside of a struct literal?
475 Ty::Unknown
476 }
477 ast::Expr::IndexExpr(_e) => Ty::Unknown,
478 ast::Expr::FieldExpr(_e) => Ty::Unknown,
479 ast::Expr::TryExpr(e) => {
480 let _inner_ty = if let Some(e) = e.expr() {
481 self.infer_expr(e)?
482 } else {
483 Ty::Unknown
484 };
485 Ty::Unknown
486 }
487 ast::Expr::CastExpr(e) => {
488 let _inner_ty = if let Some(e) = e.expr() {
489 self.infer_expr(e)?
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...
498 cast_ty
499 }
500 ast::Expr::RefExpr(e) => {
501 let _inner_ty = if let Some(e) = e.expr() {
502 self.infer_expr(e)?
503 } else {
504 Ty::Unknown
505 };
506 Ty::Unknown
507 }
508 ast::Expr::PrefixExpr(e) => {
509 let _inner_ty = if let Some(e) = e.expr() {
510 self.infer_expr(e)?
511 } else {
512 Ty::Unknown
513 };
514 Ty::Unknown
515 }
516 ast::Expr::RangeExpr(_e) => Ty::Unknown,
517 ast::Expr::BinExpr(_e) => Ty::Unknown,
518 ast::Expr::Literal(_e) => Ty::Unknown,
519 };
520 self.write_ty(expr.syntax(), ty.clone());
521 Ok(ty)
522 }
523
524 fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> {
525 for stmt in node.statements() {
526 match stmt {
527 ast::Stmt::LetStmt(stmt) => {
528 let decl_ty = if let Some(type_ref) = 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() {
534 // TODO pass expectation
535 let expr_ty = self.infer_expr(expr)?;
536 self.unify_with_coercion(&expr_ty, &decl_ty)
537 .unwrap_or(decl_ty)
538 } else {
539 decl_ty
540 };
541
542 if let Some(pat) = stmt.pat() {
543 self.write_ty(pat.syntax(), ty);
544 };
545 }
546 ast::Stmt::ExprStmt(expr_stmt) => {
547 if let Some(expr) = expr_stmt.expr() {
548 self.infer_expr(expr)?;
549 }
550 }
551 }
552 }
553 let ty = if let Some(expr) = node.expr() {
554 self.infer_expr(expr)?
555 } else {
556 Ty::unit()
557 };
558 self.write_ty(node.syntax(), ty.clone());
559 Ok(ty)
560 }
561}
562
563pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> {
564 let scopes = function.scopes(db);
565 let module = function.module(db)?;
566 let mut ctx = InferenceContext::new(db, scopes, module);
567
568 let syntax = function.syntax(db);
569 let node = syntax.borrowed();
570
571 if let Some(param_list) = node.param_list() {
572 for param in param_list.params() {
573 let pat = if let Some(pat) = param.pat() {
574 pat
575 } else {
576 continue;
577 };
578 if let Some(type_ref) = param.type_ref() {
579 let ty = Ty::new(db, type_ref)?;
580 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
581 } else {
582 // TODO self param
583 ctx.type_of
584 .insert(LocalSyntaxPtr::new(pat.syntax()), Ty::Unknown);
585 };
586 }
587 }
588
589 // TODO get Ty for node.ret_type() and pass that to infer_block as expectation
590 // (see Expectation in rustc_typeck)
591
592 if let Some(block) = node.body() {
593 ctx.infer_block(block)?;
594 }
595
596 // TODO 'resolve' the types: replace inference variables by their inferred results
597
598 Ok(InferenceResult {
599 type_of: ctx.type_of,
600 })
601}
diff --git a/crates/ra_hir/src/ty/primitive.rs b/crates/ra_hir/src/ty/primitive.rs
new file mode 100644
index 000000000..ad79b17e4
--- /dev/null
+++ b/crates/ra_hir/src/ty/primitive.rs
@@ -0,0 +1,130 @@
1use std::fmt;
2
3#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
4pub enum IntTy {
5 Isize,
6 I8,
7 I16,
8 I32,
9 I64,
10 I128,
11}
12
13impl fmt::Debug for IntTy {
14 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15 fmt::Display::fmt(self, f)
16 }
17}
18
19impl fmt::Display for IntTy {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 write!(f, "{}", self.ty_to_string())
22 }
23}
24
25impl IntTy {
26 pub fn ty_to_string(&self) -> &'static str {
27 match *self {
28 IntTy::Isize => "isize",
29 IntTy::I8 => "i8",
30 IntTy::I16 => "i16",
31 IntTy::I32 => "i32",
32 IntTy::I64 => "i64",
33 IntTy::I128 => "i128",
34 }
35 }
36
37 pub fn from_string(s: &str) -> Option<IntTy> {
38 match s {
39 "isize" => Some(IntTy::Isize),
40 "i8" => Some(IntTy::I8),
41 "i16" => Some(IntTy::I16),
42 "i32" => Some(IntTy::I32),
43 "i64" => Some(IntTy::I64),
44 "i128" => Some(IntTy::I128),
45 _ => None,
46 }
47 }
48}
49
50#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
51pub enum UintTy {
52 Usize,
53 U8,
54 U16,
55 U32,
56 U64,
57 U128,
58}
59
60impl UintTy {
61 pub fn ty_to_string(&self) -> &'static str {
62 match *self {
63 UintTy::Usize => "usize",
64 UintTy::U8 => "u8",
65 UintTy::U16 => "u16",
66 UintTy::U32 => "u32",
67 UintTy::U64 => "u64",
68 UintTy::U128 => "u128",
69 }
70 }
71
72 pub fn from_string(s: &str) -> Option<UintTy> {
73 match s {
74 "usize" => Some(UintTy::Usize),
75 "u8" => Some(UintTy::U8),
76 "u16" => Some(UintTy::U16),
77 "u32" => Some(UintTy::U32),
78 "u64" => Some(UintTy::U64),
79 "u128" => Some(UintTy::U128),
80 _ => None,
81 }
82 }
83}
84
85impl fmt::Debug for UintTy {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 fmt::Display::fmt(self, f)
88 }
89}
90
91impl fmt::Display for UintTy {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 write!(f, "{}", self.ty_to_string())
94 }
95}
96
97#[derive(Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
98pub enum FloatTy {
99 F32,
100 F64,
101}
102
103impl fmt::Debug for FloatTy {
104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 fmt::Display::fmt(self, f)
106 }
107}
108
109impl fmt::Display for FloatTy {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 write!(f, "{}", self.ty_to_string())
112 }
113}
114
115impl FloatTy {
116 pub fn ty_to_string(self) -> &'static str {
117 match self {
118 FloatTy::F32 => "f32",
119 FloatTy::F64 => "f64",
120 }
121 }
122
123 pub fn from_string(s: &str) -> Option<FloatTy> {
124 match s {
125 "f32" => Some(FloatTy::F32),
126 "f64" => Some(FloatTy::F64),
127 _ => None,
128 }
129 }
130}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
new file mode 100644
index 000000000..b6c02cd80
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -0,0 +1,134 @@
1use std::fmt::Write;
2use std::path::{PathBuf, Path};
3use std::fs;
4
5use ra_db::{SyntaxDatabase};
6use ra_syntax::ast::{self, AstNode};
7use test_utils::{project_dir, assert_eq_text, read_text};
8
9use crate::{
10 source_binder,
11 mock::MockDatabase,
12};
13
14// These tests compare the inference results for all expressions in a file
15// against snapshots of the current results. If you change something and these
16// tests fail expectedly, you can update the comparison files by deleting them
17// and running the tests again. Similarly, to add a new test, just write the
18// test here in the same pattern and it will automatically write the snapshot.
19
20#[test]
21fn infer_basics() {
22 check_inference(
23 r#"
24fn test(a: u32, b: isize, c: !, d: &str) {
25 a;
26 b;
27 c;
28 d;
29 1usize;
30 1isize;
31 "test";
32 1.0f32;
33}"#,
34 "0001_basics.txt",
35 );
36}
37
38#[test]
39fn infer_let() {
40 check_inference(
41 r#"
42fn test() {
43 let a = 1isize;
44 let b: usize = 1;
45 let c = b;
46}
47}"#,
48 "0002_let.txt",
49 );
50}
51
52#[test]
53fn infer_paths() {
54 check_inference(
55 r#"
56fn a() -> u32 { 1 }
57
58mod b {
59 fn c() -> u32 { 1 }
60}
61
62fn test() {
63 a();
64 b::c();
65}
66}"#,
67 "0003_paths.txt",
68 );
69}
70
71fn infer(content: &str) -> String {
72 let (db, _, file_id) = MockDatabase::with_single_file(content);
73 let source_file = db.source_file(file_id);
74 let mut acc = String::new();
75 for fn_def in source_file
76 .syntax()
77 .descendants()
78 .filter_map(ast::FnDef::cast)
79 {
80 let func = source_binder::function_from_source(&db, file_id, fn_def)
81 .unwrap()
82 .unwrap();
83 let inference_result = func.infer(&db).unwrap();
84 for (syntax_ptr, ty) in &inference_result.type_of {
85 let node = syntax_ptr.resolve(&source_file);
86 write!(
87 acc,
88 "{} '{}': {}\n",
89 syntax_ptr.range(),
90 ellipsize(node.text().to_string().replace("\n", " "), 15),
91 ty
92 )
93 .unwrap();
94 }
95 }
96 acc
97}
98
99fn check_inference(content: &str, data_file: impl AsRef<Path>) {
100 let data_file_path = test_data_dir().join(data_file);
101 let result = infer(content);
102
103 if !data_file_path.exists() {
104 println!("File with expected result doesn't exist, creating...\n");
105 println!("{}\n{}", content, result);
106 fs::write(&data_file_path, &result).unwrap();
107 panic!("File {:?} with expected result was created", data_file_path);
108 }
109
110 let expected = read_text(&data_file_path);
111 assert_eq_text!(&expected, &result);
112}
113
114fn ellipsize(mut text: String, max_len: usize) -> String {
115 if text.len() <= max_len {
116 return text;
117 }
118 let ellipsis = "...";
119 let e_len = ellipsis.len();
120 let mut prefix_len = (max_len - e_len) / 2;
121 while !text.is_char_boundary(prefix_len) {
122 prefix_len += 1;
123 }
124 let mut suffix_len = max_len - e_len - prefix_len;
125 while !text.is_char_boundary(text.len() - suffix_len) {
126 suffix_len += 1;
127 }
128 text.replace_range(prefix_len..text.len() - suffix_len, ellipsis);
129 text
130}
131
132fn test_data_dir() -> PathBuf {
133 project_dir().join("crates/ra_hir/src/ty/tests/data")
134}
diff --git a/crates/ra_hir/src/ty/tests/data/0001_basics.txt b/crates/ra_hir/src/ty/tests/data/0001_basics.txt
new file mode 100644
index 000000000..0c46f243a
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/0001_basics.txt
@@ -0,0 +1,13 @@
1[33; 34) 'd': [unknown]
2[88; 94) '1isize': [unknown]
3[48; 49) 'a': u32
4[55; 56) 'b': isize
5[112; 118) '1.0f32': [unknown]
6[76; 82) '1usize': [unknown]
7[9; 10) 'a': u32
8[27; 28) 'c': !
9[62; 63) 'c': !
10[17; 18) 'b': isize
11[100; 106) '"test"': [unknown]
12[42; 121) '{ ...f32; }': ()
13[69; 70) 'd': [unknown]
diff --git a/crates/ra_hir/src/ty/tests/data/0002_let.txt b/crates/ra_hir/src/ty/tests/data/0002_let.txt
new file mode 100644
index 000000000..2d0d1f57b
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/0002_let.txt
@@ -0,0 +1,7 @@
1[21; 22) 'a': [unknown]
2[52; 53) '1': [unknown]
3[11; 71) '{ ...= b; }': ()
4[63; 64) 'c': usize
5[25; 31) '1isize': [unknown]
6[41; 42) 'b': usize
7[67; 68) 'b': usize
diff --git a/crates/ra_hir/src/ty/tests/data/0003_paths.txt b/crates/ra_hir/src/ty/tests/data/0003_paths.txt
new file mode 100644
index 000000000..dcb5456ae
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/0003_paths.txt
@@ -0,0 +1,9 @@
1[15; 20) '{ 1 }': [unknown]
2[17; 18) '1': [unknown]
3[50; 51) '1': [unknown]
4[48; 53) '{ 1 }': [unknown]
5[82; 88) 'b::c()': u32
6[67; 91) '{ ...c(); }': ()
7[73; 74) 'a': fn() -> u32
8[73; 76) 'a()': u32
9[82; 86) 'b::c': fn() -> u32