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

This commit is contained in:
Tim Vermeulen 2026-07-28 22:25:25 +02:00 committed by Will Lillis
parent c279989118
commit 869638f6cf
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.
void ts_subtree_summarize_children(
MutableSubtree self,
@ -386,20 +395,25 @@ void ts_subtree_summarize_children(
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);
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;
if (ts_subtree_symbol(child) == ts_builtin_sym_error_repeat) {
// Refund an `_ERROR` child's extent penalty, which this node re-charges
// 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));
ts_assert(ts_subtree_error_cost(child) >= extent_cost);
self.ptr->error_cost += ts_subtree_error_cost(child) - extent_cost;
} else {
self.ptr->error_cost += ts_subtree_error_cost(child);
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_repeat
) {
self.ptr->error_cost +=
ERROR_COST_PER_RECOVERY +
ERROR_COST_PER_SKIPPED_CHAR * self.ptr->size.bytes +
ERROR_COST_PER_SKIPPED_LINE * self.ptr->size.extent.row;
self.ptr->error_cost += ts_subtree__error_extent_cost(self.ptr->size);
}
if (self.ptr->child_count > 0) {

View file

@ -187,3 +187,23 @@ function main(x) {
(member_expression (identifier) (property_identifier))
(arguments (string (string_fragment))))))
(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))))