aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-12-24 14:40:11 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-12-24 14:40:11 +0000
commit67e768466ff2e2611eead0f30b2e9c4083c80c20 (patch)
tree8984028019837c91131fc30f60eecf8c2a457368 /crates/ra_hir/src/ty
parentabe09eb5edfe8f4c58baa16140acbd414635836f (diff)
parent4befde1eee5b1e2b7ddc9bf764b77f82b792c318 (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')
-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
5 files changed, 293 insertions, 0 deletions
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