From fc9c3a8c906915900523de04f4d60f67671b18eb Mon Sep 17 00:00:00 2001 From: DanikVitek Date: Sat, 10 Jan 2026 23:01:55 +0200 Subject: [PATCH] fix: Clarify/fix lifetimes - One has to think about lifetimes if a type has one: - `<&'a Node<'tree>>::language` now returns `LanguageRef<'tree>` instead of `LanguageRef<'a>`, as it should; - Remove explicit "outlives" requirements from `QueryMatches`, `QueryCaptures`, and their impl blocks, because they're inferred - Removed unnecessary `&mut` from `cst_render_node`'s `cursor` parameter --- crates/cli/src/parse.rs | 2 +- crates/cli/src/tests/helpers/query_helpers.rs | 2 +- crates/generate/src/quickjs.rs | 20 ++++++++--------- crates/highlight/src/highlight.rs | 5 +++-- crates/loader/src/loader.rs | 16 ++++++++------ crates/tags/src/tags.rs | 1 + lib/binding_rust/lib.rs | 22 ++++++++----------- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/cli/src/parse.rs b/crates/cli/src/parse.rs index 1a1d9723..3551f556 100644 --- a/crates/cli/src/parse.rs +++ b/crates/cli/src/parse.rs @@ -953,7 +953,7 @@ fn render_node_range( fn cst_render_node( opts: &ParseFileOptions, - cursor: &mut TreeCursor, + cursor: &TreeCursor, source_code: &[u8], out: &mut impl Write, total_width: usize, diff --git a/crates/cli/src/tests/helpers/query_helpers.rs b/crates/cli/src/tests/helpers/query_helpers.rs index e648ac8e..e2c68f17 100644 --- a/crates/cli/src/tests/helpers/query_helpers.rs +++ b/crates/cli/src/tests/helpers/query_helpers.rs @@ -225,7 +225,7 @@ impl Pattern { } // Find every matching combination of child patterns and child nodes. - let mut finished_matches = Vec::::new(); + let mut finished_matches = Vec::>::new(); if cursor.goto_first_child() { let mut match_states = vec![(0, mat)]; loop { diff --git a/crates/generate/src/quickjs.rs b/crates/generate/src/quickjs.rs index 99e77397..d8c71cfe 100644 --- a/crates/generate/src/quickjs.rs +++ b/crates/generate/src/quickjs.rs @@ -215,11 +215,11 @@ fn try_resolve_path(path: &Path) -> rquickjs::Result { } #[allow(clippy::needless_pass_by_value)] -fn require_from_module<'a>( - ctx: Ctx<'a>, +fn require_from_module<'js>( + ctx: Ctx<'js>, module_path: String, from_module: &str, -) -> rquickjs::Result> { +) -> rquickjs::Result> { let current_module = PathBuf::from(from_module); let current_dir = if current_module.is_file() { current_module.parent().unwrap_or(Path::new(".")) @@ -234,13 +234,13 @@ fn require_from_module<'a>( load_module_from_content(&ctx, &resolved_path, &contents) } -fn load_module_from_content<'a>( - ctx: &Ctx<'a>, +fn load_module_from_content<'js>( + ctx: &Ctx<'js>, path: &Path, contents: &str, -) -> rquickjs::Result> { +) -> rquickjs::Result> { if path.extension().is_some_and(|ext| ext == "json") { - return ctx.eval::(format!("JSON.parse({contents:?})")); + return ctx.eval::, _>(format!("JSON.parse({contents:?})")); } let exports = Object::new(ctx.clone())?; @@ -256,7 +256,7 @@ fn load_module_from_content<'a>( let module_path = filename.clone(); let require = Function::new( ctx.clone(), - move |ctx_inner: Ctx<'a>, target_path: String| -> rquickjs::Result> { + move |ctx_inner: Ctx<'js>, target_path: String| -> rquickjs::Result> { require_from_module(ctx_inner, target_path, &module_path) }, )?; @@ -264,8 +264,8 @@ fn load_module_from_content<'a>( let wrapper = format!("(function(exports, require, module, __filename, __dirname) {{ {contents} }})"); - let module_func = ctx.eval::(wrapper)?; - module_func.call::<_, Value>((exports, require, module_obj.clone(), filename, dirname))?; + let module_func = ctx.eval::, _>(wrapper)?; + module_func.call::<_, Value<'js>>((exports, require, module_obj.clone(), filename, dirname))?; module_obj.get("exports") } diff --git a/crates/highlight/src/highlight.rs b/crates/highlight/src/highlight.rs index 9a78d1ac..d38351f6 100644 --- a/crates/highlight/src/highlight.rs +++ b/crates/highlight/src/highlight.rs @@ -189,7 +189,7 @@ struct HighlightIterLayer<'a> { depth: usize, } -pub struct _QueryCaptures<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> { +pub struct _QueryCaptures<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> { ptr: *mut ffi::TSQueryCursor, query: &'query Query, text_provider: T, @@ -225,7 +225,7 @@ impl<'tree> _QueryMatch<'_, 'tree> { } } -impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> Iterator +impl<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> Iterator for _QueryCaptures<'query, 'tree, T, I> { type Item = (QueryMatch<'query, 'tree>, usize); @@ -594,6 +594,7 @@ impl<'a> HighlightIterLayer<'a> { } } + // SAFETY: // The `captures` iterator borrows the `Tree` and the `QueryCursor`, which // prevents them from being moved. But both of these values are really just // pointers, so it's actually ok to move them. diff --git a/crates/loader/src/loader.rs b/crates/loader/src/loader.rs index 11c8b673..451c6b82 100644 --- a/crates/loader/src/loader.rs +++ b/crates/loader/src/loader.rs @@ -765,7 +765,7 @@ impl Loader { } #[must_use] - pub fn get_all_language_configurations(&self) -> Vec<(&LanguageConfiguration, &Path)> { + pub fn get_all_language_configurations(&self) -> Vec<(&LanguageConfiguration<'static>, &Path)> { self.language_configurations .iter() .map(|c| (c, self.languages_by_id[c.language_id].0.as_ref())) @@ -775,7 +775,7 @@ impl Loader { pub fn language_configuration_for_scope( &self, scope: &str, - ) -> LoaderResult> { + ) -> LoaderResult)>> { for configuration in &self.language_configurations { if configuration.scope.as_ref().is_some_and(|s| s == scope) { let language = self.language_for_id(configuration.language_id)?; @@ -788,7 +788,7 @@ impl Loader { pub fn language_configuration_for_first_line_regex( &self, path: &Path, - ) -> LoaderResult> { + ) -> LoaderResult)>> { self.language_configuration_ids_by_first_line_regex .iter() .try_fold(None, |_, (regex, ids)| { @@ -817,7 +817,7 @@ impl Loader { pub fn language_configuration_for_file_name( &self, path: &Path, - ) -> LoaderResult> { + ) -> LoaderResult)>> { // Find all the language configurations that match this file name // or a suffix of the file name. let configuration_ids = path @@ -889,7 +889,7 @@ impl Loader { pub fn language_configuration_for_injection_string( &self, string: &str, - ) -> LoaderResult> { + ) -> LoaderResult)>> { let mut best_match_length = 0; let mut best_match_position = None; for (i, configuration) in self.language_configurations.iter().enumerate() { @@ -1534,7 +1534,9 @@ impl Loader { } #[must_use] - pub fn get_language_configuration_in_current_path(&self) -> Option<&LanguageConfiguration> { + pub fn get_language_configuration_in_current_path( + &self, + ) -> Option<&LanguageConfiguration<'static>> { self.language_configuration_in_current_path .map(|i| &self.language_configurations[i]) } @@ -1543,7 +1545,7 @@ impl Loader { &mut self, parser_path: &Path, set_current_path_config: bool, - ) -> LoaderResult<&[LanguageConfiguration]> { + ) -> LoaderResult<&[LanguageConfiguration<'static>]> { let initial_language_configuration_count = self.language_configurations.len(); match TreeSitterJSON::from_file(parser_path) { diff --git a/crates/tags/src/tags.rs b/crates/tags/src/tags.rs index 16270b0a..c6654876 100644 --- a/crates/tags/src/tags.rs +++ b/crates/tags/src/tags.rs @@ -313,6 +313,7 @@ impl TagsContext { ) .ok_or(Error::Cancelled)?; + // SAFETY: // The `matches` iterator borrows the `Tree`, which prevents it from being // moved. But the tree is really just a pointer, so it's actually ok to // move it. diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 583ff1b0..51fd7f25 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -392,7 +392,7 @@ pub struct QueryMatch<'cursor, 'tree> { } /// A sequence of [`QueryMatch`]es associated with a given [`QueryCursor`]. -pub struct QueryMatches<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> { +pub struct QueryMatches<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> { ptr: *mut ffi::TSQueryCursor, query: &'query Query, text_provider: T, @@ -407,7 +407,7 @@ pub struct QueryMatches<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8] /// /// During iteration, each element contains a [`QueryMatch`] and index. The index can /// be used to access the new capture inside of the [`QueryMatch::captures`]'s [`captures`]. -pub struct QueryCaptures<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> { +pub struct QueryCaptures<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> { ptr: *mut ffi::TSQueryCursor, query: &'query Query, text_provider: T, @@ -1581,7 +1581,7 @@ impl<'tree> Node<'tree> { /// Get the [`Language`] that was used to parse this node's syntax tree. #[doc(alias = "ts_node_language")] #[must_use] - pub fn language(&self) -> LanguageRef { + pub fn language(&self) -> LanguageRef<'tree> { LanguageRef(unsafe { ffi::ts_node_language(self.0) }, PhantomData) } @@ -3404,7 +3404,7 @@ impl QueryProperty { /// Provide a `StreamingIterator` instead of the traditional `Iterator`, as the /// underlying object in the C library gets updated on each iteration. Copies would /// have their internal state overwritten, leading to Undefined Behavior -impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIterator +impl<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> StreamingIterator for QueryMatches<'query, 'tree, T, I> { type Item = QueryMatch<'query, 'tree>; @@ -3435,15 +3435,13 @@ impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIterato } } -impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIteratorMut - for QueryMatches<'query, 'tree, T, I> -{ +impl, I: AsRef<[u8]>> StreamingIteratorMut for QueryMatches<'_, '_, T, I> { fn get_mut(&mut self) -> Option<&mut Self::Item> { self.current_match.as_mut() } } -impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIterator +impl<'query, 'tree, T: TextProvider, I: AsRef<[u8]>> StreamingIterator for QueryCaptures<'query, 'tree, T, I> { type Item = (QueryMatch<'query, 'tree>, usize); @@ -3480,9 +3478,7 @@ impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIterato } } -impl<'query, 'tree: 'query, T: TextProvider, I: AsRef<[u8]>> StreamingIteratorMut - for QueryCaptures<'query, 'tree, T, I> -{ +impl, I: AsRef<[u8]>> StreamingIteratorMut for QueryCaptures<'_, '_, T, I> { fn get_mut(&mut self) -> Option<&mut Self::Item> { self.current_match.as_mut() } @@ -3622,8 +3618,8 @@ impl From for Range { } } -impl From<&'_ InputEdit> for ffi::TSInputEdit { - fn from(val: &'_ InputEdit) -> Self { +impl From<&InputEdit> for ffi::TSInputEdit { + fn from(val: &InputEdit) -> Self { Self { start_byte: val.start_byte as u32, old_end_byte: val.old_end_byte as u32,