Commit graph

645 commits

Author SHA1 Message Date
Tim Vermeulen f837fc9813 fix(lib): accumulate error costs through hidden error nodes
(cherry picked from commit 869638f6cf)
2026-08-14 09:47:37 +02:00
Will Lillis 063f8f886b fix(query): correctly set a state's skipped_quantifier flag when a
zero quantifier skip is performed.

The zero-skip branch currently sets skipped_quantifier unconditionally.
That flag is correct only when the quantified step and its skip target
are _siblings_ at the same query depth. Otherwise, setting it allows for
a "leak" and disables unrelated, "outer" anchors.

(cherry picked from commit 42f33fe2f8)
2026-08-11 00:26:25 -05:00
Will Lillis 6320165515 fix(query): correctly identify MISSING nodes in queries
(cherry picked from commit 5fae914f8f)
2026-08-09 23:07:51 +02:00
Newosko 7566ffacb7 fix(lib): continue search for later named siblings in
`ts_tree_cursor_current_status`

By terminating early on `has_later_siblings`, `has_later_named_siblings`
was incorrectly reported as `false` in some cases. This led to the
execution of some queries to terminate early.

Also remove some dead branches inside `ts_tree_cursor_current_status`.

Co-authored-by: Will Lillis <will.lillis24@gmail.com>
(cherry picked from commit 308aee0c90)
2026-08-08 00:20:18 +02:00
Will Lillis 3ee7c639de fix(parser): restart recovery for invalid tokens in the error state
The early `ts_parser__recover` dispatch for `ERROR_STATE` in
`ts_parser__advance` was dropped in `201b41cf1`. Without it, tokens
with no valid action re-enter `ts_parser__handle_error` per token
and fragment the tree instead of extending one ERROR node. Restore
it to recover the old (pre-0.25) error recovery behavior.

(cherry picked from commit 15ea3328e1)
2026-07-25 18:11:25 +02:00
Will Lillis 5ccdbb6b84 fix(query): transfer a leading boundary anchor across a zero-matched quantifier
`(P . Q* Y)` with zero `Q` dropped the leading `.`, so `Y` matched at any
position instead of being pinned to the parent's first named child. On a `?`/`*`
zero-skip, when the skipped step is the parent's first child and carries a
leading anchor, transfer that first-child requirement to the skip target.

(cherry picked from commit 1ffd612be5)
2026-07-17 19:47:19 -04:00
Will Lillis fcb38e9dcf fix(query): keep the trailing anchor when a zero-matched quantifier is anchored
on both sides

In `A . Q* . B`, a zero-matched `Q` made both anchors vacuous, so `A` and `B`
were no longer required to be immediate siblings. Only relax the following
anchor on a `?`/`*` zero-skip when the skipped step has no leading anchor of
its own; otherwise the adjacency transfers through the empty run.

(cherry picked from commit dfcf73921c)
2026-07-17 19:47:19 -04:00
Julia Hansbrough 90dd1a0cfb fix(templates): generated array macros do not compile with C++
Problem: A set of `array_*` macros (`array_push`, `array_extend`, etc) implicitly convert a `void*` into a different pointer type.  In environments that compile these headers as C++, this implicit conversion is an error.

Solution: This commit adds an `_array_cast` macro that uses `decltype` to cast the `void*` to the proper type when compiling as C++.
(cherry picked from commit cc7be1fd47)
2026-07-08 17:34:45 -04:00
Will Lillis 1c21ff33d6 fix(query): apply a trailing anchor when its optional node is skipped
A trailing `.` after an optional node, e.g. `(p (a)+ @a . (b)? @b .)`, sets
`is_last_child` on the `(b)?` step. When `(b)?` matched zero, the zero-skip
jumped past that step to completion, so the last-child requirement was never
enforced.

When a zero-skip would bypass a step carrying `is_last_child`, require that
the last matched node really is the last named child. Gated on the
`alternative_is_skip` edge marker.

