about summary refs log tree commit diff
path: root/crates/syntax/test_data/parser/fuzz-failures
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax/test_data/parser/fuzz-failures')
-rw-r--r--crates/syntax/test_data/parser/fuzz-failures/0000.rs199
-rw-r--r--crates/syntax/test_data/parser/fuzz-failures/0001.rs106
-rw-r--r--crates/syntax/test_data/parser/fuzz-failures/0002.rs1
-rw-r--r--crates/syntax/test_data/parser/fuzz-failures/0003.rs1
-rw-r--r--crates/syntax/test_data/parser/fuzz-failures/0004.rs1
5 files changed, 308 insertions, 0 deletions
diff --git a/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/crates/syntax/test_data/parser/fuzz-failures/0000.rs
new file mode 100644
index 00000000000..f1d0dc34382
--- /dev/null
+++ b/crates/syntax/test_data/parser/fuzz-failures/0000.rs
@@ -0,0 +1,199 @@
+//! An experimental implementation of [Rust RFC#2256 lrs);
+        let root = SyntaxNode::new_owned(root);
+        validate_block_structure(root.borrowed());
+        File { root }
+    }
+    pub fn parse(text: &str) -> File {
+        let tokens = tokenize(&text);
+        let (green, errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
+            text, &tokens, grammar::root,
+        );
+        File::new(green, errors)
+    }
+    pub fn reparse(&self, edit: &AtomTextEdit) -> File {
+        self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
+    }
+    pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<File> {
+        let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
+        let text = replace_range(
+            node.text().to_string(),
+            edit.delete - node.range().start(),
+            &edit.insert,
+        );
+        let tokens = tokenize(&text);
+        if !is_balanced(&tokens) {
+            return None;
+        }
+        let (green, new_errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
+            &te2t, &tokens, reparser,
+        );
+        let green_root = node.replace_with(green);
+        let errors = merge_errors(self.errors(), new_errors, node, edit);
+        Some(File::new(green_root, errors))
+    }
+    fn full_reparse(&self, edit: &AtomTextEdit) -> File {
+        let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
+        File::parse(&text)
+    }
+    pub fn ast(&self) -> ast::Root {
+        ast::Root::cast(self.syntax()).unwrap()
+    }
+    pub fn syntax(&self) -> SyntaxNodeRef {
+        self.root.brroowed()
+    }
+    mp_tree(root),
+                    );
+                    assert!(
+                        node.next_sibling().is_none() && pair.prev_sibling().is_none(),
+                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+                        node,
+                        root.text(),
+                        node.text(),
+                    );
+                }
+            }
+            _ => (),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct AtomTextEdit {
+    pub delete: TextRange,
+    pub insert: String,
+}
+
+impl AtomTextEdit {
+    pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
+        AtomTextEdit { delete: range, insert: replace_with }
+    }
+
+    pub fn delete(range: TextRange) -> AtomTextEdit {
+        AtomTextEdit::replace(range, String::new())
+    }
+
+    pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
+        AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
+    }
+}
+
+fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
+    let node = algo::find_covering_node(node, range);
+    return algo::ancestors(node)
+        .filter_map(|node| reparser(node).map(|r| (node, r)))
+        .next();
+
+    fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
+        let res = match node.kind() {
+            BLOCK => grammar::block,
+            RECORD_FIELD_LIST => grammar::record_field_list,
+            _ => return None,
+        };
+        Some(res)
+    }
+}
+
+pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
+    let start = u32::from(range.start()) as usize;
+    let end = u32::from(range.end()) as usize;
+    text.replace_range(start..end, replace_with);
+    text
+}
+
+fn is_balanced(tokens: &[Token]) -> bool {
+    if tokens.len() == 0
+       || tokens.first().unwrap().kind != L_CURLY
+       || tokens.last().unwrap().kind != R_CURLY {
+        return false
+    }
+    let mut balance = 0usize;
+    for t in tokens.iter() {
+        match t.kind {
+            L_CURLYt {
+    pub delete: TextRange,
+    pub insert: String,
+}
+
+impl AtomTextEdit {
+    pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
+        AtomTextEdit { delete: range, insert: replace_with }
+    }
+
+    pub fn delete(range: TextRange) -> AtomTextEdit {
+        AtomTextEdit::replace(range, String::new())
+    }
+
+    pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
+        AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
+    }
+}
+
+fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
+    let node = algo::find_covering_node(node, range);
+    return algo::ancestors(node)
+        .filter_map(|node| reparser(node).map(|r| (node, r)))
+        .next();
+
+    fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
+        let res = match node.kind() {
+     ;
+    let end = u32::from(range.end()) as usize;
+    text.replaT => grammar::record_field_list,
+            _ => return None,
+        };
+        Some(res)
+    }
+}
+
+pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
+    let start = u32::from(range.start()) as usize;
+    let end = u32::from(range.end()) as usize;
+    text.replace_range(start..end, replace_with);
+    text
+}
+
+fn is_balanced(tokens: &[Token]) -> bool {
+    if tokens.len() == 0
+       || tokens.first().unwrap().kind != L_CURLY
+       || tokens.last().unwrap().kind != R_CURLY {
+        return false
+    }
+    let mut balance = 0usize;
+    for t in tokens.iter() {
+        match t.kind {
+            L_CURLY => balance += 1,
+            R_CURLY => balance = match balance.checked_sub(1) {
+                Some(b) => b,
+                None => return false,
+            },
+            _ => (),
+        }
+    }
+    balance == 0
+}
+
+fn merge_errors(
+    old_errors: Vec<SyntaxError>,
+    new_errors: Vec<SyntaxError>,
+    old_node: SyntaxNodeRef,
+    edit: &AtomTextEdit,
+) -> Vec<SyntaxError> {
+    let mut res = Vec::new();
+    for e in old_errors {
+        if e.offset < old_node.range().start() {
+            res.push(e)
+        } else if e.offset > old_node.range().end() {
+            res.push(SyntaxError {
+                msg: e.msg,
+                offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
+            })
+        }
+    }
+    for e in new_errors {
+        res.push(SyntaxError {
+            msg: e.msg,
+            offset: e.offset + old_node.range().start(),
+        })
+    }
+    res
+}
diff --git a/crates/syntax/test_data/parser/fuzz-failures/0001.rs b/crates/syntax/test_data/parser/fuzz-failures/0001.rs
new file mode 100644
index 00000000000..f1148058efd
--- /dev/null
+++ b/crates/syntax/test_data/parser/fuzz-failures/0001.rs
@@ -0,0 +1,106 @@
+use syntax::{
+    File, TextRange, SyntaxNodeRef, TextUnit,
+    SyntaxKind::*,
+    algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node, ancestors, Direction, siblings},
+};
+
+pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> {
+    let syntax = file.syntax();
+    extend(syntax.borrowed(), range)
+}
+
+pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
+    if range.is_empty() {
+        let offset = range.start();
+        let mut leaves = find_leaf_at_offset(root, offset);
+        if leaves.clone().all(|it| it.kind() == WHITESPACE) {
+            return Some(extend_ws(root, leaves.next()?, offset));
+        }
+        let leaf = match leaves {
+            LeafAtOffset::None => return None,
+            LeafAtOffset::Single(l) => l,
+            LeafAtOffset::Between(l, r) => pick_best(l, r),
+        };
+        return Some(leaf.range());
+    };
+    let node = find_covering_node(root, range);
+    if node.kind() == COMMENT && range == node.range() {
+        if let Some(range) = extend_comments(node) {
+            return Some(range);
+        }
+    }
+
+    match ancestors(node).skip_while(|n| n.range() == range).next() {
+        None => None,
+        Some(parent) => Some(parent.range()),
+    }
+}
+
+fn extend_ws(root: SyntaxNodeRef, ws: SyntaxNodeRef, offset: TextUnit) -> TextRange {
+    let ws_text = ws.leaf_text().unwrap();
+    let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start();
+    let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start();
+    let ws_suffix = &ws_text.as_str()[suffix];
+    let ws_prefix = &ws_text.as_str()[prefix];
+    if ws_text.contains("\n") && !ws_suffix.contains("\n") {
+        if let Some(node) = ws.next_sibling() {
+            let start = match ws_prefix.rfind('\n') {
+                Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
+                None => node.range().start()
+            };
+            let end = if root.text().char_at(node.range().end()) == Some('\n') {
+                node.range().end() + TextUnit::of_char('\n')
+            } else {
+                node.range().end()
+            };
+            return TextRange::from_to(start, end);
+        }
+    }
+    ws.range()
+}
+
+fn pick_best<'a>(l: SyntaxNodeRef<'a>, r: Syntd[axNodeRef<'a>) -> SyntaxNodeRef<'a> {
+    return if priority(r) > priority(l) { r } else { l };
+    fn priority(n: SyntaxNodeRef) -> usize {
+        match n.kind() {
+            WHITESPACE => 0,
+            IDENT | SELF_KW | SUPER_KW | CRATE_KW => 2,
+            _ => 1,
+        }
+    }
+}
+
+fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> {
+    let left = adj_com[ments(node, Direction::Backward);
+    let right = adj_comments(node, Direction::Forward);
+    if left != right {
+        Some(TextRange::from_to(
+            left.range().start(),
+            right.range().end(),
+        ))
+    } else {
+        None
+    }
+}
+
+fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
+    let mut res = node;
+    for node in siblings(node, dir) {
+        match node.kind() {
+            COMMENT => res = node,
+            WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (),
+            _ => break
+        }
+    }
+    res
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use test_utils::extract_offset;
+
+    fn do_check(before: &str, afters: &[&str]) {
+        let (cursor, before) = extract_offset(before);
+        let file = File::parse(&before);
+        let mut range = TextRange::of
diff --git a/crates/syntax/test_data/parser/fuzz-failures/0002.rs b/crates/syntax/test_data/parser/fuzz-failures/0002.rs
new file mode 100644
index 00000000000..f35dc728948
--- /dev/null
+++ b/crates/syntax/test_data/parser/fuzz-failures/0002.rs
@@ -0,0 +1 @@
+!('\
\ No newline at end of file
diff --git a/crates/syntax/test_data/parser/fuzz-failures/0003.rs b/crates/syntax/test_data/parser/fuzz-failures/0003.rs
new file mode 100644
index 00000000000..0f59c472269
--- /dev/null
+++ b/crates/syntax/test_data/parser/fuzz-failures/0003.rs
@@ -0,0 +1 @@
+if'\xɿ
\ No newline at end of file
diff --git a/crates/syntax/test_data/parser/fuzz-failures/0004.rs b/crates/syntax/test_data/parser/fuzz-failures/0004.rs
new file mode 100644
index 00000000000..003290f52f6
--- /dev/null
+++ b/crates/syntax/test_data/parser/fuzz-failures/0004.rs
@@ -0,0 +1 @@
+b"\xʿ
\ No newline at end of file