Fix treesit crash (bug#71681)
To reproduce the problem: 0. emacs -Q 1. eval: (add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode)) 2. C-x v L 3. in the *vc-change-log* buffer move point to the commit20af58d3a1
4. type D 5. crash caused by diff-font-lock-syntax fontification that uses treesit Emacs:6f2036243f
(2024-06-23, latest master) Tree-sitter: 3da7deed (2024-06-08, version 0.22.6) The immediate cause of the crash is that tree-sitter accessed a node's tree, but the tree is already deleted. Commenting out the ts_tree_delete line in treesit_ensure_parsed can "fix" the crash. What happended, I think, is this: 1. Buffer modified, parser->need_reparse set to true, parser->timestamp incremented. 2. A node is created from the parser, this node has the old tree but the new timestamp (bad!). 3. Parser re-parses (treesit_ensure_parsed), new tree created, old tree deleted. 4. Ftreesit_query_capture accessed the old node, and the old tree, crash. We shouldn't bump the parser timestamp when we set parser->need_reparse to true; instead, we should bump the timestamp when we actually reparsed and created a new tree. * src/treesit.c (treesit_record_change): Don't bump parser timestamp. (treesit_sync_visible_region): Don't bump parser timestamp. (Ftreesit_parser_set_included_ranges): Don't bump parser timestamp. (treesit_ensure_parsed): Bump parser timestamp. (Ftreesit_query_capture): Add node check.
This commit is contained in:
parent
eaf2dc96c1
commit
8819e5a45d
1 changed files with 14 additions and 14 deletions
|
@ -846,7 +846,6 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
|
|||
treesit_tree_edit_1 (tree, start_offset, old_end_offset,
|
||||
new_end_offset);
|
||||
XTS_PARSER (lisp_parser)->need_reparse = true;
|
||||
XTS_PARSER (lisp_parser)->timestamp++;
|
||||
|
||||
/* VISIBLE_BEG/END records tree-sitter's range of view in
|
||||
the buffer. We need to adjust them when tree-sitter's
|
||||
|
@ -948,10 +947,7 @@ treesit_sync_visible_region (Lisp_Object parser)
|
|||
this function is called), we need to reparse. */
|
||||
if (visible_beg != BUF_BEGV_BYTE (buffer)
|
||||
|| visible_end != BUF_ZV_BYTE (buffer))
|
||||
{
|
||||
XTS_PARSER (parser)->need_reparse = true;
|
||||
XTS_PARSER (parser)->timestamp++;
|
||||
}
|
||||
XTS_PARSER (parser)->need_reparse = true;
|
||||
|
||||
/* Before we parse or set ranges, catch up with the narrowing
|
||||
situation. We change visible_beg and visible_end to match
|
||||
|
@ -1090,6 +1086,7 @@ treesit_ensure_parsed (Lisp_Object parser)
|
|||
|
||||
XTS_PARSER (parser)->tree = new_tree;
|
||||
XTS_PARSER (parser)->need_reparse = false;
|
||||
XTS_PARSER (parser)->timestamp++;
|
||||
|
||||
/* After-change functions should run at the very end, most crucially
|
||||
after need_reparse is set to false, this way if the function
|
||||
|
@ -1725,7 +1722,6 @@ buffer. */)
|
|||
ranges);
|
||||
|
||||
XTS_PARSER (parser)->need_reparse = true;
|
||||
XTS_PARSER (parser)->timestamp++;
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
@ -2923,11 +2919,10 @@ be completely in the region.
|
|||
|
||||
If NODE-ONLY is non-nil, return a list of nodes.
|
||||
|
||||
Besides a node, NODE can also be a parser, in which case the root node
|
||||
of that parser is used.
|
||||
NODE can also be a language symbol, in which case the root node of a
|
||||
parser for that language is used. If such a parser doesn't exist, it
|
||||
is created.
|
||||
Besides a node, NODE can be a parser, in which case the root node of
|
||||
that parser is used. NODE can also be a language symbol, in which case
|
||||
the root node of a parser for that language is used. If such a parser
|
||||
doesn't exist, it is created.
|
||||
|
||||
Signal `treesit-query-error' if QUERY is malformed or something else
|
||||
goes wrong. You can use `treesit-query-validate' to validate and debug
|
||||
|
@ -2941,8 +2936,13 @@ the query. */)
|
|||
|
||||
treesit_initialize ();
|
||||
|
||||
/* Resolve NODE into an actual node. */
|
||||
/* Resolve NODE into an actual node, signals if node not
|
||||
up-to-date. */
|
||||
Lisp_Object lisp_node = treesit_resolve_node (node);
|
||||
/* As of right now, the node returned by treesit_resolve_node always
|
||||
passes treesit_check_node; but it might not be true in the future,
|
||||
so adding the line below just to be safe. */
|
||||
treesit_check_node (node);
|
||||
|
||||
/* Extract C values from Lisp objects. */
|
||||
TSNode treesit_node = XTS_NODE (lisp_node)->node;
|
||||
|
@ -2970,8 +2970,8 @@ the query. */)
|
|||
&signal_symbol, &signal_data))
|
||||
xsignal (signal_symbol, signal_data);
|
||||
|
||||
/* WARN: After this point, free TREESIT_QUERY and CURSOR before every
|
||||
signal and return if NEEDS_TO_FREE_QUERY_AND_CURSOR is true. */
|
||||
/* WARN: After this point, if NEEDS_TO_FREE_QUERY_AND_CURSOR is true,
|
||||
free TREESIT_QUERY and CURSOR before every signal and return. */
|
||||
|
||||
/* Set query range. */
|
||||
if (!NILP (beg) && !NILP (end))
|
||||
|
|
Loading…
Add table
Reference in a new issue