Skip to main content

tree_sitter_cli/fuzz/
corpus_test.rs

1use tree_sitter::{LogType, Node, Parser, Point, Range, Tree};
2
3use super::{LOG_ENABLED, LOG_GRAPH_ENABLED, scope_sequence::ScopeSequence};
4use crate::util;
5
6struct SizeCheckFrame<'a> {
7    node: Node<'a>,
8    end_byte: usize,
9    end_point: Point,
10    child_count: u32,
11    child_index: u32,
12    last_child_end_byte: usize,
13    last_child_end_point: Point,
14    some_child_has_changes: bool,
15    actual_named_child_count: usize,
16}
17
18impl SizeCheckFrame<'_> {
19    fn new<'a>(node: Node<'a>, line_offsets: &[usize]) -> SizeCheckFrame<'a> {
20        let start_byte = node.start_byte();
21        let end_byte = node.end_byte();
22        let start_point = node.start_position();
23        let end_point = node.end_position();
24
25        assert!(start_byte <= end_byte);
26        assert!(start_point <= end_point);
27        assert_eq!(
28            start_byte,
29            line_offsets[start_point.row] + start_point.column
30        );
31        assert_eq!(end_byte, line_offsets[end_point.row] + end_point.column);
32
33        SizeCheckFrame {
34            node,
35            end_byte,
36            end_point,
37            child_count: node.child_count(),
38            child_index: 0,
39            last_child_end_byte: start_byte,
40            last_child_end_point: start_point,
41            some_child_has_changes: false,
42            actual_named_child_count: 0,
43        }
44    }
45}
46
47pub fn check_consistent_sizes(tree: &Tree, input: &[u8]) {
48    let mut line_offsets = vec![0];
49    for (i, c) in input.iter().enumerate() {
50        if *c == b'\n' {
51            line_offsets.push(i + 1);
52        }
53    }
54
55    let mut stack: Vec<SizeCheckFrame> = vec![SizeCheckFrame::new(tree.root_node(), &line_offsets)];
56    while let Some(top) = stack.last_mut() {
57        if top.child_index < top.child_count {
58            let i = top.child_index;
59            let child = top.node.child(i).unwrap();
60
61            assert!(child.start_byte() >= top.last_child_end_byte);
62            assert!(child.start_position() >= top.last_child_end_point);
63            if child.has_changes() {
64                top.some_child_has_changes = true;
65            }
66            if child.is_named() {
67                top.actual_named_child_count += 1;
68            }
69            top.last_child_end_byte = child.end_byte();
70            top.last_child_end_point = child.end_position();
71            top.child_index += 1;
72
73            stack.push(SizeCheckFrame::new(child, &line_offsets));
74            continue;
75        }
76
77        let frame = stack.pop().unwrap();
78        assert_eq!(
79            frame.actual_named_child_count,
80            frame.node.named_child_count()
81        );
82        if frame.child_count > 0 {
83            assert!(frame.end_byte >= frame.last_child_end_byte);
84            assert!(frame.end_point >= frame.last_child_end_point);
85        }
86        if frame.some_child_has_changes {
87            assert!(frame.node.has_changes());
88        }
89    }
90}
91
92pub fn check_changed_ranges(old_tree: &Tree, new_tree: &Tree, input: &[u8]) -> Result<(), String> {
93    let changed_ranges = old_tree.changed_ranges(new_tree).collect::<Vec<_>>();
94    let old_scope_sequence = ScopeSequence::new(old_tree);
95    let new_scope_sequence = ScopeSequence::new(new_tree);
96
97    let old_range = old_tree.root_node().range();
98    let new_range = new_tree.root_node().range();
99
100    let byte_range =
101        old_range.start_byte.min(new_range.start_byte)..old_range.end_byte.max(new_range.end_byte);
102    let point_range = old_range.start_point.min(new_range.start_point)
103        ..old_range.end_point.max(new_range.end_point);
104
105    for range in &changed_ranges {
106        if range.end_byte > byte_range.end || range.end_point > point_range.end {
107            return Err(format!(
108                "changed range extends outside of the old and new trees {range:?}",
109            ));
110        }
111    }
112
113    old_scope_sequence.check_changes(&new_scope_sequence, input, &changed_ranges)
114}
115
116pub fn set_included_ranges(parser: &mut Parser, input: &[u8], delimiters: Option<(&str, &str)>) {
117    if let Some((start, end)) = delimiters {
118        let mut ranges = Vec::new();
119        let mut ix = 0;
120        while ix < input.len() {
121            let Some(mut start_ix) = input[ix..]
122                .windows(2)
123                .position(|win| win == start.as_bytes())
124            else {
125                break;
126            };
127            start_ix += ix + start.len();
128            let end_ix = input[start_ix..]
129                .windows(2)
130                .position(|win| win == end.as_bytes())
131                .map_or(input.len(), |ix| start_ix + ix);
132            ix = end_ix;
133            ranges.push(Range {
134                start_byte: start_ix,
135                end_byte: end_ix,
136                start_point: point_for_offset(input, start_ix),
137                end_point: point_for_offset(input, end_ix),
138            });
139        }
140
141        parser.set_included_ranges(&ranges).unwrap();
142    } else {
143        parser.set_included_ranges(&[]).unwrap();
144    }
145}
146
147fn point_for_offset(text: &[u8], offset: usize) -> Point {
148    let mut point = Point::default();
149    for byte in &text[..offset] {
150        if *byte == b'\n' {
151            point.row += 1;
152            point.column = 0;
153        } else {
154            point.column += 1;
155        }
156    }
157    point
158}
159
160pub fn get_parser(session: &mut Option<util::LogSession>, log_filename: &str) -> Parser {
161    let mut parser = Parser::new();
162
163    if *LOG_ENABLED {
164        parser.set_logger(Some(Box::new(|log_type, msg| {
165            if log_type == LogType::Lex {
166                eprintln!("  {msg}");
167            } else {
168                eprintln!("{msg}");
169            }
170        })));
171    }
172    if *LOG_GRAPH_ENABLED {
173        *session = Some(util::log_graphs(&mut parser, log_filename, false).unwrap());
174    }
175
176    parser
177}