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
This commit is contained in:
DanikVitek 2026-01-10 23:01:55 +02:00
parent 275b7b7c8d
commit fc9c3a8c90
7 changed files with 34 additions and 34 deletions

View file

@ -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,

View file

@ -225,7 +225,7 @@ impl Pattern {
}
// Find every matching combination of child patterns and child nodes.
let mut finished_matches = Vec::<Match>::new();
let mut finished_matches = Vec::<Match<'_, 'tree>>::new();
if cursor.goto_first_child() {
let mut match_states = vec![(0, mat)];
loop {

View file

@ -215,11 +215,11 @@ fn try_resolve_path(path: &Path) -> rquickjs::Result<PathBuf> {
}
#[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<Value<'a>> {
) -> rquickjs::Result<Value<'js>> {
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<Value<'a>> {
) -> rquickjs::Result<Value<'js>> {
if path.extension().is_some_and(|ext| ext == "json") {
return ctx.eval::<Value, _>(format!("JSON.parse({contents:?})"));
return ctx.eval::<Value<'js>, _>(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<Value<'a>> {
move |ctx_inner: Ctx<'js>, target_path: String| -> rquickjs::Result<Value<'js>> {
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::<Function, _>(wrapper)?;
module_func.call::<_, Value>((exports, require, module_obj.clone(), filename, dirname))?;
let module_func = ctx.eval::<Function<'js>, _>(wrapper)?;
module_func.call::<_, Value<'js>>((exports, require, module_obj.clone(), filename, dirname))?;
module_obj.get("exports")
}

View file

@ -189,7 +189,7 @@ struct HighlightIterLayer<'a> {
depth: usize,
}
pub struct _QueryCaptures<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> {
pub struct _QueryCaptures<'query, 'tree, T: TextProvider<I>, 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>, I: AsRef<[u8]>> Iterator
impl<'query, 'tree, T: TextProvider<I>, 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.

View file

@ -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<Option<(Language, &LanguageConfiguration)>> {
) -> LoaderResult<Option<(Language, &LanguageConfiguration<'static>)>> {
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<Option<(Language, &LanguageConfiguration)>> {
) -> LoaderResult<Option<(Language, &LanguageConfiguration<'static>)>> {
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<Option<(Language, &LanguageConfiguration)>> {
) -> LoaderResult<Option<(Language, &LanguageConfiguration<'static>)>> {
// 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<Option<(Language, &LanguageConfiguration)>> {
) -> LoaderResult<Option<(Language, &LanguageConfiguration<'static>)>> {
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) {

View file

@ -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.

View file

@ -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>, I: AsRef<[u8]>> {
pub struct QueryMatches<'query, 'tree, T: TextProvider<I>, 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>, 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>, I: AsRef<[u8]>> {
pub struct QueryCaptures<'query, 'tree, T: TextProvider<I>, 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>, I: AsRef<[u8]>> StreamingIterator
impl<'query, 'tree, T: TextProvider<I>, 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>, I: AsRef<[u8]>> StreamingIterato
}
}
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIteratorMut
for QueryMatches<'query, 'tree, T, I>
{
impl<T: TextProvider<I>, 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>, I: AsRef<[u8]>> StreamingIterator
impl<'query, 'tree, T: TextProvider<I>, 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>, I: AsRef<[u8]>> StreamingIterato
}
}
impl<'query, 'tree: 'query, T: TextProvider<I>, I: AsRef<[u8]>> StreamingIteratorMut
for QueryCaptures<'query, 'tree, T, I>
{
impl<T: TextProvider<I>, 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<ffi::TSRange> 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,