diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-24 14:40:11 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-24 14:40:11 +0000 |
commit | 67e768466ff2e2611eead0f30b2e9c4083c80c20 (patch) | |
tree | 8984028019837c91131fc30f60eecf8c2a457368 /crates/ra_hir/src/ty/tests.rs | |
parent | abe09eb5edfe8f4c58baa16140acbd414635836f (diff) | |
parent | 4befde1eee5b1e2b7ddc9bf764b77f82b792c318 (diff) |
Merge #327
327: Beginnings of type inference r=flodiebold a=flodiebold
I was a bit bored, so I thought I'd try to start implementing the type system and see how far I come :wink: This is obviously still extremely WIP, only very basic stuff working, but I thought I'd post this now to get some feedback as to whether this approach makes sense at all.
There's no user-visible effect yet, but the type inference has tests similar to the ones for the parser. My next step will probably be to implement struct types, after which this could probably be used to complete fields.
I realize this may all get thrown away when/if the compiler query system gets usable, but I feel like there are lots of IDE features that could be implemented with somewhat working type inference in the meantime :smile:
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/ty/tests.rs')
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 134 |
1 files changed, 134 insertions, 0 deletions
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 | } | ||