fix(lib): accumulate error costs through hidden error nodes

(cherry picked from commit 869638f6cf)
This commit is contained in:
Tim Vermeulen 2026-07-28 22:25:25 +02:00 committed by Christian Clason
parent 15adfa9e51
commit f837fc9813
2 changed files with 48 additions and 17 deletions

View file

@ -335,6 +335,15 @@ void ts_subtree_compress(
} }
} }
// The part of an error node's cost that penalizes the extent it spans, as
// opposed to the cost of its contents.
static inline uint32_t ts_subtree__error_extent_cost(Length size) {
return
ERROR_COST_PER_RECOVERY +
ERROR_COST_PER_SKIPPED_CHAR * size.bytes +
ERROR_COST_PER_SKIPPED_LINE * size.extent.row;
}
// Assign all of the node's properties that depend on its children. // Assign all of the node's properties that depend on its children.
void ts_subtree_summarize_children( void ts_subtree_summarize_children(
MutableSubtree self, MutableSubtree self,
@ -386,20 +395,25 @@ void ts_subtree_summarize_children(
lookahead_end_byte = child_lookahead_end_byte; lookahead_end_byte = child_lookahead_end_byte;
} }
if (ts_subtree_symbol(child) != ts_builtin_sym_error_repeat) {
self.ptr->error_cost += ts_subtree_error_cost(child);
}
uint32_t grandchild_count = ts_subtree_child_count(child); uint32_t grandchild_count = ts_subtree_child_count(child);
if ( if (ts_subtree_symbol(child) == ts_builtin_sym_error_repeat) {
self.ptr->symbol == ts_builtin_sym_error || // Refund an `_ERROR` child's extent penalty, which this node re-charges
self.ptr->symbol == ts_builtin_sym_error_repeat // as part of its own extent below, so that the grouping is cost-neutral.
) { uint32_t extent_cost = ts_subtree__error_extent_cost(ts_subtree_size(child));
if (!ts_subtree_extra(child) && !(ts_subtree_is_error(child) && grandchild_count == 0)) { ts_assert(ts_subtree_error_cost(child) >= extent_cost);
if (ts_subtree_visible(child)) { self.ptr->error_cost += ts_subtree_error_cost(child) - extent_cost;
self.ptr->error_cost += ERROR_COST_PER_SKIPPED_TREE; } else {
} else if (grandchild_count > 0) { self.ptr->error_cost += ts_subtree_error_cost(child);
self.ptr->error_cost += ERROR_COST_PER_SKIPPED_TREE * child.ptr->visible_child_count; if (
self.ptr->symbol == ts_builtin_sym_error ||
self.ptr->symbol == ts_builtin_sym_error_repeat
) {
if (!ts_subtree_extra(child) && !(ts_subtree_is_error(child) && grandchild_count == 0)) {
if (ts_subtree_visible(child)) {
self.ptr->error_cost += ERROR_COST_PER_SKIPPED_TREE;
} else if (grandchild_count > 0) {
self.ptr->error_cost += ERROR_COST_PER_SKIPPED_TREE * child.ptr->visible_child_count;
}
} }
} }
} }
@ -443,10 +457,7 @@ void ts_subtree_summarize_children(
self.ptr->symbol == ts_builtin_sym_error || self.ptr->symbol == ts_builtin_sym_error ||
self.ptr->symbol == ts_builtin_sym_error_repeat self.ptr->symbol == ts_builtin_sym_error_repeat
) { ) {
self.ptr->error_cost += self.ptr->error_cost += ts_subtree__error_extent_cost(self.ptr->size);
ERROR_COST_PER_RECOVERY +
ERROR_COST_PER_SKIPPED_CHAR * self.ptr->size.bytes +
ERROR_COST_PER_SKIPPED_LINE * self.ptr->size.extent.row;
} }
if (self.ptr->child_count > 0) { if (self.ptr->child_count > 0) {

View file

@ -187,3 +187,23 @@ function main(x) {
(member_expression (identifier) (property_identifier)) (member_expression (identifier) (property_identifier))
(arguments (string (string_fragment)))))) (arguments (string (string_fragment))))))
(return_statement (object))))) (return_statement (object)))))
===================================================
Stray tokens around a parenthesized ternary
===================================================
x
// one
( a ? b : c ) :
// two
y.
---
(program
(ERROR
(call_expression
(identifier)
(comment)
(arguments (ternary_expression (identifier) (identifier) (identifier))))
(ERROR (comment) (identifier))))