commit a5e5ec8910ea35ebb86dcba7f58333d9d4caca47
parent 5226801be26419f9a8277cbc35592cc2f0004d64
Author: Sean Dewar <6256228+seandewar@users.noreply.github.com>
Date: Fri, 16 Jan 2026 00:53:55 +0000
fix(api): parse_expression crash with unopened ] and node
Problem: nvim_parse_expression null pointer dereference with unmatched ]
followed by a node.
Solution: if ast_stack was empty, set new_top_node_p to top of the stack after
pushing the list literal node; similar to what's done for curlies.
This bug was originally found by a Matrix user, but I couldn't remember how to
trigger it... Ran into the other crash while finding a repro. :P
Diffstat:
3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
@@ -2406,7 +2406,7 @@ viml_pexpr_parse_valid_colon:
// Always drop the topmost value:
//
// 1. When want_node != kENodeValue topmost item on stack is
- // a *finished* left operand, which may as well be "{@a}" which
+ // a *finished* left operand, which may as well be "[@a]" which
// needs not be finished again.
// 2. Otherwise it is pointing to NULL what nobody wants.
kv_drop(ast_stack, 1);
@@ -2417,6 +2417,7 @@ viml_pexpr_parse_valid_colon:
cur_node->children = *top_node_p;
}
*top_node_p = cur_node;
+ new_top_node_p = top_node_p;
goto viml_pexpr_parse_bracket_closing_error;
}
if (want_node == kENodeValue) {
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
@@ -3242,6 +3242,8 @@ describe('API', function()
api.nvim_input(':<C-r>=')
api.nvim_input('1bork/') -- #29648
assert_alive()
+ api.nvim_input('<C-u>];')
+ assert_alive()
api.nvim_parse_expression('a{b}', '', false)
assert_alive()
end)
diff --git a/test/unit/viml/expressions/parser_tests.lua b/test/unit/viml/expressions/parser_tests.lua
@@ -4755,6 +4755,38 @@ return function(itp, _check_parsing, hl, fmtn)
hl('InvalidList', ']'),
})
+ check_parsing(']a', {
+ -- 01
+ ast = {
+ {
+ 'OpMissing:0:1:',
+ children = {
+ 'ListLiteral:0:0:',
+ 'PlainIdentifier(scope=0,ident=a):0:1:a',
+ },
+ },
+ },
+ err = {
+ arg = ']a',
+ msg = 'E15: Unexpected closing figure brace: %.*s',
+ },
+ }, {
+ hl('InvalidList', ']'),
+ hl('InvalidIdentifierName', 'a'),
+ }, {
+ [1] = {
+ ast = {
+ len = 1,
+ ast = {
+ 'ListLiteral:0:0:',
+ },
+ },
+ hl_fs = {
+ [2] = REMOVE_THIS,
+ },
+ },
+ })
+
check_parsing('[] []', {
-- 01234
ast = {