| 2536 | } |
| 2537 | |
| 2538 | static void process_function_like(PHPLSPContext *ctx, TSNode node) { |
| 2539 | const char *kind = ts_node_type(node); |
| 2540 | bool is_method = (strcmp(kind, "method_declaration") == 0); |
| 2541 | bool is_named = is_method || (strcmp(kind, "function_definition") == 0) || |
| 2542 | (strcmp(kind, "function_static_declaration") == 0); |
| 2543 | |
| 2544 | /* Save context. */ |
| 2545 | CBMScope *saved_scope = ctx->current_scope; |
| 2546 | const char *saved_func = ctx->enclosing_func_qn; |
| 2547 | |
| 2548 | ctx->current_scope = cbm_scope_push(ctx->arena, ctx->current_scope); |
| 2549 | |
| 2550 | /* Determine func QN. Mirror what the unified extractor produces: classes |
| 2551 | * and free functions are namespaced by file module_qn, NOT the PHP |
| 2552 | * namespace declaration (the unified extractor ignores `namespace`). |
| 2553 | * Method QN = enclosing_class_qn + "." + method_name. Free function QN = |
| 2554 | * module_qn + "." + name. */ |
| 2555 | if (is_named) { |
| 2556 | TSNode name_node = ts_node_child_by_field_name(node, "name", 4); |
| 2557 | if (!ts_node_is_null(name_node)) { |
| 2558 | char *fname = php_node_text(ctx, name_node); |
| 2559 | if (fname) { |
| 2560 | if (is_method && ctx->enclosing_class_qn) { |
| 2561 | ctx->enclosing_func_qn = |
| 2562 | cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->enclosing_class_qn, fname); |
| 2563 | } else if (ctx->module_qn) { |
| 2564 | ctx->enclosing_func_qn = |
| 2565 | cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->module_qn, fname); |
| 2566 | } else { |
| 2567 | ctx->enclosing_func_qn = cbm_arena_strdup(ctx->arena, fname); |
| 2568 | } |
| 2569 | } |
| 2570 | } |
| 2571 | } |
| 2572 | |
| 2573 | /* Parameters. */ |
| 2574 | TSNode params = ts_node_child_by_field_name(node, "parameters", 10); |
| 2575 | bind_typed_parameters(ctx, params); |
| 2576 | |
| 2577 | /* Closure `use ($a, $b)` clause: copy captured-variable types from the |
| 2578 | * parent scope into the closure scope. tree-sitter-php emits an |
| 2579 | * `anonymous_function_use_clause` child of `anonymous_function`. */ |
| 2580 | if (strcmp(kind, "anonymous_function") == 0) { |
| 2581 | uint32_t fnc = ts_node_child_count(node); |
| 2582 | for (uint32_t i = 0; i < fnc; i++) { |
| 2583 | TSNode c = ts_node_child(node, i); |
| 2584 | if (ts_node_is_null(c) || !ts_node_is_named(c)) |
| 2585 | continue; |
| 2586 | if (strcmp(ts_node_type(c), "anonymous_function_use_clause") != 0) |
| 2587 | continue; |
| 2588 | uint32_t unc = ts_node_child_count(c); |
| 2589 | for (uint32_t j = 0; j < unc; j++) { |
| 2590 | TSNode v = ts_node_child(c, j); |
| 2591 | if (ts_node_is_null(v) || !ts_node_is_named(v)) |
| 2592 | continue; |
| 2593 | if (strcmp(ts_node_type(v), "variable_name") != 0) |
| 2594 | continue; |
| 2595 | char *vt = php_node_text(ctx, v); |
no test coverage detected