Skip to content

Commit fa19d0c

Browse files
author
Your Name
committed
feat(writes): expand WRITES detection to all obj.field = value assignments
Previously handle_readwrites only captured this.X = value (requiring the receiver to be this/base/self). In JS/TS, most state mutations use obj.field = value where obj is a parameter or local variable: filter.captureNodeId = captureNodeIds session.recordedFiles = files metadata.startTime = clipStartTime These were invisible to WRITES edge detection. Fix: remove the this/base/self receiver check from the member_expression branch. Now extracts the property name from ANY obj.field = value pattern. The registry resolution step in pass_usages.c filters out names that don't match any Property/Method node, preventing noise from built-in properties like x.length or result.data. Tested on JS repo: WRITES 1 → 14 edges. New edges correctly identify functions that mutate object state (getUpdaterStatusById → status, updateSimulatorSessionFileUploadStatus → status). Requires full reindex.
1 parent 09b843c commit fa19d0c

File tree

1 file changed

+16
-23
lines changed

1 file changed

+16
-23
lines changed

internal/cbm/extract_semantic.c

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -275,35 +275,28 @@ void handle_readwrites(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec,
275275
cbm_rw_push(&ctx->result->rw, ctx->arena, rw);
276276
}
277277
}
278-
/* member_access_expression: handle this.Property = value
279-
* for C#, Java, TypeScript, JavaScript.
280-
* Extracts the property name when the receiver is "this" or "base". */
278+
/* member_access_expression / member_expression: obj.field = value
279+
* Captures property writes for ALL receiver types, not just "this".
280+
* In JS/TS, state mutations are often obj.field = value where obj is a
281+
* parameter or local variable (filter.status, session.recordedFiles).
282+
* The extracted property name goes through registry resolution in
283+
* pass_usages.c — names that don't match any Property/Method node
284+
* are silently dropped, filtering out noise like x.length = 5. */
281285
else if (strcmp(lk, "member_access_expression") == 0 ||
282286
strcmp(lk, "member_expression") == 0) {
283-
TSNode expr = ts_node_child_by_field_name(left, "expression", 10);
284287
TSNode name_node = ts_node_child_by_field_name(left, "name", 4);
285-
/* member_expression (JS/TS) uses "object" + "property" field names */
288+
/* member_expression (JS/TS) uses "property" field name */
286289
if (ts_node_is_null(name_node)) {
287290
name_node = ts_node_child_by_field_name(left, "property", 8);
288291
}
289-
if (ts_node_is_null(expr)) {
290-
expr = ts_node_child_by_field_name(left, "object", 6);
291-
}
292-
if (!ts_node_is_null(name_node) && !ts_node_is_null(expr)) {
293-
const char *ek = ts_node_type(expr);
294-
/* Accept: this.X, base.X, self.X (Python) */
295-
if (strcmp(ek, "this_expression") == 0 ||
296-
strcmp(ek, "this") == 0 ||
297-
strcmp(ek, "base_expression") == 0 ||
298-
strcmp(ek, "self") == 0) {
299-
char *name = cbm_node_text(ctx->arena, name_node, ctx->source);
300-
if (name && name[0] && !cbm_is_keyword(name, ctx->language)) {
301-
CBMReadWrite rw;
302-
rw.var_name = name;
303-
rw.is_write = true;
304-
rw.enclosing_func_qn = state->enclosing_func_qn;
305-
cbm_rw_push(&ctx->result->rw, ctx->arena, rw);
306-
}
292+
if (!ts_node_is_null(name_node)) {
293+
char *name = cbm_node_text(ctx->arena, name_node, ctx->source);
294+
if (name && name[0] && !cbm_is_keyword(name, ctx->language)) {
295+
CBMReadWrite rw;
296+
rw.var_name = name;
297+
rw.is_write = true;
298+
rw.enclosing_func_qn = state->enclosing_func_qn;
299+
cbm_rw_push(&ctx->result->rw, ctx->arena, rw);
307300
}
308301
}
309302
}

0 commit comments

Comments
 (0)