From 99bceec689d42a175269d3b0e18e877e916f361e Mon Sep 17 00:00:00 2001 From: "Lucas M. de Jong Larrarte" Date: Mon, 18 May 2026 00:08:35 +0200 Subject: [PATCH] fix(query): skip passthrough steps when checking for fallible step splitting Problem: In queries matching a parent node with anchored children (siblings) where the first sibling has a quantifier and is not captured, the check for fallible steps skips splitting the state because the next node is a passthrough node (and not an is_immediate one). This prevents the query from matching beyond the first occurrence. Solution: In the check for fallible steps, skip the next steps if they are passthrough steps. --- crates/cli/src/tests/query_test.rs | 32 ++++++++++++++++++++++++++++++ lib/src/query.c | 9 +++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/tests/query_test.rs b/crates/cli/src/tests/query_test.rs index 609cf65e6..8482de545 100644 --- a/crates/cli/src/tests/query_test.rs +++ b/crates/cli/src/tests/query_test.rs @@ -1625,6 +1625,38 @@ fn test_matches_with_anchor_sibling_inside_parent() { }); } +#[test] +fn test_matches_with_anchor_sibling_with_quantifier_inside_parent() { + allocations::record(|| { + let language = get_language("rust"); + + let query = Query::new( + &language, + " + (source_file + (line_comment)+ + . + (function_item + name: (identifier) @name) + )", + ) + .unwrap(); + + assert_query_matches( + &language, + &query, + " + // A + fn a() {} + + // B + fn b() {} + ", + &[(0, vec![("name", "a")]), (0, vec![("name", "b")])], + ); + }); +} + #[test] fn test_matches_with_anchor_sibling_with_quantifier_captured_inside_parent() { allocations::record(|| { diff --git a/lib/src/query.c b/lib/src/query.c index 974454aa0..b4cf615df 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -3287,9 +3287,14 @@ bool ts_query__step_is_fallible( const TSQuery *self, uint16_t step_index ) { - ts_assert((uint32_t)step_index + 1 < self->steps.size); + unsigned i = 1; QueryStep *step = array_get(&self->steps, step_index); - QueryStep *next_step = array_get(&self->steps, step_index + 1); + QueryStep *next_step; + do { + ts_assert((uint32_t)step_index + i < self->steps.size); + next_step = array_get(&self->steps, step_index + i); + i++; + } while (next_step->is_pass_through); return ( next_step->depth != PATTERN_DONE_MARKER && (next_step->depth > step->depth ||