Mark query_captures function as unsafe

It's easy to mistakenly use-after-free the cursor and captures iterator
here because of the transmute. Ideally this could be fixed upstream in
tree-sitter by introducing an API with lifetimes/types that reflect the
lifetimes of the underlying data.

Co-authored-by: Pascal Kuthe <pascal.kuthe@semimod.de>
pull/9320/head
Michael Davis 2 years ago
parent 5de7f7613b
commit 12e7d126b6
No known key found for this signature in database

@ -999,7 +999,8 @@ thread_local! {
/// Creates an iterator over the captures in a query within the given range, /// Creates an iterator over the captures in a query within the given range,
/// re-using a cursor from the pool if available. /// re-using a cursor from the pool if available.
fn query_captures<'a, 'tree>( /// SAFETY: The `QueryCaptures` must be droped before the `QueryCursor` is dropped.
unsafe fn query_captures<'a, 'tree>(
query: &'a Query, query: &'a Query,
root: Node<'tree>, root: Node<'tree>,
source: RopeSlice<'a>, source: RopeSlice<'a>,
@ -1014,10 +1015,11 @@ fn query_captures<'a, 'tree>(
highlighter.cursors.pop().unwrap_or_else(QueryCursor::new) highlighter.cursors.pop().unwrap_or_else(QueryCursor::new)
}); });
// This is the unsafe line:
// The `captures` iterator borrows the `Tree` and the `QueryCursor`, which // The `captures` iterator borrows the `Tree` and the `QueryCursor`, which
// prevents them from being moved. But both of these values are really just // prevents them from being moved. But both of these values are really just
// pointers, so it's actually ok to move them. // pointers, so it's actually ok to move them.
let cursor_ref = unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) }; let cursor_ref = mem::transmute::<_, &'static mut QueryCursor>(&mut cursor);
// if reusing cursors & no range this resets to whole range // if reusing cursors & no range this resets to whole range
cursor_ref.set_byte_range(range.unwrap_or(0..usize::MAX)); cursor_ref.set_byte_range(range.unwrap_or(0..usize::MAX));
@ -1376,12 +1378,10 @@ impl Syntax {
.layers .layers
.iter() .iter()
.filter_map(|(_, layer)| { .filter_map(|(_, layer)| {
let (cursor, captures) = query_captures( let query = query_fn(&layer.config)?;
query_fn(&layer.config), let (cursor, captures) = unsafe {
layer.tree().root_node(), query_captures(query, layer.tree().root_node(), source, range.clone())
source, };
range.clone(),
);
let mut captures = captures.peekable(); let mut captures = captures.peekable();
// If there aren't any captures for this layer, skip the layer. // If there aren't any captures for this layer, skip the layer.
@ -1413,12 +1413,14 @@ impl Syntax {
.filter_map(|(_, layer)| { .filter_map(|(_, layer)| {
// TODO: if range doesn't overlap layer range, skip it // TODO: if range doesn't overlap layer range, skip it
let (cursor, captures) = query_captures( let (cursor, captures) = unsafe {
&layer.config.query, query_captures(
layer.tree().root_node(), &layer.config.query,
source, layer.tree().root_node(),
range.clone(), source,
); range.clone(),
)
};
let mut captures = captures.peekable(); let mut captures = captures.peekable();
// If there are no captures, skip the layer // If there are no captures, skip the layer

Loading…
Cancel
Save