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/test_utils/src/lib.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/test_utils/src/lib.rs')
-rw-r--r-- | crates/test_utils/src/lib.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index beb936c61..012b1d0b4 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | use std::fs; | ||
3 | use std::path::{Path, PathBuf}; | ||
2 | 4 | ||
3 | use itertools::Itertools; | 5 | use itertools::Itertools; |
4 | use text_unit::{TextRange, TextUnit}; | 6 | use text_unit::{TextRange, TextUnit}; |
@@ -262,3 +264,100 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a | |||
262 | _ => Some((expected, actual)), | 264 | _ => Some((expected, actual)), |
263 | } | 265 | } |
264 | } | 266 | } |
267 | |||
268 | pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], f: F) | ||
269 | where | ||
270 | F: Fn(&str, &Path) -> String, | ||
271 | { | ||
272 | for (path, input_code) in collect_tests(test_data_dir, paths) { | ||
273 | let parse_tree = f(&input_code, &path); | ||
274 | let path = path.with_extension("txt"); | ||
275 | if !path.exists() { | ||
276 | println!("\nfile: {}", path.display()); | ||
277 | println!("No .txt file with expected result, creating...\n"); | ||
278 | println!("{}\n{}", input_code, parse_tree); | ||
279 | fs::write(&path, &parse_tree).unwrap(); | ||
280 | panic!("No expected result") | ||
281 | } | ||
282 | let expected = read_text(&path); | ||
283 | let expected = expected.as_str(); | ||
284 | let parse_tree = parse_tree.as_str(); | ||
285 | assert_equal_text(expected, parse_tree, &path); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { | ||
290 | paths | ||
291 | .iter() | ||
292 | .flat_map(|path| { | ||
293 | let path = test_data_dir.to_owned().join(path); | ||
294 | test_from_dir(&path).into_iter() | ||
295 | }) | ||
296 | .map(|path| { | ||
297 | let text = read_text(&path); | ||
298 | (path, text) | ||
299 | }) | ||
300 | .collect() | ||
301 | } | ||
302 | |||
303 | fn test_from_dir(dir: &Path) -> Vec<PathBuf> { | ||
304 | let mut acc = Vec::new(); | ||
305 | for file in fs::read_dir(&dir).unwrap() { | ||
306 | let file = file.unwrap(); | ||
307 | let path = file.path(); | ||
308 | if path.extension().unwrap_or_default() == "rs" { | ||
309 | acc.push(path); | ||
310 | } | ||
311 | } | ||
312 | acc.sort(); | ||
313 | acc | ||
314 | } | ||
315 | |||
316 | pub fn project_dir() -> PathBuf { | ||
317 | let dir = env!("CARGO_MANIFEST_DIR"); | ||
318 | PathBuf::from(dir) | ||
319 | .parent() | ||
320 | .unwrap() | ||
321 | .parent() | ||
322 | .unwrap() | ||
323 | .to_owned() | ||
324 | } | ||
325 | |||
326 | /// Read file and normalize newlines. | ||
327 | /// | ||
328 | /// `rustc` seems to always normalize `\r\n` newlines to `\n`: | ||
329 | /// | ||
330 | /// ``` | ||
331 | /// let s = " | ||
332 | /// "; | ||
333 | /// assert_eq!(s.as_bytes(), &[10]); | ||
334 | /// ``` | ||
335 | /// | ||
336 | /// so this should always be correct. | ||
337 | pub fn read_text(path: &Path) -> String { | ||
338 | fs::read_to_string(path) | ||
339 | .expect(&format!("File at {:?} should be valid", path)) | ||
340 | .replace("\r\n", "\n") | ||
341 | } | ||
342 | |||
343 | const REWRITE: bool = false; | ||
344 | |||
345 | fn assert_equal_text(expected: &str, actual: &str, path: &Path) { | ||
346 | if expected == actual { | ||
347 | return; | ||
348 | } | ||
349 | let dir = project_dir(); | ||
350 | let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path); | ||
351 | if expected.trim() == actual.trim() { | ||
352 | println!("whitespace difference, rewriting"); | ||
353 | println!("file: {}\n", pretty_path.display()); | ||
354 | fs::write(path, actual).unwrap(); | ||
355 | return; | ||
356 | } | ||
357 | if REWRITE { | ||
358 | println!("rewriting {}", pretty_path.display()); | ||
359 | fs::write(path, actual).unwrap(); | ||
360 | return; | ||
361 | } | ||
362 | assert_eq_text!(expected, actual, "file: {}", pretty_path.display()); | ||
363 | } | ||