mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-13 09:53:15 +08:00
fix(extract): def-name + callee extraction for scss/sql/cobol/elm/teal
Several grammars produced no Function def and/or no CALLS edge because the name/callee is not in the standard `name`/`function` field. Add precise per-grammar extraction (verified against the actual tree-sitter parse trees): - SCSS: function_statement/mixin_statement name is a `name` CHILD (not a field); the call node is `include_statement` (callee = its identifier child). Also fix scss_call_types, which listed "call_expression" instead of include_statement. - SQL: create_function / invocation carry the name on a nested object_reference > `name` field. - COBOL: call_statement's callee is its `x` string-literal (program name), dequoted. - Elm: value_declaration name is functionDeclarationLeft > lower_case_identifier; function_call_expr callee is target > value_expr > name (value_qid). - Teal: the `local function foo()` form carries the name on a `function_name` child rather than the `name` field. Reproduced by the repro_grammar_* and repro_invariant_enclosing_parity suites (SCSS/SQL/COBOL/Elm/Teal), which were RED for missing defs/calls. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
@@ -78,6 +78,7 @@ const char **cbm_string_dispatch_suffixes(CBMLanguage lang) {
|
||||
// Forward declarations
|
||||
static char *extract_callee_name(CBMArena *a, TSNode node, const char *source, CBMLanguage lang);
|
||||
static char *gotemplate_callee(CBMArena *a, TSNode node, const char *source);
|
||||
static const char *strip_and_validate_string_arg(CBMArena *a, char *text);
|
||||
|
||||
// Lean 4: check if an apply node is inside a type annotation.
|
||||
// Strategy: walk up to the nearest declaration boundary; if the apply falls
|
||||
@@ -614,10 +615,99 @@ static char *extract_dart_callee(CBMArena *a, TSNode node, const char *source, c
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// SCSS: an `@include foo;` is an include_statement whose callee is its
|
||||
// `identifier` child (the mixin name).
|
||||
static char *extract_scss_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
|
||||
if (strcmp(nk, "include_statement") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode id = cbm_find_child_by_kind(node, "identifier");
|
||||
return ts_node_is_null(id) ? NULL : cbm_node_text(a, id, source);
|
||||
}
|
||||
|
||||
// SQL: an `invocation` node's callee is nested object_reference > `name` field
|
||||
// (the same shape as a create_function's name).
|
||||
static char *extract_sql_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
|
||||
if (strcmp(nk, "invocation") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode oref = cbm_find_child_by_kind(node, "object_reference");
|
||||
if (ts_node_is_null(oref)) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode nm = ts_node_child_by_field_name(oref, TS_FIELD("name"));
|
||||
return ts_node_is_null(nm) ? NULL : cbm_node_text(a, nm, source);
|
||||
}
|
||||
|
||||
// COBOL: a `CALL 'HELPER'` is a call_statement whose `x` field is a string
|
||||
// literal naming the called program; the callee is that string sans quotes.
|
||||
static char *extract_cobol_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
|
||||
if (strcmp(nk, "call_statement") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode x = ts_node_child_by_field_name(node, TS_FIELD("x"));
|
||||
if (ts_node_is_null(x)) {
|
||||
x = cbm_find_child_by_kind(node, "string");
|
||||
}
|
||||
if (ts_node_is_null(x)) {
|
||||
return NULL;
|
||||
}
|
||||
char *text = cbm_node_text(a, x, source);
|
||||
return (char *)strip_and_validate_string_arg(a, text);
|
||||
}
|
||||
|
||||
// Elm: a `function_call_expr` has a `target` field; the callee identifier is
|
||||
// target > value_expr > `name` field (value_qid) > lower_case_identifier.
|
||||
static char *extract_elm_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
|
||||
if (strcmp(nk, "function_call_expr") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode target = ts_node_child_by_field_name(node, TS_FIELD("target"));
|
||||
if (ts_node_is_null(target)) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode ve = strcmp(ts_node_type(target), "value_expr") == 0
|
||||
? target
|
||||
: cbm_find_child_by_kind(target, "value_expr");
|
||||
if (ts_node_is_null(ve)) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode qid = ts_node_child_by_field_name(ve, TS_FIELD("name"));
|
||||
if (ts_node_is_null(qid)) {
|
||||
qid = cbm_find_child_by_kind(ve, "value_qid");
|
||||
}
|
||||
if (ts_node_is_null(qid)) {
|
||||
return NULL;
|
||||
}
|
||||
TSNode id = cbm_find_child_by_kind(qid, "lower_case_identifier");
|
||||
if (ts_node_is_null(id)) {
|
||||
// module-qualified call: emit the whole qualified id text
|
||||
return cbm_node_text(a, qid, source);
|
||||
}
|
||||
return cbm_node_text(a, id, source);
|
||||
}
|
||||
|
||||
static char *extract_callee_lang_specific(CBMArena *a, TSNode node, const char *source,
|
||||
CBMLanguage lang) {
|
||||
const char *nk = ts_node_type(node);
|
||||
|
||||
if (lang == CBM_LANG_SCSS) {
|
||||
char *c = extract_scss_callee(a, node, source, nk);
|
||||
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
|
||||
}
|
||||
if (lang == CBM_LANG_SQL) {
|
||||
char *c = extract_sql_callee(a, node, source, nk);
|
||||
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
|
||||
}
|
||||
if (lang == CBM_LANG_COBOL) {
|
||||
char *c = extract_cobol_callee(a, node, source, nk);
|
||||
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
|
||||
}
|
||||
if (lang == CBM_LANG_ELM) {
|
||||
char *c = extract_elm_callee(a, node, source, nk);
|
||||
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
|
||||
}
|
||||
|
||||
if (lang == CBM_LANG_CLOJURE || lang == CBM_LANG_COMMONLISP || lang == CBM_LANG_SCHEME ||
|
||||
lang == CBM_LANG_FENNEL || lang == CBM_LANG_RACKET || lang == CBM_LANG_EMACSLISP) {
|
||||
return extract_lisp_callee(a, node, source, nk);
|
||||
|
||||
@@ -826,6 +826,57 @@ TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Teal: the `local function foo()` form reduces to a function_statement
|
||||
* whose name is carried on a `function_name` child rather than the `name`
|
||||
* field (the field is only populated for the bare `function foo()` form).
|
||||
* func_name_node() already handled the field case above; here we cover the
|
||||
* function_name child so local functions also produce a Function def. */
|
||||
if (lang == CBM_LANG_TEAL && (strcmp(kind, "function_statement") == 0 ||
|
||||
strcmp(kind, "function_signature") == 0)) {
|
||||
TSNode fn = cbm_find_child_by_kind(node, "function_name");
|
||||
if (!ts_node_is_null(fn)) {
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
|
||||
/* SCSS: function_statement/mixin_statement have no `name` field; the def
|
||||
* name is a plain `name` child node. */
|
||||
if (lang == CBM_LANG_SCSS &&
|
||||
(strcmp(kind, "function_statement") == 0 || strcmp(kind, "mixin_statement") == 0)) {
|
||||
TSNode nm = cbm_find_child_by_kind(node, "name");
|
||||
if (!ts_node_is_null(nm)) {
|
||||
return nm;
|
||||
}
|
||||
}
|
||||
|
||||
/* SQL: create_function has no `name` field; the function name is nested as
|
||||
* object_reference > `name` field (an identifier). */
|
||||
if (lang == CBM_LANG_SQL && strcmp(kind, "create_function") == 0) {
|
||||
TSNode oref = cbm_find_child_by_kind(node, "object_reference");
|
||||
if (!ts_node_is_null(oref)) {
|
||||
TSNode nm = ts_node_child_by_field_name(oref, TS_FIELD("name"));
|
||||
if (!ts_node_is_null(nm)) {
|
||||
return nm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Elm: value_declaration carries its name on the
|
||||
* `functionDeclarationLeft` field's function_declaration_left child,
|
||||
* whose first lower_case_identifier is the function name. */
|
||||
if (lang == CBM_LANG_ELM && strcmp(kind, "value_declaration") == 0) {
|
||||
TSNode lhs = ts_node_child_by_field_name(node, TS_FIELD("functionDeclarationLeft"));
|
||||
if (ts_node_is_null(lhs)) {
|
||||
lhs = cbm_find_child_by_kind(node, "function_declaration_left");
|
||||
}
|
||||
if (!ts_node_is_null(lhs)) {
|
||||
TSNode nm = cbm_find_child_by_kind(lhs, "lower_case_identifier");
|
||||
if (!ts_node_is_null(nm)) {
|
||||
return nm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pine Script: function_declaration_statement carries the name on the
|
||||
* `function` field (or `method` field for the method form), not `name`. */
|
||||
if (lang == CBM_LANG_PINE && strcmp(kind, "function_declaration_statement") == 0) {
|
||||
|
||||
@@ -636,7 +636,7 @@ static const char *css_import_types[] = {"import_statement", NULL};
|
||||
// ==================== SCSS ====================
|
||||
static const char *scss_func_types[] = {"mixin_statement", "function_statement", NULL};
|
||||
static const char *scss_module_types[] = {"stylesheet", NULL};
|
||||
static const char *scss_call_types[] = {"call_expression", NULL};
|
||||
static const char *scss_call_types[] = {"call_expression", "include_statement", NULL};
|
||||
static const char *scss_import_types[] = {"import_statement", "use_statement", "include_statement",
|
||||
NULL};
|
||||
static const char *scss_branch_types[] = {"if_statement", NULL};
|
||||
|
||||
Reference in New Issue
Block a user