Compute function QN for scope tracking (mirrors cbm_enclosing_func_qn logic).
| 695 | |
| 696 | // Compute function QN for scope tracking (mirrors cbm_enclosing_func_qn logic). |
| 697 | static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec, |
| 698 | WalkState *state) { |
| 699 | (void)spec; |
| 700 | if (ctx->language == CBM_LANG_WOLFRAM) { |
| 701 | return compute_wolfram_func_qn(ctx, node); |
| 702 | } |
| 703 | if (ctx->language == CBM_LANG_OBJECTSCRIPT_UDL) { |
| 704 | return objectscript_get_method_qn(ctx, node, state->enclosing_class_qn); |
| 705 | } |
| 706 | if (ctx->language == CBM_LANG_OBJECTSCRIPT_ROUTINE) { |
| 707 | const char *kind = ts_node_type(node); |
| 708 | TSNode tag = {0}; |
| 709 | if (strcmp(kind, "procedure") == 0) { |
| 710 | tag = cbm_find_child_by_kind(node, "tag"); |
| 711 | } else if (strcmp(kind, "tag") == 0) { |
| 712 | /* The enclosing procedure owns the full callable range and already |
| 713 | * pushed this QN. Do not create a nested label-only function scope. */ |
| 714 | TSNode parent = ts_node_parent(node); |
| 715 | if (ts_node_is_null(parent) || strcmp(ts_node_type(parent), "procedure") != 0) { |
| 716 | tag = node; |
| 717 | } |
| 718 | } |
| 719 | if (!ts_node_is_null(tag)) { |
| 720 | char *name = cbm_node_text(ctx->arena, tag, ctx->source); |
| 721 | if (name && name[0]) { |
| 722 | return cbm_fqn_compute(ctx->arena, ctx->project, ctx->rel_path, name); |
| 723 | } |
| 724 | } |
| 725 | return NULL; |
| 726 | } |
| 727 | |
| 728 | /* CFML tag dialect: <cffunction name="foo"> is a cf_function_tag whose name |
| 729 | * lives in a cf_attribute, not a `name` field — gate here where ctx->source |
| 730 | * is available to read the attribute. Other CFML func nodes (embedded |
| 731 | * CFScript function_declaration/_expression) fall through to the shared |
| 732 | * resolver below. */ |
| 733 | if (ctx->language == CBM_LANG_CFML && strcmp(ts_node_type(node), "cf_function_tag") == 0) { |
| 734 | return compute_cfml_func_qn(ctx, node); |
| 735 | } |
| 736 | |
| 737 | /* Go templates: {{ define "x" }} is a define_action whose name is a quoted |
| 738 | * string literal — strip the quotes here (the shared resolver has no source). */ |
| 739 | if (ctx->language == CBM_LANG_GOTEMPLATE) { |
| 740 | return compute_gotemplate_func_qn(ctx, node); |
| 741 | } |
| 742 | |
| 743 | /* Lisp family (Clojure/Scheme/Racket): the def node is a list/list_lit, a |
| 744 | * very general kind that also matches plain call forms. The shared resolver |
| 745 | * has no source pointer to read the head symbol, so the def-vs-call gate |
| 746 | * lives here (we have ctx->source). Non-def lists return NULL → no scope |
| 747 | * pushed → the in-body call sources to the enclosing def, not the Module. */ |
| 748 | if (ctx->language == CBM_LANG_CLOJURE || ctx->language == CBM_LANG_SCHEME || |
| 749 | ctx->language == CBM_LANG_RACKET) { |
| 750 | return compute_lisp_func_qn(ctx, node); |
| 751 | } |
| 752 | |
| 753 | /* Elixir: def/defp/defmacro are `call` nodes (so is every in-body call). |
| 754 | * Gate on the def-macro target text so only definitions push a scope. */ |
no test coverage detected