diff options
Diffstat (limited to 'crates/syntax/src/fuzz.rs')
-rw-r--r-- | crates/syntax/src/fuzz.rs | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/crates/syntax/src/fuzz.rs b/crates/syntax/src/fuzz.rs new file mode 100644 index 000000000..fbb97aa27 --- /dev/null +++ b/crates/syntax/src/fuzz.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::{ | ||
4 | convert::TryInto, | ||
5 | str::{self, FromStr}, | ||
6 | }; | ||
7 | |||
8 | use text_edit::Indel; | ||
9 | |||
10 | use crate::{validation, AstNode, SourceFile, TextRange}; | ||
11 | |||
12 | fn check_file_invariants(file: &SourceFile) { | ||
13 | let root = file.syntax(); | ||
14 | validation::validate_block_structure(root); | ||
15 | } | ||
16 | |||
17 | pub fn check_parser(text: &str) { | ||
18 | let file = SourceFile::parse(text); | ||
19 | check_file_invariants(&file.tree()); | ||
20 | } | ||
21 | |||
22 | #[derive(Debug, Clone)] | ||
23 | pub struct CheckReparse { | ||
24 | text: String, | ||
25 | edit: Indel, | ||
26 | edited_text: String, | ||
27 | } | ||
28 | |||
29 | impl CheckReparse { | ||
30 | pub fn from_data(data: &[u8]) -> Option<Self> { | ||
31 | const PREFIX: &str = "fn main(){\n\t"; | ||
32 | const SUFFIX: &str = "\n}"; | ||
33 | |||
34 | let data = str::from_utf8(data).ok()?; | ||
35 | let mut lines = data.lines(); | ||
36 | let delete_start = usize::from_str(lines.next()?).ok()? + PREFIX.len(); | ||
37 | let delete_len = usize::from_str(lines.next()?).ok()?; | ||
38 | let insert = lines.next()?.to_string(); | ||
39 | let text = lines.collect::<Vec<_>>().join("\n"); | ||
40 | let text = format!("{}{}{}", PREFIX, text, SUFFIX); | ||
41 | text.get(delete_start..delete_start.checked_add(delete_len)?)?; // make sure delete is a valid range | ||
42 | let delete = | ||
43 | TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap()); | ||
44 | let edited_text = | ||
45 | format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]); | ||
46 | let edit = Indel { delete, insert }; | ||
47 | Some(CheckReparse { text, edit, edited_text }) | ||
48 | } | ||
49 | |||
50 | pub fn run(&self) { | ||
51 | let parse = SourceFile::parse(&self.text); | ||
52 | let new_parse = parse.reparse(&self.edit); | ||
53 | check_file_invariants(&new_parse.tree()); | ||
54 | assert_eq!(&new_parse.tree().syntax().text().to_string(), &self.edited_text); | ||
55 | let full_reparse = SourceFile::parse(&self.edited_text); | ||
56 | for (a, b) in | ||
57 | new_parse.tree().syntax().descendants().zip(full_reparse.tree().syntax().descendants()) | ||
58 | { | ||
59 | if (a.kind(), a.text_range()) != (b.kind(), b.text_range()) { | ||
60 | eprint!("original:\n{:#?}", parse.tree().syntax()); | ||
61 | eprint!("reparsed:\n{:#?}", new_parse.tree().syntax()); | ||
62 | eprint!("full reparse:\n{:#?}", full_reparse.tree().syntax()); | ||
63 | assert_eq!( | ||
64 | format!("{:?}", a), | ||
65 | format!("{:?}", b), | ||
66 | "different syntax tree produced by the full reparse" | ||
67 | ); | ||
68 | } | ||
69 | } | ||
70 | // FIXME | ||
71 | // assert_eq!(new_file.errors(), full_reparse.errors()); | ||
72 | } | ||
73 | } | ||