aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax
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_syntax
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_syntax')
-rw-r--r--crates/ra_syntax/src/ast/generated.rs60
-rw-r--r--crates/ra_syntax/src/grammar.ron19
-rw-r--r--crates/ra_syntax/tests/test.rs157
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
526impl<'a> CastExpr<'a> {} 526impl<'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
2397impl<'a> ParenExpr<'a> {} 2413impl<'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
2684impl<'a> PathType<'a> {} 2704impl<'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
2832impl<'a> PrefixExpr<'a> {} 2856impl<'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
2943impl<'a> RefExpr<'a> {} 2971impl<'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
3054impl<'a> RetType<'a> {} 3086impl<'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
3091impl<'a> ReturnExpr<'a> {} 3127impl<'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
3581impl<'a> TryExpr<'a> {} 3621impl<'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 @@
1extern crate ra_syntax; 1extern crate ra_syntax;
2#[macro_use]
3extern crate test_utils; 2extern crate test_utils;
4extern crate walkdir; 3extern crate walkdir;
5 4
6use std::{ 5use std::{
7 fmt::Write, 6 fmt::Write,
8 fs, 7 path::{PathBuf, Component},
9 path::{Path, PathBuf, Component},
10}; 8};
11 9
10use test_utils::{project_dir, dir_tests, read_text, collect_tests};
12use ra_syntax::{ 11use 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]
18fn lexer_tests() { 17fn 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]
26fn parser_tests() { 25fn 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]
52fn parser_fuzz_tests() { 59fn 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.
106fn 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
112fn dir_tests<F>(paths: &[&str], f: F)
113where
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
133const REWRITE: bool = false;
134
135fn 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
155fn 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
169fn 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
182fn 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
192fn test_data_dir() -> PathBuf { 103fn test_data_dir() -> PathBuf {
193 project_dir().join("crates/ra_syntax/tests/data") 104 project_dir().join("crates/ra_syntax/tests/data")