From 2d9a2a1855bf02c39f6561c8cf30f674ee4ffdaf Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Thu, 22 Apr 2021 19:18:51 +0300 Subject: [PATCH 1/4] test(binding_rust): test query captures/matches lifetime is separate from nodes lifetime --- cli/src/tests/query_test.rs | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index d94ebac0..967f39f5 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -3036,6 +3036,57 @@ 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(); + let query = Query::new(language, query).unwrap(); + + fn take_first_node_from_captures<'tree>( + source: &str, + query: &Query, + node: Node<'tree>, + ) -> Node<'tree> { + 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: &Query, + node: Node<'tree>, + ) -> Node<'tree> { + 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(|| { From b57266cbb84a238ecf188f3d3cfb8385cd8233c2 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Thu, 22 Apr 2021 20:58:08 +0300 Subject: [PATCH 2/4] test(binding_rust): Use some redundancy to trigger more clear comliler error on the master --- cli/src/tests/query_test.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index 967f39f5..d6153dd4 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -3046,13 +3046,16 @@ fn test_query_lifetime_is_separate_from_nodes_lifetime() { let mut parser = Parser::new(); parser.set_language(language).unwrap(); let tree = parser.parse(&source, None).unwrap(); - let query = Query::new(language, query).unwrap(); fn take_first_node_from_captures<'tree>( source: &str, - query: &Query, + 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)) @@ -3063,14 +3066,16 @@ fn test_query_lifetime_is_separate_from_nodes_lifetime() { node } - let node = take_first_node_from_captures(source, &query, tree.root_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: &Query, + 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)) @@ -3082,7 +3087,7 @@ fn test_query_lifetime_is_separate_from_nodes_lifetime() { node } - let node = take_first_node_from_matches(source, &query, tree.root_node()); + let node = take_first_node_from_matches(source, query, tree.root_node()); assert_eq!(node.kind(), "call_expression"); }); } From 9c91affe56aaaee1808ec248a554a387859ffe86 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Thu, 22 Apr 2021 11:50:44 +0300 Subject: [PATCH 3/4] binding_rust: Unbind nodes lifitime from a source for QueryCursor.matches --- lib/binding_rust/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index a0b757d5..17c91289 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -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,7 +1633,7 @@ 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>( &'a mut self, query: &'a Query, node: Node<'a>, From de23c9219a0b7f22514ecd022d0a9d91897baf2f Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Thu, 22 Apr 2021 12:31:32 +0300 Subject: [PATCH 4/4] binding_rust: Unbind nodes lifitime from a source for QueryCursor.captures --- highlight/src/lib.rs | 16 ++++++++-------- lib/binding_rust/lib.rs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) 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 17c91289..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`. @@ -1633,12 +1633,12 @@ impl<'a> QueryCursor { /// /// This is useful if don't care about which pattern matched, and just want a single, /// ordered sequence of captures. - pub fn captures>( + 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 {