diff options
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/db.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/function.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir/src/module.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/module/nameres.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/query_definitions.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 601 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/primitive.rs | 130 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 134 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0001_basics.txt | 13 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0002_let.txt | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/0003_paths.txt | 9 |
13 files changed, 966 insertions, 7 deletions
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 | ||
19 | salsa::query_group! { | 20 | salsa::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 | ||
8 | use ra_db::Cancelable; | ||
8 | use ra_syntax::{ | 9 | use ra_syntax::{ |
9 | TextRange, TextUnit, | 10 | TextRange, TextUnit, |
10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 11 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, |
11 | }; | 12 | }; |
12 | 13 | ||
13 | use crate::{ DefId, HirDatabase }; | 14 | use crate::{ DefId, HirDatabase, ty::InferenceResult, Module }; |
14 | 15 | ||
15 | pub use self::scope::FnScopes; | 16 | pub use self::scope::FnScopes; |
16 | 17 | ||
@@ -18,7 +19,7 @@ pub use self::scope::FnScopes; | |||
18 | pub struct FnId(pub(crate) DefId); | 19 | pub struct FnId(pub(crate) DefId); |
19 | 20 | ||
20 | pub struct Function { | 21 | pub struct Function { |
21 | fn_id: FnId, | 22 | pub(crate) fn_id: FnId, |
22 | } | 23 | } |
23 | 24 | ||
24 | impl Function { | 25 | impl 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; | |||
25 | mod krate; | 25 | mod krate; |
26 | mod module; | 26 | mod module; |
27 | mod function; | 27 | mod function; |
28 | mod ty; | ||
28 | 29 | ||
29 | use std::ops::Index; | 30 | use std::ops::Index; |
30 | 31 | ||
31 | use ra_syntax::{SyntaxNodeRef, SyntaxNode}; | 32 | use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind}; |
32 | use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; | 33 | use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; |
33 | 34 | ||
34 | use crate::{ | 35 | use crate::{ |
@@ -66,6 +67,23 @@ pub struct DefLoc { | |||
66 | source_item_id: SourceItemId, | 67 | source_item_id: SourceItemId, |
67 | } | 68 | } |
68 | 69 | ||
70 | impl 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 | |||
69 | impl DefId { | 87 | impl 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 | ||
9 | use crate::{db, DefId, DefLoc}; | 9 | use crate::{db, DefId, DefLoc}; |
10 | 10 | ||
11 | const WORKSPACE: SourceRootId = SourceRootId(0); | 11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
12 | 12 | ||
13 | #[derive(Debug)] | 13 | #[derive(Debug)] |
14 | pub(crate) struct MockDatabase { | 14 | pub(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; | |||
2 | pub(super) mod nameres; | 2 | pub(super) mod nameres; |
3 | 3 | ||
4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
5 | use log; | ||
5 | 6 | ||
6 | use ra_syntax::{ | 7 | use 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::{ | |||
11 | use ra_db::{SourceRootId, FileId, Cancelable,}; | 11 | use ra_db::{SourceRootId, FileId, Cancelable,}; |
12 | 12 | ||
13 | use crate::{ | 13 | use 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 | ||
39 | pub(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 | |||
44 | pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | ||
45 | ty::type_for_def(db, def_id) | ||
46 | } | ||
47 | |||
38 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 48 | pub(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 @@ | |||
1 | mod primitive; | ||
2 | #[cfg(test)] | ||
3 | mod tests; | ||
4 | |||
5 | use std::sync::Arc; | ||
6 | use std::fmt; | ||
7 | |||
8 | use log; | ||
9 | use rustc_hash::{FxHashMap}; | ||
10 | |||
11 | use ra_db::{LocalSyntaxPtr, Cancelable}; | ||
12 | use ra_syntax::{ | ||
13 | SmolStr, | ||
14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner}, | ||
15 | SyntaxNodeRef | ||
16 | }; | ||
17 | |||
18 | use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; | ||
19 | |||
20 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
21 | pub 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 | |||
108 | type TyRef = Arc<Ty>; | ||
109 | |||
110 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
111 | pub struct FnSig { | ||
112 | input: Vec<Ty>, | ||
113 | output: Ty, | ||
114 | } | ||
115 | |||
116 | impl 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 | |||
167 | impl 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 | |||
197 | pub 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. | ||
227 | pub 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)] | ||
243 | pub struct InferenceResult { | ||
244 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | ||
245 | } | ||
246 | |||
247 | impl 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)] | ||
254 | pub 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 | |||
262 | impl<'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 | |||
563 | pub 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 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] | ||
4 | pub enum IntTy { | ||
5 | Isize, | ||
6 | I8, | ||
7 | I16, | ||
8 | I32, | ||
9 | I64, | ||
10 | I128, | ||
11 | } | ||
12 | |||
13 | impl fmt::Debug for IntTy { | ||
14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
15 | fmt::Display::fmt(self, f) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl fmt::Display for IntTy { | ||
20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
21 | write!(f, "{}", self.ty_to_string()) | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl 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)] | ||
51 | pub enum UintTy { | ||
52 | Usize, | ||
53 | U8, | ||
54 | U16, | ||
55 | U32, | ||
56 | U64, | ||
57 | U128, | ||
58 | } | ||
59 | |||
60 | impl 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 | |||
85 | impl fmt::Debug for UintTy { | ||
86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
87 | fmt::Display::fmt(self, f) | ||
88 | } | ||
89 | } | ||
90 | |||
91 | impl 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)] | ||
98 | pub enum FloatTy { | ||
99 | F32, | ||
100 | F64, | ||
101 | } | ||
102 | |||
103 | impl fmt::Debug for FloatTy { | ||
104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
105 | fmt::Display::fmt(self, f) | ||
106 | } | ||
107 | } | ||
108 | |||
109 | impl fmt::Display for FloatTy { | ||
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
111 | write!(f, "{}", self.ty_to_string()) | ||
112 | } | ||
113 | } | ||
114 | |||
115 | impl 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 @@ | |||
1 | use std::fmt::Write; | ||
2 | use std::path::{PathBuf, Path}; | ||
3 | use std::fs; | ||
4 | |||
5 | use ra_db::{SyntaxDatabase}; | ||
6 | use ra_syntax::ast::{self, AstNode}; | ||
7 | use test_utils::{project_dir, assert_eq_text, read_text}; | ||
8 | |||
9 | use 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] | ||
21 | fn infer_basics() { | ||
22 | check_inference( | ||
23 | r#" | ||
24 | fn 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] | ||
39 | fn infer_let() { | ||
40 | check_inference( | ||
41 | r#" | ||
42 | fn 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] | ||
53 | fn infer_paths() { | ||
54 | check_inference( | ||
55 | r#" | ||
56 | fn a() -> u32 { 1 } | ||
57 | |||
58 | mod b { | ||
59 | fn c() -> u32 { 1 } | ||
60 | } | ||
61 | |||
62 | fn test() { | ||
63 | a(); | ||
64 | b::c(); | ||
65 | } | ||
66 | }"#, | ||
67 | "0003_paths.txt", | ||
68 | ); | ||
69 | } | ||
70 | |||
71 | fn 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 | |||
99 | fn 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 | |||
114 | fn 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 | |||
132 | fn 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 | ||