Files
deusdata--codebase-memory-mcp/tests/repro/repro_new_ts_class_field_arrow.c
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

209 lines
8.5 KiB
C

/*
* repro_new_ts_class_field_arrow.c -- Reproduce-first case for a NEW, un-filed
* bug discovered during QA sweep (2026-06-26).
*
* BUG: TypeScript class field arrow functions are silently dropped from
* the Method definition list AND calls inside them receive the wrong
* enclosing_func_qn (the class QN instead of the method QN).
*
* PATTERN AFFECTED:
* class Foo {
* handleClick = () => {
* helper();
* };
* }
*
* This is an extremely common React/TypeScript pattern for event handlers.
*
* ROOT CAUSE -- TWO co-located defects:
*
* DEFECT A -- extract_defs.c, extract_class_methods() (~line 3578):
* The function iterates the class body's direct children. For each child it
* checks:
* cbm_kind_in_set(method_node, spec->function_node_types)
* "public_field_definition" is NOT in ts_func_types -- only
* "function_declaration", "arrow_function", "method_definition", etc. are.
* So the body-scan loop hits `continue` and the method is never emitted.
*
* The parallel path (extract_func_def, called from walk_defs when the DFS
* visits the inner "arrow_function" node) also fails: it calls
* resolve_toplevel_arrow_name() which only handles the `variable_declarator`
* and `pair` parent cases -- NOT `public_field_definition`. So it returns
* NULL and extract_func_def() returns early with no def emitted.
*
* DEFECT B -- extract_unified.c, push_boundary_scopes() / compute_func_qn():
* When the DFS cursor visits the `arrow_function` node inside
* `public_field_definition`, it IS in ts_func_types so push_boundary_scopes
* calls compute_func_qn(). compute_func_qn() calls resolve_func_name_node()
* which only handles the `variable_declarator` parent -- NOT
* `public_field_definition`. So name_node is NULL -> compute_func_qn
* returns NULL -> no SCOPE_FUNC is pushed for this arrow function.
*
* Consequence: any call inside the arrow function body runs handle_calls()
* with state->enclosing_func_qn still set to state->enclosing_class_qn
* (the class "proj.ts.Foo"), NOT the method "proj.ts.Foo.handleClick".
*
* EXPECTED (correct) behavior:
* A. cbm_extract_file must emit a Method def with name="handleClick"
* and qualified_name containing both "Foo" and "handleClick".
* B. The call to helper() inside handleClick must have
* enclosing_func_qn pointing to the handleClick method, NOT just
* the class "Foo". Specifically enclosing_func_qn must contain
* "handleClick" and must NOT equal the module QN.
*
* ACTUAL (buggy) behavior:
* A. r->defs contains no Method entry for "handleClick" -- the def is
* silently dropped. ASSERT_NOT_NULL(method_def) fires -> RED.
* B. The helper() call has enclosing_func_qn == class QN ("proj.ts.Foo"),
* not the method QN. ASSERT_NOT_NULL(strstr(enc, "handleClick")) fires
* -> RED.
*
* HOW TO CONFIRM THE BUG WITHOUT COMPILING:
* 1. extract_class_methods (extract_defs.c ~3578): iterates body children;
* line ~3620 guards on cbm_kind_in_set(method_node, spec->function_node_types);
* "public_field_definition" is absent from ts_func_types (lang_specs.c ~237)
* -> guard fails -> no Method emitted.
* 2. resolve_toplevel_arrow_name (extract_defs.c ~598): only handles
* variable_declarator and pair parents -- not public_field_definition.
* 3. resolve_func_name_node (extract_unified.c ~91): same gap for
* push_boundary_scopes scope tracking.
*
* FIX LOCATION (not implemented here):
* extract_defs.c extract_class_methods: add a peek-through for
* "public_field_definition" (similar to the decorated_definition peek),
* extract the inner arrow_function's name from the field's "name" child,
* and call push_method_def.
* extract_unified.c resolve_func_name_node: add a "public_field_definition"
* / "field_definition" parent case (similar to the variable_declarator case)
* so compute_func_qn can push a SCOPE_FUNC for the arrow function.
*/
#include "test_framework.h"
#include "cbm.h"
#include <string.h>
static CBMFileResult *rx_ts(const char *src) {
return cbm_extract_file(src, (int)strlen(src), CBM_LANG_TYPESCRIPT, "proj", "ts.ts",
0, NULL, NULL);
}
static CBMDefinition *find_def_by_name(CBMFileResult *r, const char *label, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
CBMDefinition *d = &r->defs.items[i];
if (label && (!d->label || strcmp(d->label, label) != 0))
continue;
if (name && (!d->name || strcmp(d->name, name) != 0))
continue;
return d;
}
return NULL;
}
/*
* repro_new_ts_class_field_arrow_method_def_dropped
*
* DEFECT A: the "handleClick" Method def is not emitted at all.
*
* WHY RED on current code:
* extract_class_methods skips public_field_definition (not in ts_func_types);
* resolve_toplevel_arrow_name only handles variable_declarator/pair parents.
* find_def_by_name returns NULL -> ASSERT_NOT_NULL fires.
*/
TEST(repro_new_ts_class_field_arrow_method_def_dropped) {
static const char *src =
"function helper(): void {}\n"
"\n"
"class Foo {\n"
" handleClick = () => {\n"
" helper();\n"
" };\n"
"}\n";
CBMFileResult *r = rx_ts(src);
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Precondition: the class Foo itself must be extracted. */
CBMDefinition *cls = find_def_by_name(r, "Class", "Foo");
ASSERT_NOT_NULL(cls);
/* Precondition: the free helper() function must be extracted. */
CBMDefinition *helper = find_def_by_name(r, "Function", "helper");
ASSERT_NOT_NULL(helper);
/* DEFECT A PRIMARY ASSERTION: the arrow-function class field must
* be emitted as a Method def under the class.
* WHY RED: extract_class_methods bails out at the cbm_kind_in_set check
* (public_field_definition is not in ts_func_types) without ever calling
* push_method_def; and the walk_defs path fails in resolve_toplevel_arrow_name
* (parent is public_field_definition, not variable_declarator). */
CBMDefinition *method = find_def_by_name(r, "Method", "handleClick");
ASSERT_NOT_NULL(method); /* RED on buggy code */
/* Sanity: the emitted Method must be scoped to its class. */
ASSERT_NOT_NULL(method->qualified_name);
ASSERT_TRUE(strstr(method->qualified_name, "Foo") != NULL);
ASSERT_TRUE(strstr(method->qualified_name, "handleClick") != NULL);
cbm_free_result(r);
PASS();
}
/*
* repro_new_ts_class_field_arrow_call_enclosing_qn
*
* DEFECT B: calls inside the arrow-function body receive enclosing_func_qn
* equal to the CLASS qn, not the METHOD qn.
*
* WHY RED on current code:
* resolve_func_name_node (extract_unified.c) only handles variable_declarator
* arrow parents. For public_field_definition it returns NULL, so compute_func_qn
* returns NULL and no SCOPE_FUNC is pushed. The enclosing scope remains the
* class scope ("proj.ts.Foo"), so state->enclosing_func_qn == class_qn.
* The assertion that enclosing_func_qn contains "handleClick" then FAILS -> RED.
*/
TEST(repro_new_ts_class_field_arrow_call_enclosing_qn) {
static const char *src =
"function helper(): void {}\n"
"\n"
"class Foo {\n"
" handleClick = () => {\n"
" helper();\n"
" };\n"
"}\n";
CBMFileResult *r = rx_ts(src);
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
/* Find the call to helper() inside handleClick. */
const char *enc = NULL;
for (int i = 0; i < r->calls.count; i++) {
if (strcmp(r->calls.items[i].callee_name, "helper") == 0) {
enc = r->calls.items[i].enclosing_func_qn;
break;
}
}
/* The helper() call must be found at all. */
ASSERT_NOT_NULL(enc);
/* DEFECT B PRIMARY ASSERTION: enclosing_func_qn must point to the
* handleClick arrow function, NOT just to the class.
* WHY RED: push_boundary_scopes never pushes a SCOPE_FUNC for the
* arrow function (compute_func_qn returns NULL for public_field_definition
* parents), so the scope stays at the class level -> enc is "proj.ts.Foo"
* which does not contain "handleClick" -> ASSERT_TRUE fires -> RED. */
ASSERT_TRUE(strstr(enc, "handleClick") != NULL); /* RED on buggy code */
cbm_free_result(r);
PASS();
}
/* ---- Suite --------------------------------------------------------------- */
SUITE(repro_new_ts_class_field_arrow) {
RUN_TEST(repro_new_ts_class_field_arrow_method_def_dropped);
RUN_TEST(repro_new_ts_class_field_arrow_call_enclosing_qn);
}