| 2517 | // Uses WalkState flags instead of parent-chain walks for O(1) context checks. |
| 2518 | |
| 2519 | void handle_usages(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec, WalkState *state) { |
| 2520 | if (emit_direct_perl_coderef_usage(ctx, node, state->enclosing_func_qn, |
| 2521 | active_lexical_scope_id(state))) { |
| 2522 | return; |
| 2523 | } |
| 2524 | if (inside_direct_perl_coderef(ctx, node)) { |
| 2525 | return; |
| 2526 | } |
| 2527 | bool reference_node = is_reference_node(node, ctx->language); |
| 2528 | const CBMOccurrenceSpec *occurrence = &occurrence_specs[ctx->language]; |
| 2529 | bool possible_binding_leaf = |
| 2530 | ts_node_is_named(node) && ts_node_named_child_count(node) == 0 && |
| 2531 | (state->inside_import || is_exact_language_binding(ctx, node, state) || |
| 2532 | is_policy_binding(ctx, node, occurrence)); |
| 2533 | if (!reference_node && !possible_binding_leaf) { |
| 2534 | return; |
| 2535 | } |
| 2536 | |
| 2537 | // An invocation relationship replaces only the exact AST occurrence it |
| 2538 | // consumes. Receivers, qualifiers, computed keys, arguments, and nested |
| 2539 | // bodies remain ordinary value references. Explicit callable syntax keeps |
| 2540 | // that exact occurrence as a typed reference instead of erasing it. |
| 2541 | bool exact_callee = |
| 2542 | (!ts_node_is_null(state->callee_expr) && ts_node_eq(node, state->callee_expr)) || |
| 2543 | (!ts_node_is_null(state->callee_leaf) && ts_node_eq(node, state->callee_leaf)); |
| 2544 | if (exact_callee) { |
| 2545 | if (reference_node && state->invocation_kind == CBM_INVOCATION_CALLABLE_REFERENCE) { |
| 2546 | char *name = reference_name(ctx, node); |
| 2547 | if (name && name[0] && !cbm_is_keyword(name, ctx->language)) { |
| 2548 | CBMUsage usage = {0}; |
| 2549 | usage.ref_name = name; |
| 2550 | usage.enclosing_func_qn = state->enclosing_func_qn; |
| 2551 | usage.lexical_scope_id = usage_lexical_scope_id_for_node(ctx, state, node); |
| 2552 | /* Kotlin callable-reference syntax is only a precision |
| 2553 | * candidate. The semantic pass decides whether `::name` |
| 2554 | * denotes one exact callable; unresolved properties and |
| 2555 | * ambiguous values must remain ordinary USAGE. */ |
| 2556 | if (ctx->language == CBM_LANG_KOTLIN) { |
| 2557 | usage.kind = CBM_USAGE_VALUE; |
| 2558 | usage.may_be_call_reference = true; |
| 2559 | } else { |
| 2560 | usage.kind = CBM_USAGE_CALL_REFERENCE; |
| 2561 | } |
| 2562 | usage.site_start_byte = ts_node_start_byte(node); |
| 2563 | usage.site_end_byte = ts_node_end_byte(node); |
| 2564 | cbm_usages_push(&ctx->result->usages, ctx->arena, usage); |
| 2565 | } |
| 2566 | } |
| 2567 | return; |
| 2568 | } |
| 2569 | if (is_forward_sibling_callee(ctx->language, node)) { |
| 2570 | return; |
| 2571 | } |
| 2572 | if (is_call_argument_label_walk(node, state)) { |
| 2573 | return; |
| 2574 | } |
| 2575 | // Imports do not emit ordinary usages, but their binding occurrence must |
| 2576 | // be recorded before the subtree is skipped. |
no test coverage detected