(cherry picked from commit a8486ca239)
2026-07-01 20:04:13 -04:00
Will Lillis 5756ced350 fix(query): make an anchor after a zero-matched quantifier vacuous
A `.` anchor between a quantified pattern and a following node, e.g.
`(parent (comment)* @c . (decl))`, was treated as a leading anchor when the
quantifier matched zero. The zero-skip jumps a state directly onto the anchored
step, where `is_immediate` was enforced even though no sibling had matched before
it. Name that skip edge (`alternative_is_skip`) and, when a state follows it,
record that it skipped the quantifier (`skipped_quantifier`). The
immediate anchor is then supressoed for that state's next match.

(cherry picked from commit 7e7f2584d1)
2026-07-01 20:04:13 -04:00
Will Lillis eca86a7a6c fix(query): forbid an anchor at the end of a group
A `.` at the end of a group, such as `((comment)+ @c .)`, was silently dropped
during compilation. Reject rather than dropping.

(cherry picked from commit 966dc52557)
2026-07-01 20:04:13 -04:00
Will Lillis 306f868299 feat(query): enhanced step dumper
Extract the step dump into `ts_query__dump_steps` and enrich it with the control-flow
fields and analysis annotations.

(cherry picked from commit 6bfbd9d84d)
2026-07-01 20:04:13 -04:00
Will Lillis e7d6651336 fix(query): short-circuit longest-match dedup for disjoint states
An unanchored quantified sibling like `(program (comment)+ @doc (class))`
keeps one match state per matching sibling. A recent fix stopped over-pruning
them, but the per-node longest-match dedup is pairwise, so with n live states
matching became O(n^3).

Two states can only be capture subsets of one another if their captured
byte ranges overlap, so keep each group ordered by first-capture position
and stop the pairwise scan once the rest of the group is disjoint.

Refs neovim/neovim#40517

(cherry picked from commit 91fd04f96a)
2026-07-01 19:01:52 -04:00
Lucas M. de Jong Larrarte 2efce10fe9 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.

(cherry picked from commit 99bceec689)
2026-06-28 00:10:08 +02:00
Lucas M. de Jong Larrarte e8b9639291 fix(query): avoid overriding states not seeking immediate match with states seeking immediate match
Problem: In queries matching a parent node with anchored children
(siblings) where the first anchored sibling has a quantifier, the
deduplication logic for states is overly aggressive. The logic causes
the state split on the first capture (with the parent) to be dropped in
favor of the loopback state. This results in the query not being able to
match beyond the first occurrence.

Solution: Prevent the deduplication logic from dropping a state that is
not seeking an immediate match for one that is seeking an immediate
match, since the one not seeking for an immediate match could still
match later nodes.

(cherry picked from commit 8b43f42dca)
2026-06-28 00:10:08 +02:00
Lucas M. de Jong Larrarte 576564d25a fix(query): take anchors between siblings into account for fallible step splitting
Problem: In queries matching a parent node with anchored children
(siblings), the check for fallible steps only considers nodes with
children, which results in no state split being made for anchored
siblings (node1) . (node2). This prevents the query from matching beyond
the first occurrence.

Solution: Make the check for fallible steps consider also the case where
the next step is at the same depth and must be matched immediately
after.

(cherry picked from commit 1b110f62dd)
2026-06-28 00:10:08 +02:00
Will Lillis a61be4ac27 fix(lib): address strict aliasing violations in TreeCursor type
Omit the static assert from master to avoid a breaking change on the
release branch.
2026-06-17 00:29:06 -04:00
Will Lillis 70068dd487 fix(lib): Consider subtree lookahead bytes when determining whether the
parser can reuse a node.

Lookahead bytes can be used to decide what a node is parsed as, so it's
resaonable to consider this as part of a node's "range" when deciding
which edits affect it.

(cherry picked from commit 15ddfb21ed)
2026-06-01 19:12:50 -04:00
Georges Savoundararadj 3ab78c3c5b fix(lib): use correct TREE_SITTER_HIDE_SYMBOLS macro name in alloc.h
The alloc.h header checked for TREE_SITTER_HIDDEN_SYMBOLS, but the
canonical macro name used everywhere else (api.h, render.rs, setup.py)
is TREE_SITTER_HIDE_SYMBOLS. This mismatch meant that defining
TREE_SITTER_HIDE_SYMBOLS (as the Python binding build does) would not
actually hide the allocator symbols in alloc.h.

