aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/fuzz.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-03-22 05:48:55 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-03-22 05:48:55 +0000
commit2a6544f906818263e2791bc4cdf4fcbdf7260ab9 (patch)
tree12cc178506343e5dbbea0285e1dcd0bd0035398c /crates/ra_syntax/src/fuzz.rs
parented823cb38d6c6852b2645f6bcd4c3b699b4b7539 (diff)
parentbf8e7930daa3fb168106534b1cc418f5bc44e8c0 (diff)
Merge #1013
1013: Fuzz reparsing and fix found bugs r=matklad a=pcpthm Add fuzz test for reparsing which: - Checks reparsing doesn't panic and validate result syntax tree. - Checks that incremental reparsing produces the same syntax tree as full reparse. - Check for that errors are the same as full reparsing is disabled because errors are less important than syntax tree and produce failures which I couldn't figure out how to fix immediately (FIXME comment). I guess the current input generation is inefficient but still found several bugs: - Arithmetic overflow (negative result on an unsigned type). I changed the signature of `SyntaxError::add_offset` to solve this problem. - When reparsing a leaf, the token of the leaf can be joined to the next characters. Such case was not considered. - UNDERSCORE token was not produced when text length is exactly 1 (not a reparsing bug). - When reparsing a block, *inner* curly braces should be balanced. i.e. `{}{}` is invalid. - Effects of deleting newlines were not considered. Co-authored-by: pcpthm <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/fuzz.rs')
-rw-r--r--crates/ra_syntax/src/fuzz.rs67
1 files changed, 67 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs
new file mode 100644
index 000000000..af11b2e1a
--- /dev/null
+++ b/crates/ra_syntax/src/fuzz.rs
@@ -0,0 +1,67 @@
1use crate::{SourceFile, validation, TextUnit, TextRange, AstNode};
2use ra_text_edit::AtomTextEdit;
3use std::str::{self, FromStr};
4
5fn check_file_invariants(file: &SourceFile) {
6 let root = file.syntax();
7 validation::validate_block_structure(root);
8 let _ = file.errors();
9}
10
11pub fn check_parser(text: &str) {
12 let file = SourceFile::parse(text);
13 check_file_invariants(&file);
14}
15
16#[derive(Debug, Clone)]
17pub struct CheckReparse {
18 text: String,
19 edit: AtomTextEdit,
20 edited_text: String,
21}
22
23impl CheckReparse {
24 pub fn from_data(data: &[u8]) -> Option<Self> {
25 const PREFIX: &'static str = "fn main(){\n\t";
26 const SUFFIX: &'static str = "\n}";
27
28 let data = str::from_utf8(data).ok()?;
29 let mut lines = data.lines();
30 let delete_start = usize::from_str(lines.next()?).ok()? + PREFIX.len();
31 let delete_len = usize::from_str(lines.next()?).ok()?;
32 let insert = lines.next()?.to_string();
33 let text = lines.collect::<Vec<_>>().join("\n");
34 let text = format!("{}{}{}", PREFIX, text, SUFFIX);
35 text.get(delete_start..delete_start.checked_add(delete_len)?)?; // make sure delete is a valid range
36 let delete = TextRange::offset_len(
37 TextUnit::from_usize(delete_start),
38 TextUnit::from_usize(delete_len),
39 );
40 let edited_text =
41 format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
42 let edit = AtomTextEdit { delete, insert };
43 Some(CheckReparse { text, edit, edited_text })
44 }
45
46 pub fn run(&self) {
47 let file = SourceFile::parse(&self.text);
48 let new_file = file.reparse(&self.edit);
49 check_file_invariants(&new_file);
50 assert_eq!(&new_file.syntax().text().to_string(), &self.edited_text);
51 let full_reparse = SourceFile::parse(&self.edited_text);
52 for (a, b) in new_file.syntax().descendants().zip(full_reparse.syntax().descendants()) {
53 if (a.kind(), a.range()) != (b.kind(), b.range()) {
54 eprint!("original:\n{}", file.syntax().debug_dump());
55 eprint!("reparsed:\n{}", new_file.syntax().debug_dump());
56 eprint!("full reparse:\n{}", full_reparse.syntax().debug_dump());
57 assert_eq!(
58 format!("{:?}", a),
59 format!("{:?}", b),
60 "different syntax tree produced by the full reparse"
61 );
62 }
63 }
64 // FIXME
65 // assert_eq!(new_file.errors(), full_reparse.errors());
66 }
67}