[GH-ISSUE #2567] fix(javascript): member assignment targets appear as property reads #883

Closed
opened 2026-10-15 18:29:17 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2567

Summary

JavaScript semantic analysis reports property targets in destructuring assignments, for...of, for...in, and delete as reads. The object-operation result and its graph occurrence therefore describe a property value read where the source performs a property mutation. This gives graph consumers a false read relation for these valid forms.

Steps to reproduce

  1. Check out the baseline under Runtime or environment with its locked dependencies.
  2. Add the following focused test as src/domain/javascript/memberWriteClassification.test.ts.
  3. Run npm run test:focused -- src/domain/javascript/memberWriteClassification.test.ts.
import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
import { graphForJavaScript } from "../../../tests/fixtures/javascriptSemanticGraph.js";

it.each([
  ["direct assignment control", "source.count = 2;"],
  ["destructuring assignment", "[source.count] = [2];"],
  ["for-of", "for (source.count of [4]) {}"],
  ["for-in", "for (source.count in { key: true }) {}"],
  ["delete", "delete source.count;"],
])("records %s as a property write", (_name, statement) => {
  const ir = analyzeJavaScriptSemantics(`const source = { count: 1 }; ${statement}`);
  expect(ir.objectOperations.map(({ kind }) => kind)).toEqual(["write"]);
});

it("labels the destructuring target as a write occurrence", () => {
  const graph = graphForJavaScript("const source = { count: 1 }; [source.count] = [2];");
  const occurrences = graph.nodes.filter(({ properties }) => properties.operation_kind !== undefined);
  expect(occurrences.map(({ label }) => label)).toEqual(["write:/count"]);
});

Expected behavior

Each target changes source.count without reading its value, so its object operation is write. A direct source.count = 2 assignment is the control and must remain write. The semantic graph should label the target's object-operation occurrence as a write and project it as writes-property.

Actual behavior

The focused test passed the direct assignment control and failed all four other forms: each produced ["read"] instead of ["write"]. A separate graph check for [source.count] = [2] found a read:/count object-operation occurrence instead of write:/count. That occurrence projects a false reads-property relation. A separate value projection can still add a writes-property relation, so the false read and missing object-operation write occurrence are the graph failures.

Evidence

  • Expected source: src/domain/javascript/javascriptSemanticIr.ts:369-378 defines property read/write operations; src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:188-212 establishes write-only direct assignment and read-modify-write behavior; src/domain/javascript/javascriptSemanticMemberMutations.ts:403-431 recognizes these forms as member mutations in the sibling value analysis.
  • Failure source: The focused reproduction produced four read results and a read:/count graph occurrence; src/domain/javascript/javascriptSemanticObjects.ts:87-126 only classifies immediate assignment and update parents as writes, and src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-55,141-161 projects the resulting read operation.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Restoration check

On the four provided inputs, objectOperations should contain write for source.count. The destructuring case should produce a write:/count object-operation occurrence and no false read:/count occurrence or read relation for that target. The direct assignment control must continue to yield write; an ordinary const value = source.count should continue to yield read.

Runtime or environment

Failure verified at a2d8b94dd5 and still present at 8b181716ac with the repository's locked dependencies. The focused verification used the supported Node 26 runtime.

Additional context

The closed for-of stale-literal issue concerns a yielded alias whose later write failed to invalidate a value fact. Its merged fix does not address this object-operation classification; the focused reproduction still fails at the stated baseline.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: For four valid syntax forms, the object-operation result and graph occurrence assert a property read where a mutation occurs. This can mislead consumers tracing property reads or writes, but the proven failure is bounded to these forms and the separate value analysis already treats them as mutations.
  • Workaround status: none
  • Workaround basis: No caller option corrects the object-operation kind or removes the false graph read while preserving that graph result; manually inspecting the source does not supply the missing correct occurrence.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2567 ## Summary JavaScript semantic analysis reports property targets in destructuring assignments, `for...of`, `for...in`, and `delete` as reads. The object-operation result and its graph occurrence therefore describe a property value read where the source performs a property mutation. This gives graph consumers a false read relation for these valid forms. ## Steps to reproduce 1. Check out the baseline under Runtime or environment with its locked dependencies. 2. Add the following focused test as `src/domain/javascript/memberWriteClassification.test.ts`. 3. Run `npm run test:focused -- src/domain/javascript/memberWriteClassification.test.ts`. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; import { graphForJavaScript } from "../../../tests/fixtures/javascriptSemanticGraph.js"; it.each([ ["direct assignment control", "source.count = 2;"], ["destructuring assignment", "[source.count] = [2];"], ["for-of", "for (source.count of [4]) {}"], ["for-in", "for (source.count in { key: true }) {}"], ["delete", "delete source.count;"], ])("records %s as a property write", (_name, statement) => { const ir = analyzeJavaScriptSemantics(`const source = { count: 1 }; ${statement}`); expect(ir.objectOperations.map(({ kind }) => kind)).toEqual(["write"]); }); it("labels the destructuring target as a write occurrence", () => { const graph = graphForJavaScript("const source = { count: 1 }; [source.count] = [2];"); const occurrences = graph.nodes.filter(({ properties }) => properties.operation_kind !== undefined); expect(occurrences.map(({ label }) => label)).toEqual(["write:/count"]); }); ``` ## Expected behavior Each target changes `source.count` without reading its value, so its object operation is `write`. A direct `source.count = 2` assignment is the control and must remain `write`. The semantic graph should label the target's object-operation occurrence as a write and project it as `writes-property`. ## Actual behavior The focused test passed the direct assignment control and failed all four other forms: each produced `["read"]` instead of `["write"]`. A separate graph check for `[source.count] = [2]` found a `read:/count` object-operation occurrence instead of `write:/count`. That occurrence projects a false `reads-property` relation. A separate value projection can still add a `writes-property` relation, so the false read and missing object-operation write occurrence are the graph failures. ## Evidence - Expected source: `src/domain/javascript/javascriptSemanticIr.ts:369-378` defines property read/write operations; `src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:188-212` establishes write-only direct assignment and read-modify-write behavior; `src/domain/javascript/javascriptSemanticMemberMutations.ts:403-431` recognizes these forms as member mutations in the sibling value analysis. - Failure source: The focused reproduction produced four `read` results and a `read:/count` graph occurrence; `src/domain/javascript/javascriptSemanticObjects.ts:87-126` only classifies immediate assignment and update parents as writes, and `src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-55,141-161` projects the resulting read operation. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ## Restoration check On the four provided inputs, `objectOperations` should contain `write` for `source.count`. The destructuring case should produce a `write:/count` object-operation occurrence and no false `read:/count` occurrence or read relation for that target. The direct assignment control must continue to yield `write`; an ordinary `const value = source.count` should continue to yield `read`. ## Runtime or environment Failure verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and still present at 8b181716aca2cb467be499cba226abfb5ae19141 with the repository's locked dependencies. The focused verification used the supported Node 26 runtime. ## Additional context The closed [for-of stale-literal issue](https://github.com/morluto/rea/issues/1428) concerns a yielded alias whose later write failed to invalidate a value fact. Its merged fix does not address this object-operation classification; the focused reproduction still fails at the stated baseline. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: For four valid syntax forms, the object-operation result and graph occurrence assert a property read where a mutation occurs. This can mislead consumers tracing property reads or writes, but the proven failure is bounded to these forms and the separate value analysis already treats them as mutations. - Workaround status: none - Workaround basis: No caller option corrects the object-operation kind or removes the false graph read while preserving that graph result; manually inspecting the source does not supply the missing correct occurrence.
bsakaguchi 2026-10-15 18:29:17 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Addressed on main in 896696b2e761e34c267977bb5c6982c607a622d0.

The graph regression now checks direct, destructuring, for-in/of, and delete write targets against ordinary read controls; the existing producer no longer emits false property-read relations.

Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit.

Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.

<!-- gh-comment-id:6159199939 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [896696b2e761e34c267977bb5c6982c607a622d0](https://github.com/morluto/rea/commit/896696b2e761e34c267977bb5c6982c607a622d0). The graph regression now checks direct, destructuring, for-in/of, and delete write targets against ordinary read controls; the existing producer no longer emits false property-read relations. Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit. Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#883
No description provided.