diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index 9a1cd76f..6b28cdd5 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -78,6 +78,7 @@ fn test_query_errors_on_invalid_syntax() { .join("\n") ); + // Empty tree pattern assert_eq!( Query::new(language, r#"((identifier) ()"#) .unwrap_err() @@ -88,6 +89,8 @@ fn test_query_errors_on_invalid_syntax() { ] .join("\n") ); + + // Empty alternation assert_eq!( Query::new(language, r#"((identifier) [])"#) .unwrap_err() @@ -98,6 +101,8 @@ fn test_query_errors_on_invalid_syntax() { ] .join("\n") ); + + // Unclosed sibling expression with predicate assert_eq!( Query::new(language, r#"((identifier) (#a)"#) .unwrap_err() @@ -108,6 +113,8 @@ fn test_query_errors_on_invalid_syntax() { ] .join("\n") ); + + // Unclosed predicate assert_eq!( Query::new(language, r#"((identifier) @x (#eq? @x a"#) .unwrap_err() @@ -144,6 +151,7 @@ fn test_query_errors_on_invalid_syntax() { .join("\n") ); + // Unclosed alternation within a tree // tree-sitter/tree-sitter/issues/968 assert_eq!( Query::new(get_language("c"), r#"(parameter_list [ ")" @foo)"#) @@ -155,6 +163,22 @@ fn test_query_errors_on_invalid_syntax() { ] .join("\n") ); + + // Unclosed tree within an alternation + // tree-sitter/tree-sitter/issues/1436 + assert_eq!( + Query::new( + get_language("python"), + r#"[(unary_operator (_) @operand) (not_operator (_) @operand]"# + ) + .unwrap_err() + .message, + [ + r#"[(unary_operator (_) @operand) (not_operator (_) @operand]"#, + r#" ^"# + ] + .join("\n") + ); }); } diff --git a/lib/src/query.c b/lib/src/query.c index 86a9dfea..3e9c3198 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -1655,11 +1655,14 @@ static TSQueryError ts_query__parse_pattern( is_immediate ); - if (e == PARENT_DONE && stream->next == ']' && branch_step_indices.size > 0) { - stream_advance(stream); - break; - } else if (e) { - if (e == PARENT_DONE) e = TSQueryErrorSyntax; + if (e == PARENT_DONE) { + if (stream->next == ']' && branch_step_indices.size > 0) { + stream_advance(stream); + break; + } + e = TSQueryErrorSyntax; + } + if (e) { array_delete(&branch_step_indices); return e; } @@ -1707,12 +1710,14 @@ static TSQueryError ts_query__parse_pattern( depth, child_is_immediate ); - if (e == PARENT_DONE && stream->next == ')') { - stream_advance(stream); - break; - } else if (e) { - return e; + if (e == PARENT_DONE) { + if (stream->next == ')') { + stream_advance(stream); + break; + } + e = TSQueryErrorSyntax; } + if (e) return e; child_is_immediate = false; } @@ -1853,28 +1858,28 @@ static TSQueryError ts_query__parse_pattern( depth + 1, child_is_immediate ); - if (e == PARENT_DONE && stream->next == ')') { - if (child_is_immediate) { - if (last_child_step_index == 0) { - return TSQueryErrorSyntax; + if (e == PARENT_DONE) { + if (stream->next == ')') { + if (child_is_immediate) { + if (last_child_step_index == 0) return TSQueryErrorSyntax; + self->steps.contents[last_child_step_index].is_last_child = true; } - self->steps.contents[last_child_step_index].is_last_child = true; - } - if (negated_field_count) { - ts_query__add_negated_fields( - self, - starting_step_index, - negated_field_ids, - negated_field_count - ); - } + if (negated_field_count) { + ts_query__add_negated_fields( + self, + starting_step_index, + negated_field_ids, + negated_field_count + ); + } - stream_advance(stream); - break; - } else if (e) { - return e; + stream_advance(stream); + break; + } + e = TSQueryErrorSyntax; } + if (e) return e; last_child_step_index = step_index; child_is_immediate = false;