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_syntax | |
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_syntax')
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 60 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 19 | ||||
-rw-r--r-- | crates/ra_syntax/tests/test.rs | 157 |
3 files changed, 96 insertions, 140 deletions
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index bf056131e..c73533861 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -523,7 +523,15 @@ impl<R: TreeRoot<RaTypes>> CastExprNode<R> { | |||
523 | } | 523 | } |
524 | 524 | ||
525 | 525 | ||
526 | impl<'a> CastExpr<'a> {} | 526 | impl<'a> CastExpr<'a> { |
527 | pub fn expr(self) -> Option<Expr<'a>> { | ||
528 | super::child_opt(self) | ||
529 | } | ||
530 | |||
531 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
532 | super::child_opt(self) | ||
533 | } | ||
534 | } | ||
527 | 535 | ||
528 | // Char | 536 | // Char |
529 | #[derive(Debug, Clone, Copy,)] | 537 | #[derive(Debug, Clone, Copy,)] |
@@ -1553,6 +1561,10 @@ impl<'a> LetStmt<'a> { | |||
1553 | super::child_opt(self) | 1561 | super::child_opt(self) |
1554 | } | 1562 | } |
1555 | 1563 | ||
1564 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
1565 | super::child_opt(self) | ||
1566 | } | ||
1567 | |||
1556 | pub fn initializer(self) -> Option<Expr<'a>> { | 1568 | pub fn initializer(self) -> Option<Expr<'a>> { |
1557 | super::child_opt(self) | 1569 | super::child_opt(self) |
1558 | } | 1570 | } |
@@ -2312,6 +2324,10 @@ impl<'a> Param<'a> { | |||
2312 | pub fn pat(self) -> Option<Pat<'a>> { | 2324 | pub fn pat(self) -> Option<Pat<'a>> { |
2313 | super::child_opt(self) | 2325 | super::child_opt(self) |
2314 | } | 2326 | } |
2327 | |||
2328 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
2329 | super::child_opt(self) | ||
2330 | } | ||
2315 | } | 2331 | } |
2316 | 2332 | ||
2317 | // ParamList | 2333 | // ParamList |
@@ -2394,7 +2410,11 @@ impl<R: TreeRoot<RaTypes>> ParenExprNode<R> { | |||
2394 | } | 2410 | } |
2395 | 2411 | ||
2396 | 2412 | ||
2397 | impl<'a> ParenExpr<'a> {} | 2413 | impl<'a> ParenExpr<'a> { |
2414 | pub fn expr(self) -> Option<Expr<'a>> { | ||
2415 | super::child_opt(self) | ||
2416 | } | ||
2417 | } | ||
2398 | 2418 | ||
2399 | // ParenType | 2419 | // ParenType |
2400 | #[derive(Debug, Clone, Copy,)] | 2420 | #[derive(Debug, Clone, Copy,)] |
@@ -2681,7 +2701,11 @@ impl<R: TreeRoot<RaTypes>> PathTypeNode<R> { | |||
2681 | } | 2701 | } |
2682 | 2702 | ||
2683 | 2703 | ||
2684 | impl<'a> PathType<'a> {} | 2704 | impl<'a> PathType<'a> { |
2705 | pub fn path(self) -> Option<Path<'a>> { | ||
2706 | super::child_opt(self) | ||
2707 | } | ||
2708 | } | ||
2685 | 2709 | ||
2686 | // PlaceholderPat | 2710 | // PlaceholderPat |
2687 | #[derive(Debug, Clone, Copy,)] | 2711 | #[derive(Debug, Clone, Copy,)] |
@@ -2829,7 +2853,11 @@ impl<R: TreeRoot<RaTypes>> PrefixExprNode<R> { | |||
2829 | } | 2853 | } |
2830 | 2854 | ||
2831 | 2855 | ||
2832 | impl<'a> PrefixExpr<'a> {} | 2856 | impl<'a> PrefixExpr<'a> { |
2857 | pub fn expr(self) -> Option<Expr<'a>> { | ||
2858 | super::child_opt(self) | ||
2859 | } | ||
2860 | } | ||
2833 | 2861 | ||
2834 | // RangeExpr | 2862 | // RangeExpr |
2835 | #[derive(Debug, Clone, Copy,)] | 2863 | #[derive(Debug, Clone, Copy,)] |
@@ -2940,7 +2968,11 @@ impl<R: TreeRoot<RaTypes>> RefExprNode<R> { | |||
2940 | } | 2968 | } |
2941 | 2969 | ||
2942 | 2970 | ||
2943 | impl<'a> RefExpr<'a> {} | 2971 | impl<'a> RefExpr<'a> { |
2972 | pub fn expr(self) -> Option<Expr<'a>> { | ||
2973 | super::child_opt(self) | ||
2974 | } | ||
2975 | } | ||
2944 | 2976 | ||
2945 | // RefPat | 2977 | // RefPat |
2946 | #[derive(Debug, Clone, Copy,)] | 2978 | #[derive(Debug, Clone, Copy,)] |
@@ -3051,7 +3083,11 @@ impl<R: TreeRoot<RaTypes>> RetTypeNode<R> { | |||
3051 | } | 3083 | } |
3052 | 3084 | ||
3053 | 3085 | ||
3054 | impl<'a> RetType<'a> {} | 3086 | impl<'a> RetType<'a> { |
3087 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
3088 | super::child_opt(self) | ||
3089 | } | ||
3090 | } | ||
3055 | 3091 | ||
3056 | // ReturnExpr | 3092 | // ReturnExpr |
3057 | #[derive(Debug, Clone, Copy,)] | 3093 | #[derive(Debug, Clone, Copy,)] |
@@ -3088,7 +3124,11 @@ impl<R: TreeRoot<RaTypes>> ReturnExprNode<R> { | |||
3088 | } | 3124 | } |
3089 | 3125 | ||
3090 | 3126 | ||
3091 | impl<'a> ReturnExpr<'a> {} | 3127 | impl<'a> ReturnExpr<'a> { |
3128 | pub fn expr(self) -> Option<Expr<'a>> { | ||
3129 | super::child_opt(self) | ||
3130 | } | ||
3131 | } | ||
3092 | 3132 | ||
3093 | // SelfParam | 3133 | // SelfParam |
3094 | #[derive(Debug, Clone, Copy,)] | 3134 | #[derive(Debug, Clone, Copy,)] |
@@ -3578,7 +3618,11 @@ impl<R: TreeRoot<RaTypes>> TryExprNode<R> { | |||
3578 | } | 3618 | } |
3579 | 3619 | ||
3580 | 3620 | ||
3581 | impl<'a> TryExpr<'a> {} | 3621 | impl<'a> TryExpr<'a> { |
3622 | pub fn expr(self) -> Option<Expr<'a>> { | ||
3623 | super::child_opt(self) | ||
3624 | } | ||
3625 | } | ||
3582 | 3626 | ||
3583 | // TupleExpr | 3627 | // TupleExpr |
3584 | #[derive(Debug, Clone, Copy,)] | 3628 | #[derive(Debug, Clone, Copy,)] |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index eed67637e..e3b9032a0 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -254,7 +254,7 @@ Grammar( | |||
254 | ], | 254 | ], |
255 | options: [ "ParamList", ["body", "Block"], "RetType" ], | 255 | options: [ "ParamList", ["body", "Block"], "RetType" ], |
256 | ), | 256 | ), |
257 | "RetType": (), | 257 | "RetType": (options: ["TypeRef"]), |
258 | "StructDef": ( | 258 | "StructDef": ( |
259 | traits: [ | 259 | traits: [ |
260 | "NameOwner", | 260 | "NameOwner", |
@@ -304,7 +304,7 @@ Grammar( | |||
304 | "ParenType": (), | 304 | "ParenType": (), |
305 | "TupleType": (), | 305 | "TupleType": (), |
306 | "NeverType": (), | 306 | "NeverType": (), |
307 | "PathType": (), | 307 | "PathType": (options: ["Path"]), |
308 | "PointerType": (), | 308 | "PointerType": (), |
309 | "ArrayType": (), | 309 | "ArrayType": (), |
310 | "SliceType": (), | 310 | "SliceType": (), |
@@ -346,7 +346,7 @@ Grammar( | |||
346 | 346 | ||
347 | "TupleExpr": (), | 347 | "TupleExpr": (), |
348 | "ArrayExpr": (), | 348 | "ArrayExpr": (), |
349 | "ParenExpr": (), | 349 | "ParenExpr": (options: ["Expr"]), |
350 | "PathExpr": (options: ["Path"]), | 350 | "PathExpr": (options: ["Path"]), |
351 | "LambdaExpr": ( | 351 | "LambdaExpr": ( |
352 | options: [ | 352 | options: [ |
@@ -377,7 +377,7 @@ Grammar( | |||
377 | "BlockExpr": ( | 377 | "BlockExpr": ( |
378 | options: [ "Block" ] | 378 | options: [ "Block" ] |
379 | ), | 379 | ), |
380 | "ReturnExpr": (), | 380 | "ReturnExpr": (options: ["Expr"]), |
381 | "MatchExpr": ( | 381 | "MatchExpr": ( |
382 | options: [ "Expr", "MatchArmList" ], | 382 | options: [ "Expr", "MatchArmList" ], |
383 | ), | 383 | ), |
@@ -405,10 +405,10 @@ Grammar( | |||
405 | ), | 405 | ), |
406 | "IndexExpr": (), | 406 | "IndexExpr": (), |
407 | "FieldExpr": (), | 407 | "FieldExpr": (), |
408 | "TryExpr": (), | 408 | "TryExpr": (options: ["Expr"]), |
409 | "CastExpr": (), | 409 | "CastExpr": (options: ["Expr", "TypeRef"]), |
410 | "RefExpr": (), | 410 | "RefExpr": (options: ["Expr"]), |
411 | "PrefixExpr": (), | 411 | "PrefixExpr": (options: ["Expr"]), |
412 | "RangeExpr": (), | 412 | "RangeExpr": (), |
413 | "BinExpr": (), | 413 | "BinExpr": (), |
414 | "String": (), | 414 | "String": (), |
@@ -499,6 +499,7 @@ Grammar( | |||
499 | ), | 499 | ), |
500 | "LetStmt": ( options: [ | 500 | "LetStmt": ( options: [ |
501 | ["pat", "Pat"], | 501 | ["pat", "Pat"], |
502 | ["type_ref", "TypeRef"], | ||
502 | ["initializer", "Expr"], | 503 | ["initializer", "Expr"], |
503 | ]), | 504 | ]), |
504 | "Condition": ( | 505 | "Condition": ( |
@@ -521,7 +522,7 @@ Grammar( | |||
521 | ), | 522 | ), |
522 | "SelfParam": (), | 523 | "SelfParam": (), |
523 | "Param": ( | 524 | "Param": ( |
524 | options: [ "Pat" ], | 525 | options: [ "Pat", "TypeRef" ], |
525 | ), | 526 | ), |
526 | "UseItem": ( | 527 | "UseItem": ( |
527 | options: [ "UseTree" ] | 528 | options: [ "UseTree" ] |
diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs index 4266864bd..2235dc401 100644 --- a/crates/ra_syntax/tests/test.rs +++ b/crates/ra_syntax/tests/test.rs | |||
@@ -1,14 +1,13 @@ | |||
1 | extern crate ra_syntax; | 1 | extern crate ra_syntax; |
2 | #[macro_use] | ||
3 | extern crate test_utils; | 2 | extern crate test_utils; |
4 | extern crate walkdir; | 3 | extern crate walkdir; |
5 | 4 | ||
6 | use std::{ | 5 | use std::{ |
7 | fmt::Write, | 6 | fmt::Write, |
8 | fs, | 7 | path::{PathBuf, Component}, |
9 | path::{Path, PathBuf, Component}, | ||
10 | }; | 8 | }; |
11 | 9 | ||
10 | use test_utils::{project_dir, dir_tests, read_text, collect_tests}; | ||
12 | use ra_syntax::{ | 11 | use ra_syntax::{ |
13 | utils::{check_fuzz_invariants, dump_tree}, | 12 | utils::{check_fuzz_invariants, dump_tree}, |
14 | SourceFileNode, | 13 | SourceFileNode, |
@@ -16,7 +15,7 @@ use ra_syntax::{ | |||
16 | 15 | ||
17 | #[test] | 16 | #[test] |
18 | fn lexer_tests() { | 17 | fn lexer_tests() { |
19 | dir_tests(&["lexer"], |text, _| { | 18 | dir_tests(&test_data_dir(), &["lexer"], |text, _| { |
20 | let tokens = ra_syntax::tokenize(text); | 19 | let tokens = ra_syntax::tokenize(text); |
21 | dump_tokens(&tokens, text) | 20 | dump_tokens(&tokens, text) |
22 | }) | 21 | }) |
@@ -24,33 +23,41 @@ fn lexer_tests() { | |||
24 | 23 | ||
25 | #[test] | 24 | #[test] |
26 | fn parser_tests() { | 25 | fn parser_tests() { |
27 | dir_tests(&["parser/inline/ok", "parser/ok"], |text, path| { | 26 | dir_tests( |
28 | let file = SourceFileNode::parse(text); | 27 | &test_data_dir(), |
29 | let errors = file.errors(); | 28 | &["parser/inline/ok", "parser/ok"], |
30 | assert_eq!( | 29 | |text, path| { |
31 | &*errors, | 30 | let file = SourceFileNode::parse(text); |
32 | &[] as &[ra_syntax::SyntaxError], | 31 | let errors = file.errors(); |
33 | "There should be no errors in the file {:?}", | 32 | assert_eq!( |
34 | path.display() | 33 | &*errors, |
35 | ); | 34 | &[] as &[ra_syntax::SyntaxError], |
36 | dump_tree(file.syntax()) | 35 | "There should be no errors in the file {:?}", |
37 | }); | 36 | path.display() |
38 | dir_tests(&["parser/err", "parser/inline/err"], |text, path| { | 37 | ); |
39 | let file = SourceFileNode::parse(text); | 38 | dump_tree(file.syntax()) |
40 | let errors = file.errors(); | 39 | }, |
41 | assert_ne!( | 40 | ); |
42 | &*errors, | 41 | dir_tests( |
43 | &[] as &[ra_syntax::SyntaxError], | 42 | &test_data_dir(), |
44 | "There should be errors in the file {:?}", | 43 | &["parser/err", "parser/inline/err"], |
45 | path.display() | 44 | |text, path| { |
46 | ); | 45 | let file = SourceFileNode::parse(text); |
47 | dump_tree(file.syntax()) | 46 | let errors = file.errors(); |
48 | }); | 47 | assert_ne!( |
48 | &*errors, | ||
49 | &[] as &[ra_syntax::SyntaxError], | ||
50 | "There should be errors in the file {:?}", | ||
51 | path.display() | ||
52 | ); | ||
53 | dump_tree(file.syntax()) | ||
54 | }, | ||
55 | ); | ||
49 | } | 56 | } |
50 | 57 | ||
51 | #[test] | 58 | #[test] |
52 | fn parser_fuzz_tests() { | 59 | fn parser_fuzz_tests() { |
53 | for (_, text) in collect_tests(&["parser/fuzz-failures"]) { | 60 | for (_, text) in collect_tests(&test_data_dir(), &["parser/fuzz-failures"]) { |
54 | check_fuzz_invariants(&text) | 61 | check_fuzz_invariants(&text) |
55 | } | 62 | } |
56 | } | 63 | } |
@@ -92,102 +99,6 @@ fn self_hosting_parsing() { | |||
92 | "self_hosting_parsing found too few files - is it running in the right directory?" | 99 | "self_hosting_parsing found too few files - is it running in the right directory?" |
93 | ) | 100 | ) |
94 | } | 101 | } |
95 | /// Read file and normalize newlines. | ||
96 | /// | ||
97 | /// `rustc` seems to always normalize `\r\n` newlines to `\n`: | ||
98 | /// | ||
99 | /// ``` | ||
100 | /// let s = " | ||
101 | /// "; | ||
102 | /// assert_eq!(s.as_bytes(), &[10]); | ||
103 | /// ``` | ||
104 | /// | ||
105 | /// so this should always be correct. | ||
106 | fn read_text(path: &Path) -> String { | ||
107 | fs::read_to_string(path) | ||
108 | .expect(&format!("File at {:?} should be valid", path)) | ||
109 | .replace("\r\n", "\n") | ||
110 | } | ||
111 | |||
112 | fn dir_tests<F>(paths: &[&str], f: F) | ||
113 | where | ||
114 | F: Fn(&str, &Path) -> String, | ||
115 | { | ||
116 | for (path, input_code) in collect_tests(paths) { | ||
117 | let parse_tree = f(&input_code, &path); | ||
118 | let path = path.with_extension("txt"); | ||
119 | if !path.exists() { | ||
120 | println!("\nfile: {}", path.display()); | ||
121 | println!("No .txt file with expected result, creating...\n"); | ||
122 | println!("{}\n{}", input_code, parse_tree); | ||
123 | fs::write(&path, &parse_tree).unwrap(); | ||
124 | panic!("No expected result") | ||
125 | } | ||
126 | let expected = read_text(&path); | ||
127 | let expected = expected.as_str(); | ||
128 | let parse_tree = parse_tree.as_str(); | ||
129 | assert_equal_text(expected, parse_tree, &path); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | const REWRITE: bool = false; | ||
134 | |||
135 | fn assert_equal_text(expected: &str, actual: &str, path: &Path) { | ||
136 | if expected == actual { | ||
137 | return; | ||
138 | } | ||
139 | let dir = project_dir(); | ||
140 | let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path); | ||
141 | if expected.trim() == actual.trim() { | ||
142 | println!("whitespace difference, rewriting"); | ||
143 | println!("file: {}\n", pretty_path.display()); | ||
144 | fs::write(path, actual).unwrap(); | ||
145 | return; | ||
146 | } | ||
147 | if REWRITE { | ||
148 | println!("rewriting {}", pretty_path.display()); | ||
149 | fs::write(path, actual).unwrap(); | ||
150 | return; | ||
151 | } | ||
152 | assert_eq_text!(expected, actual, "file: {}", pretty_path.display()); | ||
153 | } | ||
154 | |||
155 | fn collect_tests(paths: &[&str]) -> Vec<(PathBuf, String)> { | ||
156 | paths | ||
157 | .iter() | ||
158 | .flat_map(|path| { | ||
159 | let path = test_data_dir().join(path); | ||
160 | test_from_dir(&path).into_iter() | ||
161 | }) | ||
162 | .map(|path| { | ||
163 | let text = read_text(&path); | ||
164 | (path, text) | ||
165 | }) | ||
166 | .collect() | ||
167 | } | ||
168 | |||
169 | fn test_from_dir(dir: &Path) -> Vec<PathBuf> { | ||
170 | let mut acc = Vec::new(); | ||
171 | for file in fs::read_dir(&dir).unwrap() { | ||
172 | let file = file.unwrap(); | ||
173 | let path = file.path(); | ||
174 | if path.extension().unwrap_or_default() == "rs" { | ||
175 | acc.push(path); | ||
176 | } | ||
177 | } | ||
178 | acc.sort(); | ||
179 | acc | ||
180 | } | ||
181 | |||
182 | fn project_dir() -> PathBuf { | ||
183 | let dir = env!("CARGO_MANIFEST_DIR"); | ||
184 | PathBuf::from(dir) | ||
185 | .parent() | ||
186 | .unwrap() | ||
187 | .parent() | ||
188 | .unwrap() | ||
189 | .to_owned() | ||
190 | } | ||
191 | 102 | ||
192 | fn test_data_dir() -> PathBuf { | 103 | fn test_data_dir() -> PathBuf { |
193 | project_dir().join("crates/ra_syntax/tests/data") | 104 | project_dir().join("crates/ra_syntax/tests/data") |