From 6cabd9e67f537cd6dec78663742b69f249ed63d0 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Tue, 3 Jun 2025 18:41:12 +0200 Subject: [PATCH] fix(query)!: assert that predicates end in `!` or `?` Predicates/directives are documented to end in either `!` or `?`. However, `query.c` allows them to be any valid identifier, and also allows `?` or `!` characters anywhere inside an identifier. This commit removes `?` and `!` from the list of valid identifier characters, and asserts that predicates/directives only *end* in `?` or `!`, respectively. This commit is breaking because you can no longer do something like `(#eq? @capture foo!bar)` (`foo!bar` must now be quoted). --- cli/src/tests/query_test.rs | 18 +++++++++++++++--- lib/src/query.c | 8 +++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cli/src/tests/query_test.rs b/cli/src/tests/query_test.rs index ec77c91c..5486a537 100644 --- a/cli/src/tests/query_test.rs +++ b/cli/src/tests/query_test.rs @@ -117,12 +117,24 @@ fn test_query_errors_on_invalid_syntax() { // Unclosed sibling expression with predicate assert_eq!( - Query::new(&language, r"((identifier) (#a)") + Query::new(&language, r"((identifier) (#a?)") .unwrap_err() .message, [ - "((identifier) (#a)", // - " ^", + "((identifier) (#a?)", // + " ^", + ] + .join("\n") + ); + + // Predicate not ending in `?` or `!` + assert_eq!( + Query::new(&language, r"((identifier) (#a))") + .unwrap_err() + .message, + [ + "((identifier) (#a))", // + " ^", ] .join("\n") ); diff --git a/lib/src/query.c b/lib/src/query.c index 4748a00f..73c1e936 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -411,9 +411,7 @@ static void stream_scan_identifier(Stream *stream) { iswalnum(stream->next) || stream->next == '_' || stream->next == '-' || - stream->next == '.' || - stream->next == '?' || - stream->next == '!' + stream->next == '.' ); } @@ -2092,6 +2090,10 @@ static TSQueryError ts_query__parse_predicate( if (!stream_is_ident_start(stream)) return TSQueryErrorSyntax; const char *predicate_name = stream->input; stream_scan_identifier(stream); + if (stream->next != '?' && stream->next != '!') { + return TSQueryErrorSyntax; + } + stream_advance(stream); uint32_t length = (uint32_t)(stream->input - predicate_name); uint16_t id = symbol_table_insert_name( &self->predicate_values,