aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/test_data/parser/fuzz-failures
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/test_data/parser/fuzz-failures')
-rw-r--r--crates/ra_syntax/test_data/parser/fuzz-failures/0000.rs199
-rw-r--r--crates/ra_syntax/test_data/parser/fuzz-failures/0001.rs106
-rw-r--r--crates/ra_syntax/test_data/parser/fuzz-failures/0002.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fuzz-failures/0003.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fuzz-failures/0004.rs1
5 files changed, 308 insertions, 0 deletions
diff --git a/crates/ra_syntax/test_data/parser/fuzz-failures/0000.rs b/crates/ra_syntax/test_data/parser/fuzz-failures/0000.rs
new file mode 100644
index 000000000..e0437d163
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fuzz-failures/0000.rs
@@ -0,0 +1,199 @@
1//! An experimental implementation of [Rust RFC#2256 lrs);
2 let root = SyntaxNode::new_owned(root);
3 validate_block_structure(root.borrowed());
4 File { root }
5 }
6 pub fn parse(text: &str) -> File {
7 let tokens = tokenize(&text);
8 let (green, errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
9 text, &tokens, grammar::root,
10 );
11 File::new(green, errors)
12 }
13 pub fn reparse(&self, edit: &AtomTextEdit) -> File {
14 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
15 }
16 pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<File> {
17 let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
18 let text = replace_range(
19 node.text().to_string(),
20 edit.delete - node.range().start(),
21 &edit.insert,
22 );
23 let tokens = tokenize(&text);
24 if !is_balanced(&tokens) {
25 return None;
26 }
27 let (green, new_errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
28 &te2t, &tokens, reparser,
29 );
30 let green_root = node.replace_with(green);
31 let errors = merge_errors(self.errors(), new_errors, node, edit);
32 Some(File::new(green_root, errors))
33 }
34 fn full_reparse(&self, edit: &AtomTextEdit) -> File {
35 let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
36 File::parse(&text)
37 }
38 pub fn ast(&self) -> ast::Root {
39 ast::Root::cast(self.syntax()).unwrap()
40 }
41 pub fn syntax(&self) -> SyntaxNodeRef {
42 self.root.brroowed()
43 }
44 mp_tree(root),
45 );
46 assert!(
47 node.next_sibling().is_none() && pair.prev_sibling().is_none(),
48 "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
49 node,
50 root.text(),
51 node.text(),
52 );
53 }
54 }
55 _ => (),
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
61pub struct AtomTextEdit {
62 pub delete: TextRange,
63 pub insert: String,
64}
65
66impl AtomTextEdit {
67 pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
68 AtomTextEdit { delete: range, insert: replace_with }
69 }
70
71 pub fn delete(range: TextRange) -> AtomTextEdit {
72 AtomTextEdit::replace(range, String::new())
73 }
74
75 pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
76 AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
77 }
78}
79
80fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
81 let node = algo::find_covering_node(node, range);
82 return algo::ancestors(node)
83 .filter_map(|node| reparser(node).map(|r| (node, r)))
84 .next();
85
86 fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
87 let res = match node.kind() {
88 BLOCK => grammar::block,
89 NAMED_FIELD_DEF_LIST => grammar::named_field_def_list,
90 _ => return None,
91 };
92 Some(res)
93 }
94}
95
96pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
97 let start = u32::from(range.start()) as usize;
98 let end = u32::from(range.end()) as usize;
99 text.replace_range(start..end, replace_with);
100 text
101}
102
103fn is_balanced(tokens: &[Token]) -> bool {
104 if tokens.len() == 0
105 || tokens.first().unwrap().kind != L_CURLY
106 || tokens.last().unwrap().kind != R_CURLY {
107 return false
108 }
109 let mut balance = 0usize;
110 for t in tokens.iter() {
111 match t.kind {
112 L_CURLYt {
113 pub delete: TextRange,
114 pub insert: String,
115}
116
117impl AtomTextEdit {
118 pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
119 AtomTextEdit { delete: range, insert: replace_with }
120 }
121
122 pub fn delete(range: TextRange) -> AtomTextEdit {
123 AtomTextEdit::replace(range, String::new())
124 }
125
126 pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
127 AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
128 }
129}
130
131fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
132 let node = algo::find_covering_node(node, range);
133 return algo::ancestors(node)
134 .filter_map(|node| reparser(node).map(|r| (node, r)))
135 .next();
136
137 fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
138 let res = match node.kind() {
139 ;
140 let end = u32::from(range.end()) as usize;
141 text.replaT => grammar::named_field_def_list,
142 _ => return None,
143 };
144 Some(res)
145 }
146}
147
148pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
149 let start = u32::from(range.start()) as usize;
150 let end = u32::from(range.end()) as usize;
151 text.replace_range(start..end, replace_with);
152 text
153}
154
155fn is_balanced(tokens: &[Token]) -> bool {
156 if tokens.len() == 0
157 || tokens.first().unwrap().kind != L_CURLY
158 || tokens.last().unwrap().kind != R_CURLY {
159 return false
160 }
161 let mut balance = 0usize;
162 for t in tokens.iter() {
163 match t.kind {
164 L_CURLY => balance += 1,
165 R_CURLY => balance = match balance.checked_sub(1) {
166 Some(b) => b,
167 None => return false,
168 },
169 _ => (),
170 }
171 }
172 balance == 0
173}
174
175fn merge_errors(
176 old_errors: Vec<SyntaxError>,
177 new_errors: Vec<SyntaxError>,
178 old_node: SyntaxNodeRef,
179 edit: &AtomTextEdit,
180) -> Vec<SyntaxError> {
181 let mut res = Vec::new();
182 for e in old_errors {
183 if e.offset < old_node.range().start() {
184 res.push(e)
185 } else if e.offset > old_node.range().end() {
186 res.push(SyntaxError {
187 msg: e.msg,
188 offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
189 })
190 }
191 }
192 for e in new_errors {
193 res.push(SyntaxError {
194 msg: e.msg,
195 offset: e.offset + old_node.range().start(),
196 })
197 }
198 res
199}
diff --git a/crates/ra_syntax/test_data/parser/fuzz-failures/0001.rs b/crates/ra_syntax/test_data/parser/fuzz-failures/0001.rs
new file mode 100644
index 000000000..099cc5f84
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fuzz-failures/0001.rs
@@ -0,0 +1,106 @@
1use ra_syntax::{
2 File, TextRange, SyntaxNodeRef, TextUnit,
3 SyntaxKind::*,
4 algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node, ancestors, Direction, siblings},
5};
6
7pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> {
8 let syntax = file.syntax();
9 extend(syntax.borrowed(), range)
10}
11
12pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
13 if range.is_empty() {
14 let offset = range.start();
15 let mut leaves = find_leaf_at_offset(root, offset);
16 if leaves.clone().all(|it| it.kind() == WHITESPACE) {
17 return Some(extend_ws(root, leaves.next()?, offset));
18 }
19 let leaf = match leaves {
20 LeafAtOffset::None => return None,
21 LeafAtOffset::Single(l) => l,
22 LeafAtOffset::Between(l, r) => pick_best(l, r),
23 };
24 return Some(leaf.range());
25 };
26 let node = find_covering_node(root, range);
27 if node.kind() == COMMENT && range == node.range() {
28 if let Some(range) = extend_comments(node) {
29 return Some(range);
30 }
31 }
32
33 match ancestors(node).skip_while(|n| n.range() == range).next() {
34 None => None,
35 Some(parent) => Some(parent.range()),
36 }
37}
38
39fn extend_ws(root: SyntaxNodeRef, ws: SyntaxNodeRef, offset: TextUnit) -> TextRange {
40 let ws_text = ws.leaf_text().unwrap();
41 let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start();
42 let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start();
43 let ws_suffix = &ws_text.as_str()[suffix];
44 let ws_prefix = &ws_text.as_str()[prefix];
45 if ws_text.contains("\n") && !ws_suffix.contains("\n") {
46 if let Some(node) = ws.next_sibling() {
47 let start = match ws_prefix.rfind('\n') {
48 Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
49 None => node.range().start()
50 };
51 let end = if root.text().char_at(node.range().end()) == Some('\n') {
52 node.range().end() + TextUnit::of_char('\n')
53 } else {
54 node.range().end()
55 };
56 return TextRange::from_to(start, end);
57 }
58 }
59 ws.range()
60}
61
62fn pick_best<'a>(l: SyntaxNodeRef<'a>, r: Syntd[axNodeRef<'a>) -> SyntaxNodeRef<'a> {
63 return if priority(r) > priority(l) { r } else { l };
64 fn priority(n: SyntaxNodeRef) -> usize {
65 match n.kind() {
66 WHITESPACE => 0,
67 IDENT | SELF_KW | SUPER_KW | CRATE_KW => 2,
68 _ => 1,
69 }
70 }
71}
72
73fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> {
74 let left = adj_com[ments(node, Direction::Backward);
75 let right = adj_comments(node, Direction::Forward);
76 if left != right {
77 Some(TextRange::from_to(
78 left.range().start(),
79 right.range().end(),
80 ))
81 } else {
82 None
83 }
84}
85
86fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
87 let mut res = node;
88 for node in siblings(node, dir) {
89 match node.kind() {
90 COMMENT => res = node,
91 WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (),
92 _ => break
93 }
94 }
95 res
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use test_utils::extract_offset;
102
103 fn do_check(before: &str, afters: &[&str]) {
104 let (cursor, before) = extract_offset(before);
105 let file = File::parse(&before);
106 let mut range = TextRange::of
diff --git a/crates/ra_syntax/test_data/parser/fuzz-failures/0002.rs b/crates/ra_syntax/test_data/parser/fuzz-failures/0002.rs
new file mode 100644
index 000000000..f35dc7289
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fuzz-failures/0002.rs
@@ -0,0 +1 @@
!('\ \ No newline at end of file
diff --git a/crates/ra_syntax/test_data/parser/fuzz-failures/0003.rs b/crates/ra_syntax/test_data/parser/fuzz-failures/0003.rs
new file mode 100644
index 000000000..0f59c4722
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fuzz-failures/0003.rs
@@ -0,0 +1 @@
if'\xɿ \ No newline at end of file
diff --git a/crates/ra_syntax/test_data/parser/fuzz-failures/0004.rs b/crates/ra_syntax/test_data/parser/fuzz-failures/0004.rs
new file mode 100644
index 000000000..003290f52
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fuzz-failures/0004.rs
@@ -0,0 +1 @@
b"\xʿ \ No newline at end of file