From 8e332bc2ac6f5cd78f1aa41395480b674a739261 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sun, 28 Jun 2026 08:45:57 +0200 Subject: [PATCH] fix(purescript): extract exp_apply callees so function applications form CALLS edges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PureScript function application (greet "world", log (...)) is an exp_apply node whose function head is an exp_name child — extract_fp_callee only handled apply/application_expression, and PureScript wasn't routed to it, so the callee was never extracted and no call to greet was found. Route PureScript through extract_fp_callee (like Haskell/OCaml) and teach it exp_apply: the head is an exp_name (its text is the qualified function name), and curried applications nest exp_apply, so descend the function head to recover the leftmost callee. Haskell/OCaml apply handling is unchanged. Signed-off-by: Martin Vogel --- internal/cbm/extract_calls.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/cbm/extract_calls.c b/internal/cbm/extract_calls.c index 51658205f..2f68a9aaa 100644 --- a/internal/cbm/extract_calls.c +++ b/internal/cbm/extract_calls.c @@ -322,14 +322,24 @@ static char *extract_callee_from_fields(CBMArena *a, TSNode node, const char *so // Haskell/OCaml: extract callee from apply/infix nodes. static char *extract_fp_callee(CBMArena *a, TSNode node, const char *source, const char *nk) { - if (strcmp(nk, "apply") == 0 || strcmp(nk, "application_expression") == 0) { + if (strcmp(nk, "apply") == 0 || strcmp(nk, "application_expression") == 0 || + strcmp(nk, "exp_apply") == 0) { if (ts_node_child_count(node) > 0) { TSNode callee = ts_node_child(node, 0); const char *ck = ts_node_type(callee); if (strcmp(ck, "identifier") == 0 || strcmp(ck, "variable") == 0 || - strcmp(ck, "constructor") == 0 || strcmp(ck, "value_path") == 0) { + strcmp(ck, "constructor") == 0 || strcmp(ck, "value_path") == 0 || + /* PureScript: exp_apply's function head is an `exp_name` whose + * text is the (possibly qualified) function name. */ + strcmp(ck, "exp_name") == 0) { return cbm_node_text(a, callee, source); } + /* Curried application `f a b` nests exp_apply/apply — descend the + * function head to recover the leftmost callee. */ + if (strcmp(ck, "exp_apply") == 0 || strcmp(ck, "apply") == 0 || + strcmp(ck, "application_expression") == 0) { + return extract_fp_callee(a, callee, source, ck); + } } } if (strcmp(nk, "infix") == 0 || strcmp(nk, "infix_expression") == 0) { @@ -1081,7 +1091,7 @@ static char *extract_callee_lang_specific(CBMArena *a, TSNode node, const char * if (lang == CBM_LANG_ERLANG) { return extract_erlang_callee(a, node, source, nk); } - if (lang == CBM_LANG_HASKELL || lang == CBM_LANG_OCAML) { + if (lang == CBM_LANG_HASKELL || lang == CBM_LANG_OCAML || lang == CBM_LANG_PURESCRIPT) { return extract_fp_callee(a, node, source, nk); } if (lang == CBM_LANG_WOLFRAM && strcmp(nk, "apply") == 0) {