Reproduce-first (ms-typescript reallyLargeFile.ts, 2026-07-07): a file * whose root node has hundreds of thousands of FLAT SIBLINGS (580k //// * comment lines in the 3.5 MB fourslash fixture) hung extraction for over * 15 minutes: walk_defs pushed children via index-based ts_node_child(i), * which is O(i) per call in tree-sitter — O(n^2) per wide node (~1.7e11 * iterator steps on the real fil
| 3412 | * children under the root, plus sparse real defs so the breadth check cannot |
| 3413 | * pass vacuously). Returns elapsed milliseconds; stores the def count. */ |
| 3414 | static long extract_wide_flat_ms(int n, int *out_defs) { |
| 3415 | const size_t cap = (size_t)n * 24 + (size_t)8192; /* "// wide filler N\n" <= 24 chars */ |
| 3416 | char *src = malloc(cap); |
| 3417 | if (!src) { |
| 3418 | return -1; |
| 3419 | } |
| 3420 | size_t off = 0; |
| 3421 | /* CONSTANT def count (10), independent of n: defs that scale WITH n make |
| 3422 | * the fixture superlinear through the separate per-def sibling-scan cost |
| 3423 | * (O(defs x siblings)) — on windows-CLANG64 ASan that pushed the ratio |
| 3424 | * of LINEAR walk code to 43x. Ten spread-out defs keep the breadth check |
| 3425 | * honest while the sibling-scan term stays 10 x n = linear. */ |
| 3426 | const int def_stride = n / 10; |
| 3427 | for (int i = 0; i < n; i++) { |
| 3428 | off += (size_t)snprintf(src + off, cap - off, "// wide filler %d\n", i); |
| 3429 | if (i % def_stride == 0) { |
| 3430 | off += (size_t)snprintf(src + off, cap - off, "var wide_a%d = %d;\n", i, i); |
| 3431 | } |
| 3432 | } |
| 3433 | struct timespec a; |
| 3434 | struct timespec b; |
| 3435 | cbm_clock_gettime(CLOCK_MONOTONIC, &a); |
| 3436 | CBMFileResult *r = |
| 3437 | cbm_extract_file(src, (int)off, CBM_LANG_JAVASCRIPT, "proj", "wide.js", 0, NULL, NULL); |
| 3438 | cbm_clock_gettime(CLOCK_MONOTONIC, &b); |
| 3439 | free(src); |
| 3440 | if (!r) { |
| 3441 | return -1; |
| 3442 | } |
| 3443 | *out_defs = r->defs.count; |
| 3444 | cbm_free_result(r); |
| 3445 | return (b.tv_sec - a.tv_sec) * 1000L + (b.tv_nsec - a.tv_nsec) / 1000000L; |
| 3446 | } |
| 3447 | |
| 3448 | TEST(extract_wide_flat_file_is_linear) { |
| 3449 | /* SCALING-RATIO guard: assert the COMPLEXITY CLASS, not a wall-clock |
no test coverage detected