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.
| 2754 | // be bound to `arr`'s element type via Array<T>.map's `(x: T) => U` signature). The |
| 2755 | // caller passes `expected` = the FUNC type the callback must conform to. |
| 2756 | static void process_callback_arrow(TSLSPContext *ctx, TSNode arrow, const CBMType *expected) { |
| 2757 | if (ts_node_is_null(arrow) || !expected || expected->kind != CBM_TYPE_FUNC) |
| 2758 | return; |
| 2759 | |
| 2760 | CBMScope *saved = ctx->current_scope; |
| 2761 | ctx->current_scope = cbm_scope_push(ctx->arena, saved); |
| 2762 | |
| 2763 | // Tree-sitter typescript shapes for arrow functions: |
| 2764 | // `(x, y) => body` → parameters: formal_parameters |
| 2765 | // `x => body` → parameter: identifier (singular field) |
| 2766 | TSNode params = |
| 2767 | ts_node_child_by_field_name(arrow, "parameters", TS_LSP_FIELD_LEN("parameters")); |
| 2768 | TSNode bare_param = |
| 2769 | ts_node_child_by_field_name(arrow, "parameter", TS_LSP_FIELD_LEN("parameter")); |
| 2770 | int idx = 0; |
| 2771 | if (!ts_node_is_null(bare_param)) { |
| 2772 | // Single bare param: `x => ...`. |
| 2773 | const char *pk = ts_node_type(bare_param); |
| 2774 | if (strcmp(pk, "identifier") == 0) { |
| 2775 | char *nm = node_text(ctx, bare_param); |
| 2776 | const CBMType *pt = |
| 2777 | (expected->data.func.param_types && expected->data.func.param_types[0]) |
| 2778 | ? expected->data.func.param_types[0] |
| 2779 | : cbm_type_unknown(); |
| 2780 | if (nm) |
| 2781 | cbm_scope_bind(ctx->current_scope, nm, pt); |
| 2782 | } |
| 2783 | } else if (!ts_node_is_null(params)) { |
| 2784 | uint32_t pc = ts_node_named_child_count(params); |
| 2785 | for (uint32_t i = 0; i < pc; i++) { |
| 2786 | TSNode p = ts_node_named_child(params, i); |
| 2787 | if (ts_node_is_null(p)) |
| 2788 | continue; |
| 2789 | const char *pk = ts_node_type(p); |
| 2790 | const char *pname = NULL; |
| 2791 | const CBMType *expected_pt = |
| 2792 | (expected->data.func.param_types && expected->data.func.param_types[idx]) |
| 2793 | ? expected->data.func.param_types[idx] |
| 2794 | : NULL; |
| 2795 | |
| 2796 | if (strcmp(pk, "identifier") == 0) { |
| 2797 | pname = node_text(ctx, p); |
| 2798 | } else { |
| 2799 | // required_parameter / optional_parameter — extract pattern. |
| 2800 | TSNode pp = ts_node_child_by_field_name(p, "pattern", TS_LSP_FIELD_LEN("pattern")); |
| 2801 | if (ts_node_is_null(pp)) { |
| 2802 | pp = ts_node_child_by_field_name(p, "name", TS_LSP_FIELD_LEN("name")); |
| 2803 | } |
| 2804 | if (!ts_node_is_null(pp) && strcmp(ts_node_type(pp), "identifier") == 0) { |
| 2805 | pname = node_text(ctx, pp); |
| 2806 | } |
| 2807 | // If the param has its own annotation, use that. Otherwise use expected. |
| 2808 | TSNode tann = ts_node_child_by_field_name(p, "type", TS_LSP_FIELD_LEN("type")); |
| 2809 | if (!ts_node_is_null(tann)) { |
| 2810 | TSNode tch = (strcmp(ts_node_type(tann), "type_annotation") == 0) |
| 2811 | ? ts_node_named_child(tann, 0) |
| 2812 | : tann; |
| 2813 | if (!ts_node_is_null(tch)) { |
no test coverage detected