feat!: implement StreamingIterator instead of Iterator for QueryMatches and QueryCaptures
This fixes UB when either `QueryMatches` or `QueryCaptures` had collect called on it. Co-authored-by: Amaan Qureshi <amaanq12@gmail.com>
This commit is contained in:
parent
12007d3ebe
commit
6b1ebd3d29
14 changed files with 271 additions and 105 deletions
|
|
@ -27,6 +27,7 @@ use std::os::fd::AsRawFd;
|
|||
#[cfg(all(windows, feature = "std"))]
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
|
||||
use streaming_iterator::{StreamingIterator, StreamingIteratorMut};
|
||||
use tree_sitter_language::LanguageFn;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
|
|
@ -201,23 +202,25 @@ pub struct QueryMatch<'cursor, 'tree> {
|
|||
}
|
||||
|
||||
/// A sequence of [`QueryMatch`]es associated with a given [`QueryCursor`].
|
||||
pub struct QueryMatches<'query, 'cursor, T: TextProvider<I>, I: AsRef<[u8]>> {
|
||||
pub struct QueryMatches<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> {
|
||||
ptr: *mut ffi::TSQueryCursor,
|
||||
query: &'query Query,
|
||||
text_provider: T,
|
||||
buffer1: Vec<u8>,
|
||||
buffer2: Vec<u8>,
|
||||
_phantom: PhantomData<(&'cursor (), I)>,
|
||||
current_match: Option<QueryMatch<'query, 'tree>>,
|
||||
_phantom: PhantomData<(&'tree (), I)>,
|
||||
}
|
||||
|
||||
/// A sequence of [`QueryCapture`]s associated with a given [`QueryCursor`].
|
||||
pub struct QueryCaptures<'query, 'cursor, T: TextProvider<I>, I: AsRef<[u8]>> {
|
||||
pub struct QueryCaptures<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> {
|
||||
ptr: *mut ffi::TSQueryCursor,
|
||||
query: &'query Query,
|
||||
text_provider: T,
|
||||
buffer1: Vec<u8>,
|
||||
buffer2: Vec<u8>,
|
||||
_phantom: PhantomData<(&'cursor (), I)>,
|
||||
current_match: Option<(QueryMatch<'query, 'tree>, usize)>,
|
||||
_phantom: PhantomData<(&'tree (), I)>,
|
||||
}
|
||||
|
||||
pub trait TextProvider<I>
|
||||
|
|
@ -2433,6 +2436,7 @@ impl QueryCursor {
|
|||
text_provider,
|
||||
buffer1: Vec::default(),
|
||||
buffer2: Vec::default(),
|
||||
current_match: None,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -2457,6 +2461,7 @@ impl QueryCursor {
|
|||
text_provider,
|
||||
buffer1: Vec::default(),
|
||||
buffer2: Vec::default(),
|
||||
current_match: None,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -2522,7 +2527,7 @@ impl<'tree> QueryMatch<'_, 'tree> {
|
|||
}
|
||||
|
||||
#[doc(alias = "ts_query_cursor_remove_match")]
|
||||
pub fn remove(self) {
|
||||
pub fn remove(&self) {
|
||||
unsafe { ffi::ts_query_cursor_remove_match(self.cursor, self.id) }
|
||||
}
|
||||
|
||||
|
|
@ -2551,7 +2556,7 @@ impl<'tree> QueryMatch<'_, 'tree> {
|
|||
}
|
||||
}
|
||||
|
||||
fn satisfies_text_predicates<I: AsRef<[u8]>>(
|
||||
pub fn satisfies_text_predicates<I: AsRef<[u8]>>(
|
||||
&self,
|
||||
query: &Query,
|
||||
buffer1: &mut Vec<u8>,
|
||||
|
|
@ -2669,13 +2674,16 @@ impl QueryProperty {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> Iterator
|
||||
/// Provide StreamingIterator instead of traditional one as the underlying object in the C library
|
||||
/// gets updated on each iteration. Created copies would have their internal state overwritten,
|
||||
/// leading to Undefined Behavior
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIterator
|
||||
for QueryMatches<'query, 'tree, T, I>
|
||||
{
|
||||
type Item = QueryMatch<'query, 'tree>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
fn advance(&mut self) {
|
||||
self.current_match = unsafe {
|
||||
loop {
|
||||
let mut m = MaybeUninit::<ffi::TSQueryMatch>::uninit();
|
||||
if ffi::ts_query_cursor_next_match(self.ptr, m.as_mut_ptr()) {
|
||||
|
|
@ -2686,23 +2694,35 @@ impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> Iterator
|
|||
&mut self.buffer2,
|
||||
&mut self.text_provider,
|
||||
) {
|
||||
return Some(result);
|
||||
break Some(result);
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
break None;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<&Self::Item> {
|
||||
self.current_match.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> Iterator
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIteratorMut
|
||||
for QueryMatches<'query, 'tree, T, I>
|
||||
{
|
||||
fn get_mut(&mut self) -> Option<&mut Self::Item> {
|
||||
self.current_match.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIterator
|
||||
for QueryCaptures<'query, 'tree, T, I>
|
||||
{
|
||||
type Item = (QueryMatch<'query, 'tree>, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
fn advance(&mut self) {
|
||||
self.current_match = unsafe {
|
||||
loop {
|
||||
let mut capture_index = 0u32;
|
||||
let mut m = MaybeUninit::<ffi::TSQueryMatch>::uninit();
|
||||
|
|
@ -2718,15 +2738,27 @@ impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> Iterator
|
|||
&mut self.buffer2,
|
||||
&mut self.text_provider,
|
||||
) {
|
||||
return Some((result, capture_index as usize));
|
||||
break Some((result, capture_index as usize));
|
||||
}
|
||||
result.remove();
|
||||
} else {
|
||||
return None;
|
||||
break None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<&Self::Item> {
|
||||
self.current_match.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIteratorMut
|
||||
for QueryCaptures<'query, 'tree, T, I>
|
||||
{
|
||||
fn get_mut(&mut self) -> Option<&mut Self::Item> {
|
||||
self.current_match.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TextProvider<I>, I: AsRef<[u8]>> QueryMatches<'_, '_, T, I> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue