Skip to content

fix: release the options handle after parsing - #56

Open
Garbee wants to merge 2 commits into
tree-sitter:masterfrom
Garbee:fix/parsewithoptions-options-handle-leak
Open

fix: release the options handle after parsing#56
Garbee wants to merge 2 commits into
tree-sitter:masterfrom
Garbee:fix/parsewithoptions-options-handle-leak

Conversation

@Garbee

@Garbee Garbee commented May 23, 2026

Copy link
Copy Markdown

This patch re-uses the existing pattern to release references. Freeing memory to prevent a leak. It also provides a unit test to ensure this does not regress in the future.

With the test, you can run it on the current master to see the failure. Then with the patch, it passes successfully.

AI Disclosure: Claude was used in iterating on the patch and test. Using the test case I ran things manually to validate and confirm. However, I don't yet have a strong enough grasp of tooling with memory analysis in Go to validate the behavior using an external tool that is abstract from runtime operation.

Fixes: #55

Garbee added 2 commits May 23, 2026 11:51
Parser.ParseWithOptions and its UTF-16 / custom-encoding siblings Saved
their *ParseOptions into mattn/go-pointer's process-global handle map but
never Unref'd it, so every call made with non-nil options retained the
options value -- and the progress callback it holds -- for the lifetime
of the process. The input payload handle in the same functions is already
released with a deferred Unref; the options handle was not.

Capture the saved options pointer and defer its Unref, mirroring the input
payload. The parse is synchronous and tree-sitter does not retain the
payload past ts_parser_parse_with_options, so releasing it on return is
safe.

Applies to all four option-taking entry points: ParseWithOptions,
ParseUTF16LEWithOptions, ParseUTF16BEWithOptions, and ParseCustomEncoding.
Assert that a *ParseOptions passed to ParseWithOptions becomes collectable once
the call returns, by counting finalizer runs across many parses, with a
nil-options control loop proving finalizers run at all. Fails on the unfixed
binding (options reclaimed 0/N, pinned in go-pointer's global map) and passes
once the handle is released. No grammar needed — the handle is saved before
parsing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parser.ParseWithOptions leaks a cgo handle on every call with non-nil options (Saved, never Unref'd)

1 participant