| 6654 | } |
| 6655 | |
| 6656 | static void wd_push(wd_stack_t *s, TSNode node, const char *enclosing_qn) { |
| 6657 | if (s->top >= s->cap) { |
| 6658 | int ncap = s->cap ? s->cap * 2 : 256; |
| 6659 | if (ncap > wd_stack_max()) { |
| 6660 | if (!s->warned) { |
| 6661 | char lim[24]; |
| 6662 | snprintf(lim, sizeof(lim), "%d", wd_stack_max()); |
| 6663 | cbm_log_warn("extract.walk_defs_capped", "limit", lim, "path", |
| 6664 | s->path ? s->path : ""); |
| 6665 | s->warned = true; |
| 6666 | } |
| 6667 | return; // bounded: stop growing (warned, not silent) |
| 6668 | } |
| 6669 | walk_defs_frame_t *nd = safe_realloc(s->data, (size_t)ncap * sizeof(walk_defs_frame_t)); |
| 6670 | if (!nd) { |
| 6671 | /* OOM — safe_realloc already freed the old buffer. Bail cleanly: drop |
| 6672 | * pending frames so the walk_defs loop drains and exits without a NULL |
| 6673 | * deref; extraction keeps whatever was already emitted. */ |
| 6674 | s->data = NULL; |
| 6675 | s->cap = 0; |
| 6676 | s->top = 0; |
| 6677 | return; |
| 6678 | } |
| 6679 | s->data = nd; |
| 6680 | s->cap = ncap; |
| 6681 | } |
| 6682 | s->data[s->top++] = (walk_defs_frame_t){node, enclosing_qn}; |
| 6683 | } |
| 6684 | |
| 6685 | /* Push all children of `node` in REVERSE order (so they pop in source order) |
| 6686 | * using a LINEAR traversal. Index-based ts_node_child(i) is O(i) per call in |
no test coverage detected