From c25fa9910ecfb07f92a37fb2faef70d418ec6781 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2021 21:41:35 -0800 Subject: [PATCH] Fix query match failure when indefinite steps have no captures Fixes #937 --- cli/src/tests/query_test.rs | 50 +++++++++++++++++++++++++++++++++++++ lib/src/query.c | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index 37e13f05..8a44af3c 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -1861,6 +1861,56 @@ fn test_query_matches_with_repeated_fields() { }); } +#[test] +fn test_query_matches_with_indefinite_step_containing_no_captures() { + allocations::record(|| { + // This pattern depends on the field declarations within the + // struct's body, but doesn't capture anything within the body. + // It demonstrates that internally, state-splitting needs to occur + // for each field declaration within the body, in order to avoid + // prematurely failing if the first field does not match. + // + // https://github.com/tree-sitter/tree-sitter/issues/937 + let language = get_language("c"); + let query = Query::new( + language, + "(struct_specifier + name: (type_identifier) @name + body: (field_declaration_list + (field_declaration + type: (union_specifier))))", + ) + .unwrap(); + + assert_query_matches( + language, + &query, + " + struct LacksUnionField { + int a; + struct { + B c; + } d; + G *h; + }; + + struct HasUnionField { + int a; + struct { + B c; + } d; + union { + bool e; + float f; + } g; + G *h; + }; + ", + &[(0, vec![("name", "HasUnionField")])], + ); + }); +} + #[test] fn test_query_captures_basic() { allocations::record(|| { diff --git a/lib/src/query.c b/lib/src/query.c index 247880c7..d28e1b92 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -2676,7 +2676,7 @@ static inline bool ts_query_cursor__advance( // parent, then this query state cannot simply be updated in place. It must be // split into two states: one that matches this node, and one which skips over // this node, to preserve the possibility of matching later siblings. - if (later_sibling_can_match && step->contains_captures) { + if (later_sibling_can_match && (step->contains_captures || !step->is_definite)) { if (ts_query_cursor__copy_state(self, &state)) { LOG( " split state for capture. pattern:%u, step:%u\n",