diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index d94ebac0..d6153dd4 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -3036,6 +3036,62 @@ fn test_query_capture_names() { }); } +#[test] +fn test_query_lifetime_is_separate_from_nodes_lifetime() { + allocations::record(|| { + let query = r#"(call_expression) @call"#; + let source = "a(1); b(2);"; + + let language = get_language("javascript"); + let mut parser = Parser::new(); + parser.set_language(language).unwrap(); + let tree = parser.parse(&source, None).unwrap(); + + fn take_first_node_from_captures<'tree>( + source: &str, + query: &str, + node: Node<'tree>, + ) -> Node<'tree> { + // Following 2 lines are redundant but needed to demonstrate + // more understandable compiler error message + let language = get_language("javascript"); + let query = Query::new(language, query).unwrap(); + let mut cursor = QueryCursor::new(); + let node = cursor + .matches(&query, node, to_callback(source)) + .next() + .unwrap() + .captures[0] + .node; + node + } + + let node = take_first_node_from_captures(source, query, tree.root_node()); + assert_eq!(node.kind(), "call_expression"); + + fn take_first_node_from_matches<'tree>( + source: &str, + query: &str, + node: Node<'tree>, + ) -> Node<'tree> { + let language = get_language("javascript"); + let query = Query::new(language, query).unwrap(); + let mut cursor = QueryCursor::new(); + let node = cursor + .captures(&query, node, to_callback(source)) + .next() + .unwrap() + .0 + .captures[0] + .node; + node + } + + let node = take_first_node_from_matches(source, query, tree.root_node()); + assert_eq!(node.kind(), "call_expression"); + }); +} + #[test] fn test_query_with_no_patterns() { allocations::record(|| { diff --git a/highlight/src/lib.rs b/highlight/src/lib.rs index 0f48847b..c1e7aba4 100644 --- a/highlight/src/lib.rs +++ b/highlight/src/lib.rs @@ -83,7 +83,7 @@ struct LocalScope<'a> { local_defs: Vec>, } -struct HighlightIter<'a, F> +struct HighlightIter<'a, 'tree: 'a, F> where F: FnMut(&str) -> Option<&'a HighlightConfiguration> + 'a, { @@ -92,16 +92,16 @@ where highlighter: &'a mut Highlighter, injection_callback: F, cancellation_flag: Option<&'a AtomicUsize>, - layers: Vec>, + layers: Vec>, iter_count: usize, next_event: Option, last_highlight_range: Option<(usize, usize, usize)>, } -struct HighlightIterLayer<'a> { +struct HighlightIterLayer<'a, 'tree: 'a> { _tree: Tree, cursor: QueryCursor, - captures: iter::Peekable>, + captures: iter::Peekable>, config: &'a HighlightConfiguration, highlight_end_stack: Vec, scope_stack: Vec>, @@ -319,7 +319,7 @@ impl HighlightConfiguration { } } -impl<'a> HighlightIterLayer<'a> { +impl<'a, 'tree: 'a> HighlightIterLayer<'a, 'tree> { /// Create a new 'layer' of highlighting for this document. /// /// In the even that the new layer contains "combined injections" (injections where multiple @@ -548,7 +548,7 @@ impl<'a> HighlightIterLayer<'a> { } } -impl<'a, F> HighlightIter<'a, F> +impl<'a, 'tree: 'a, F> HighlightIter<'a, 'tree, F> where F: FnMut(&str) -> Option<&'a HighlightConfiguration> + 'a, { @@ -596,7 +596,7 @@ where } } - fn insert_layer(&mut self, mut layer: HighlightIterLayer<'a>) { + fn insert_layer(&mut self, mut layer: HighlightIterLayer<'a, 'tree>) { if let Some(sort_key) = layer.sort_key() { let mut i = 1; while i < self.layers.len() { @@ -615,7 +615,7 @@ where } } -impl<'a, F> Iterator for HighlightIter<'a, F> +impl<'a, 'tree: 'a, F> Iterator for HighlightIter<'a, 'tree, F> where F: FnMut(&str) -> Option<&'a HighlightConfiguration> + 'a, { diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index a0b757d5..f0447d8d 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -134,10 +134,10 @@ pub struct QueryMatch<'a> { } /// A sequence of `QueryCapture`s within a `QueryMatch`. -pub struct QueryCaptures<'a, T: AsRef<[u8]>> { +pub struct QueryCaptures<'a, 'tree: 'a, T: AsRef<[u8]>> { ptr: *mut ffi::TSQueryCursor, query: &'a Query, - text_callback: Box) -> T + 'a>, + text_callback: Box) -> T + 'a>, } /// A particular `Node` that has been captured with a particular name within a `Query`. @@ -1587,7 +1587,7 @@ impl Query { } } -impl QueryCursor { +impl<'a> QueryCursor { /// Create a new cursor for executing a given query. /// /// The cursor stores the state that is needed to iteratively search for matches. @@ -1606,12 +1606,12 @@ impl QueryCursor { /// Each match contains the index of the pattern that matched, and a list of captures. /// Because multiple patterns can match the same set of nodes, one match may contain /// captures that appear *before* some of the captures from a previous match. - pub fn matches<'a, T: AsRef<[u8]>>( + pub fn matches<'tree: 'a, T: AsRef<[u8]>>( &'a mut self, query: &'a Query, - node: Node<'a>, - mut text_callback: impl FnMut(Node<'a>) -> T + 'a, - ) -> impl Iterator> + 'a { + node: Node<'tree>, + mut text_callback: impl FnMut(Node<'tree>) -> T + 'a, + ) -> impl Iterator> + 'a { let ptr = self.0.as_ptr(); unsafe { ffi::ts_query_cursor_exec(ptr, query.ptr.as_ptr(), node.0) }; std::iter::from_fn(move || loop { @@ -1633,12 +1633,12 @@ impl QueryCursor { /// /// This is useful if don't care about which pattern matched, and just want a single, /// ordered sequence of captures. - pub fn captures<'a, T: AsRef<[u8]>>( + pub fn captures<'tree, T: AsRef<[u8]>>( &'a mut self, query: &'a Query, - node: Node<'a>, - text_callback: impl FnMut(Node<'a>) -> T + 'a, - ) -> QueryCaptures<'a, T> { + node: Node<'tree>, + text_callback: impl FnMut(Node<'tree>) -> T + 'a, + ) -> QueryCaptures<'a, 'tree, T> { let ptr = self.0.as_ptr(); unsafe { ffi::ts_query_cursor_exec(ptr, query.ptr.as_ptr(), node.0) }; QueryCaptures { @@ -1732,8 +1732,8 @@ impl QueryProperty { } } -impl<'a, T: AsRef<[u8]>> Iterator for QueryCaptures<'a, T> { - type Item = (QueryMatch<'a>, usize); +impl<'a, 'tree: 'a, T: AsRef<[u8]>> Iterator for QueryCaptures<'a, 'tree, T> { + type Item = (QueryMatch<'tree>, usize); fn next(&mut self) -> Option { loop {