Process an arrow_function or function_expression as a callback whose param types are contextually typed by the enclosing call (e.g. `arr.map(x => x.length)` — `x` should be bound to `arr`'s element type via Array .map's `(x: T) => U` signature). The caller passes `expected` = the FUNC type the callback must conform to.
| 2404 | // be bound to `arr`'s element type via Array<T>.map's `(x: T) => U` signature). The |
| 2405 | // caller passes `expected` = the FUNC type the callback must conform to. |
| 2406 | static void process_callback_arrow(TSLSPContext *ctx, TSNode arrow, const CBMType *expected) { |
| 2407 | if (ts_node_is_null(arrow) || !expected || expected->kind != CBM_TYPE_FUNC) |
| 2408 | return; |
| 2409 | |
| 2410 | CBMScope *saved = ctx->current_scope; |
| 2411 | ctx->current_scope = cbm_scope_push(ctx->arena, saved); |
| 2412 | |
| 2413 | // Tree-sitter typescript shapes for arrow functions: |
| 2414 | // `(x, y) => body` → parameters: formal_parameters |
| 2415 | // `x => body` → parameter: identifier (singular field) |
| 2416 | TSNode params = |
| 2417 | ts_node_child_by_field_name(arrow, "parameters", TS_LSP_FIELD_LEN("parameters")); |
| 2418 | TSNode bare_param = |
| 2419 | ts_node_child_by_field_name(arrow, "parameter", TS_LSP_FIELD_LEN("parameter")); |
| 2420 | int idx = 0; |
| 2421 | if (!ts_node_is_null(bare_param)) { |
| 2422 | // Single bare param: `x => ...`. |
| 2423 | const char *pk = ts_node_type(bare_param); |
| 2424 | if (strcmp(pk, "identifier") == 0) { |
| 2425 | char *nm = node_text(ctx, bare_param); |
| 2426 | const CBMType *pt = |
| 2427 | (expected->data.func.param_types && expected->data.func.param_types[0]) |
| 2428 | ? expected->data.func.param_types[0] |
| 2429 | : cbm_type_unknown(); |
| 2430 | if (nm) |
| 2431 | cbm_scope_bind(ctx->current_scope, nm, pt); |
| 2432 | } |
| 2433 | } else if (!ts_node_is_null(params)) { |
| 2434 | uint32_t pc = ts_node_named_child_count(params); |
| 2435 | for (uint32_t i = 0; i < pc; i++) { |
| 2436 | TSNode p = ts_node_named_child(params, i); |
| 2437 | if (ts_node_is_null(p)) |
| 2438 | continue; |
| 2439 | const char *pk = ts_node_type(p); |
| 2440 | const char *pname = NULL; |
| 2441 | const CBMType *expected_pt = |
| 2442 | (expected->data.func.param_types && expected->data.func.param_types[idx]) |
| 2443 | ? expected->data.func.param_types[idx] |
| 2444 | : NULL; |
| 2445 | |
| 2446 | if (strcmp(pk, "identifier") == 0) { |
| 2447 | pname = node_text(ctx, p); |
| 2448 | } else { |
| 2449 | // required_parameter / optional_parameter — extract pattern. |
| 2450 | TSNode pp = ts_node_child_by_field_name(p, "pattern", TS_LSP_FIELD_LEN("pattern")); |
| 2451 | if (ts_node_is_null(pp)) { |
| 2452 | pp = ts_node_child_by_field_name(p, "name", TS_LSP_FIELD_LEN("name")); |
| 2453 | } |
| 2454 | if (!ts_node_is_null(pp) && strcmp(ts_node_type(pp), "identifier") == 0) { |
| 2455 | pname = node_text(ctx, pp); |
| 2456 | } |
| 2457 | // If the param has its own annotation, use that. Otherwise use expected. |
| 2458 | TSNode tann = ts_node_child_by_field_name(p, "type", TS_LSP_FIELD_LEN("type")); |
| 2459 | if (!ts_node_is_null(tann)) { |
| 2460 | TSNode tch = (strcmp(ts_node_type(tann), "type_annotation") == 0) |
| 2461 | ? ts_node_named_child(tann, 0) |
| 2462 | : tann; |
| 2463 | if (!ts_node_is_null(tch)) { |
no test coverage detected