Fixes tree-sitter/tree-sitter#5625

Signed-off-by: Georges Savoundararadj <savoundg@amazon.com>
(cherry picked from commit 1da46327b3)
2026-05-30 23:00:00 -04:00
tree-sitter-ci-bot[bot] 77b96de523
fix(wasm): validate memory reads (#5569) (#5613)
Problem: Offsets such as parse_table, symbol_names, lex_modes, and the alias/supertype tables are used directly as indexes into the store's memory buffer. A malformed module can point one of those fields outside the current linear memory and make the host process read through an invalid pointer while loading the language.

Solution: Add a small checked-memory wrapper for Wasm language loading and routes descriptor reads, table copies, string reads, and the alias-map scan through it. Invalid descriptor addresses now fail loading with TSWasmErrorKindInstantiate.

(cherry picked from commit 21cfae7b56)

Co-authored-by: 𝙽!𝙻 <z_hakmi@estin.dz>
2026-05-19 19:40:15 +02:00
tree-sitter-ci-bot[bot] 7aea01521d
fix(wasm): load supertype tables for ABI 15 grammars (#5605) (#5606)
The wasm store gated supertype_symbols / supertype_map_slices /
supertype_map_entries copies on abi_version > LANGUAGE_VERSION_WITH_RESERVED_WORDS,
but every other consumer (language.c, query.c) treats those tables as
present when abi_version >= LANGUAGE_VERSION_WITH_RESERVED_WORDS.

A Wasm grammar built at ABI exactly 15 with supertype_count > 0 ends up
with supertype_count copied into the native TSLanguage but supertype_map_slices
left NULL. ts_query__analyze_patterns then calls ts_language_subtypes,
which dereferences self->supertype_map_slices[supertype] and crashes.

(cherry picked from commit a53c3b03a0)

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2026-05-18 13:58:51 -07:00
Amaan Qureshi b7964b9b19 query: fix finished state heap invariants
(cherry picked from commit 43dc8eadbe)
2026-04-27 03:37:38 -04:00
Will Lillis b0ddae770e perf(query): use min-heap for finished_states in next_capture
`ts_query_cursor_next_capture` linearly scanned all finished states to
find the one with the earliest next capture byte offset. With deeply
nested code, this O(n) scan per capture caused the highlight crate to
hang for minutes on large files.

Replace the linear scan with a min-heap over the finished_states array,
keyed by (next_capture_byte_offset, pattern_index, id). The heap is
maintained lazily: ts_query_cursor__advance uses plain array_push
(preserving FIFO insertion order), and next_capture sifts new elements
into the heap on entry via a tracked heap_size boundary. This preserves
the documented "order found" guarantee for next_match while giving
next_capture O(log n) per call.

(cherry picked from commit 123fb1c13c)
2026-04-27 03:37:38 -04:00
Will Lillis 0b9a7f87ee fix(query): widen capture list pool from uint16_t to uint32_t
Commit 1f6eac55 ("query: Use uint32_t for capture list IDs") widened
QueryState.capture_list_id to uint32_t and removed the 65536 pool cap,
but left the pool function signatures as uint16_t. This caused silent
truncation when the pool exceeded 65535 entries, leading to a segfault.

(cherry picked from commit 361f293a73)
2026-04-27 03:37:38 -04:00
Daniel Jalkut e4ac513afd lexer: pass code-unit length to U16_NEXT in UTF-16 decoders
`ts_decode_utf16_le` and `ts_decode_utf16_be` passed a byte length to
`U16_NEXT_LE` and `U16_NEXT_BE`, but those macros count `uint16_t` code
units. If a lead surrogate was the last code unit in a chunk, the decoder
could peek past the chunk and combine it with adjacent memory.

This commit passes the code-unit length to the UTF-16 macros, and fails
with `TS_DECODE_ERROR` when a chunk is too short to contain even one full
code unit. This lets the lexer retry with a fresh chunk or advance through
the invalid byte as it already does.

Co-authored-by: Will Lillis <will.lillis24@gmail.com>
Co-authored-by: Amaan Qureshi <git@amaanq.com>
(cherry picked from commit 0f6780b9a3)
2026-04-26 03:40:09 -04:00
Max Brunsfeld 0b04fd0533 Fix wasm loading of languages w/ multiple reserved word sets (#5475)
(cherry picked from commit d3ff0ce81d)
2026-03-31 02:01:50 -04:00
Riley Bruins 8e87144b61 fix(query): don't add copies for quantifier steps outside alternations
(cherry picked from commit cf302b07d1)
2026-03-04 11:01:39 +01:00
Riley Bruins b61eabb4d2 refactor(query): remove alternative_is_immediate
This is implied by `is_pass_through`.

(cherry picked from commit a95fff5477)
2026-03-04 02:52:27 -05:00
Riley Bruins 16c7bfb48f chore(parser): return NULL, not false, for incomplete parse
Small nit; `NULL` is returned everywhere else in this function for an
incomplete parse.

(cherry picked from commit 4ae90615d1)
2026-03-01 05:13:28 -05:00
lucasew a226c68720 fix(lib): cast NULL in ts_subtree_children macro
Explicitly casting NULL to (Subtree *) in the ternary expression ensures
type consistency. This resolves ambiguous type inference issues encountered
by strict static analysis tools and C-to-Go transpilers (like ccgo).

Signed-off-by: lucasew <lucas59356@gmail.com>
(cherry picked from commit 4e9fededff)
2026-02-21 04:34:07 -05:00
Christian Clason d03bb288e2 build(deps): bump wasmtime to v36.0.5 LTS
Wasmtime v34 introduced breaking ABI changes to `wasmtime_func_t` and
`wasmtime_table_t` structures. The `__private` field changed from
`size_t` to `void*`, requiring code updates to store complete
`wasmtime_func_t` structures instead of raw indices.

Changes:
- `BuiltinFunctionIndices`: `uint32_t` -> `wasmtime_func_t`
- `stdlib_fn_indices`: `uint32_t*` -> `wasmtime_func_t*`
- `FunctionDefinition.storage_location`: `uint32_t*` -> `void*`
- `get_builtin_extern()`: simplified to return stored func directly
- Lexer functions: use temp array for func storage, store table index
- Zero-initialize `builtin_fn_indices` so `store_id == 0` sentinel
  reliably detects missing stdlib exports
- Free `lexer_funcs` on error paths in `ts_wasm_store_new`
- Remove stale `(uint32_t *)` casts from `lexer_definitions`

Co-Authored-By: nzinfo <li.monan@gmail.com>
(cherry picked from commit a21ee02710)
2026-02-20 10:52:27 +01:00
Sergey Reshetnikov ed06ca058b fix(lib): add RedoxOS support to portable/endian.h
(cherry picked from commit 5e23ccaac2)
2026-02-18 16:51:18 +01:00
Will Lillis 89e804b7e4 fix(query): prevent cross-branch capture contamination in alternations with quantifiers
When a branch inside an alternation has a + or * quantifier, the
quantifier's pass_through step loops back to the branch's first step.
The alternation linking also sets that step's alternative_index to
point to the next branch. This causes the quantifier loop-back to
incorrectly explore other branches in the case of a failed match that
follows a successful match, contaminating captures.

This is corrected by redirecting the quantifier loop-back to a
"clean" copy of the target step without the alternative index pointing
to the next alternation branch.

Co-authored-by: Riley Bruins <ribru17@hotmail.com>
(cherry picked from commit 596a4d69bb)
2026-02-09 22:55:40 -05:00
Amaan Qureshi 325bc50d6f lib: clean up strict aliasing fixes in array.h
(cherry picked from commit 22cda59a19)
2026-02-09 21:11:32 -05:00
Will Lillis 666144d3ed fix(wasm): when reallocating the last allocated region, properly grow
the region in place.

Previous changes to `malloc` caused `realloc` to sometimes pull regions
off of the free list during this optimization. Because no `memcpy` is
performed, this resulted in corrupted data returning to the caller.

Co-authored-by: trim21 <i@trim21.me>
2026-01-31 20:06:32 -05:00
Will Lillis 88a5475496 fix(wasm): return early from calloc if malloc fails
Co-authored-by: trim21 <i@trim21.me>
2026-01-26 23:36:50 -05:00
Will Lillis 6a8a5e33d9 fix(wasm): correct several bugs in realloc
- free memory if 0 size is passed in
- Don't `memcpy` contents if new pointer is `NULL`
- Copy old region's contents only up to size of new region
- free old region

Co-authored-by: trim21 <i@trim21.me>
2026-01-26 23:36:50 -05:00
Will Lillis ed6e42cbf0 fix(lib): address strict aliasing violations with Array type
Altering the `Array` type itself isn't feasible, as this causes
unacceptable breakage with existing parsers that depend on it. Instead,
pass in individual `Array` fields for to various `_array__*` functions.

Any time the `contents` of an array may be modified (`free`d, `realloc`d,
etc), return the potentially new address out by value. This prevents any
strict aliasing violations as we're no longer writing to a type-casted
pointer.

Co-authored-by: Nathaniel Wesley Filardo <nwfilardo@gmail.com>
(cherry picked from commit 5177b3dc26)
2026-01-24 22:19:28 +01:00
Christian Clason f05efbb352 fix(wasm): regenerate stdlib with wasm-opt
Problem: Output of `cargo xtask build-wasm-stdlib` depends on whether
`wasm-opt` is installed (since `clang` will use it by default if it
finds it).

Solution: Install it and rerun the xtask.
(cherry picked from commit 5d290a2a75)
2026-01-15 16:52:47 +01:00
Christian Clason fa7b1b2a66 fix(wasm): update wasm-stdlib.h
(cherry picked from commit cd6672701b)
2026-01-06 19:27:35 +01:00
Max Brunsfeld be8fe690d8 Clean up node range tracking in query_cursor__advance 2025-12-03 18:06:16 -05:00
Piotr Osiewicz c0b1710f8a Add containing range APIs to query cursor
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
Co-authored-by: John Tur <john-tur@outlook.com>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
Co-authored-by: Will Lillis <will.lillis24@gmail.com>
2025-12-03 18:06:16 -05:00
Riley Bruins 829733a35e fix(query): prevent infinite loop with + and ? quantifiers
**Problem:** A query with a `?` quantifier followed by a `+` quantifier
would hang at 100% CPU usage while iterating through a tree, regardless
of the source content.

**Solution:** Collect all quantifiers in one step, and then add the
required repeat/optional step logic *after* we have determined the
composite quantifier we need to use for the current step.
2025-11-25 03:21:13 -05:00
Amaan Qureshi 00e394f0f1 feat(lib)!: disallow whitespace in supertype syntax 2025-09-28 00:56:30 -04:00
Amaan Qureshi 341665824c fix(lib): validate subtypes in supertype queries 2025-09-28 00:56:30 -04:00
Amaan Qureshi bd02be25d5 fix(lib): allow anonymous nodes in the supertype query syntax 2025-09-28 00:56:30 -04:00
Amaan Qureshi 90ee433c9b fix(lib): account for unreachable patterns with children
Co-authored-by: Will Lillis <will.lillis24@gmail.com>
2025-09-23 17:17:45 -04:00
ObserverOfTime a1640e4fe4 chore: rebuild wasm stdlib 2025-09-21 16:28:08 -04:00
Amaan Qureshi a69367f739 feat: add API for editing points and ranges 2025-09-19 17:40:26 -04:00
Nia f09dc3cf46
fix(wasm): fix alias map size computation
This fixes a crash where parsing with certain languages can lead to a crash due to how the alias map was allocated and laid out in wasm memory
2025-09-18 18:34:27 -04:00