[GH-ISSUE #2646] fix(javascript): logical assignment turns an unknown parameter into an exact literal #947

Closed
opened 2026-10-15 18:29:48 +00:00 by bsakaguchi · 0 comments
Owner

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

Summary

Analyzing a function that uses x ||= 2 or x ??= 2 reports both x and its direct return as the exact literal 2. The incoming argument can survive either assignment, so that result loses a caller-dependent alternative. The same failure was observed with destructured parameters and catch bindings. Consumers that read these semantic values receive an incorrect constant.

Steps to reproduce

Use the source baseline below with its locked dependencies and pinned toolchain. Put this diagnostic test in src/domain/javascript/logical-assignment-reproduction.test.ts, then run npm run test:focused -- src/domain/javascript/logical-assignment-reproduction.test.ts. It calls the semantic entry point used by the application's parsed-source analysis. The independent focused run exercised these same two parameter inputs, plus the related forms and controls described below. The diagnostic assertions record the current erroneous result.

import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";

it("reports the observed values after logical parameter assignment", () => {
  for (const operator of ["||=", "??="]) {
    const source = `function read(x) { x ${operator} 2; return x; }`;
    const ir = analyzeJavaScriptSemantics(source);
    const binding = ir.bindings.find((item) => item.name === "x");
    const returned = ir.callables.find((item) => item.name === "read")
      ?.returnSites[0]?.value;
    console.log(JSON.stringify({ operator, binding: binding?.value, returned }));
    expect(binding?.value).toEqual({ status: "literal", value: 2 });
    expect(returned).toEqual({ status: "literal", value: 2 });
  }
  const readOr = (x: number) => { x ||= 2; return x; };
  const readNullish = (x: number) => { x ??= 2; return x; };
  expect(readOr(5)).toBe(5);
  expect(readNullish(5)).toBe(5);
});

Expected behavior

The semantic result must preserve uncertainty when the input does not establish the parameter's incoming value. ||= replaces only a falsy value; ??= replaces only a null or undefined value. With the argument 5, both functions retain and return 5. Reporting only the RHS literal as the binding or return value therefore contradicts these operators. An unknown or another conservative representation that retains the incoming alternative satisfies the contract.

Actual behavior

The independent source test observed this value for both the binding and return site under each operator:

{"status":"literal","value":2}

The same output appeared in all six affected fixtures: each operator applied to an ordinary parameter, a destructured parameter, and a catch binding. All six had complete semantic coverage. The focused runner exited with status 0, with one diagnostic test passed. Direct execution of the owned parameter functions returned 5 for argument 5, contradicting the analyzer's single literal.

Controls distinguished the failure from a parser error or general inability to recover writes. x = 2 produced literal 2; x &&= 2 stayed unknown; and function read(x) { return x; } stayed unknown. Runtime controls also confirmed that ||= replaced 0 with 2, while ??= retained 0 and replaced null with 2.

Evidence

  • Expected source: The repository's AGENTS.md, Architecture and Cleanup section, requires interpretations to preserve uncertainty and source evidence. src/domain/javascript/javascriptSemanticAnalysis.member_mutations.test.ts:123 checks conservative treatment of a short-circuit assignment. The owned runtime controls above establish the conditional operator behavior for the tested parameter input.
  • Failure source: Independent focused execution of analyzeJavaScriptSemantics produced the binding and direct-return values shown above. src/domain/javascript/javascriptSemanticAnalysis.ts:829 substitutes the RHS as the initializer for ||= and ??=; its addAssignment at line 1240 retains that initializer. src/domain/javascript/javascriptSemanticValues.ts:318 evaluates a sole initializer without retaining the unknown incoming binding. src/domain/javascript/javascriptSemanticProjection.ts:218 exposes the binding value, and src/domain/javascript/javascriptSemanticReturns.ts:39 evaluates the return identifier.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The production application path reaches this analysis in src/application/javascript/JavaScriptArtifactAnalysis.ts:365 after parsing an admitted JavaScript source. The graph consumer in src/application/javascript/JavaScriptSemanticGraphValueProjection.ts:23 converts a literal binding value into a literal node with a resolved defines relation. Those application and graph statements are source-derived; a complete CLI/MCP query and export comparison were not executed.

Related: #2562 covers initialization skipped by an enclosing branch or loop. This report concerns logical operators retaining an incoming parameter or catch value even when the assignment statement is reached.

Restoration check

Reanalyze function read(x) { x ||= 2; return x; } and the corresponding ??= input. Both the binding and return-site result must preserve the possible incoming value rather than report only literal 2. Check the destructured-parameter and catch-binding forms against the same conditional-assignment contract. Preserve the discriminating controls: an unconditional x = 2 remains literal 2, and the unchanged parameter and &&= cases do not become exact literals. A successful diagnostic test that merely asserts the current literal output does not establish restoration.

Runtime or environment

Source checkout at commit a2d8b94dd5, tested with Node 24.18.0 / npm 11.16.0 and locked dependencies. The published rea-agents 6.4.0 package was not executed, so this report establishes the source behavior only. The fixtures use static JavaScript analysis and tiny owned functions; no external analysis provider is needed.

Current-upstream source recheck at commit 3ae826ceba found the logical-assignment producer, binding and return evaluation, and graph value projection unchanged. Runtime reproduction was not rerun at this revision.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Six supported input forms produced incorrect exact binding and direct-return values. This is a bounded analysis correctness defect that can mislead consumers of recovered constants. The observations do not establish data loss, a universal workflow failure, or severe downstream harm.
  • Workaround status: unverified
  • Workaround basis: No public analysis option or alternative workflow was tested that preserves the result while avoiding the incorrect constant. The verified controls demonstrate unaffected syntax, but changing the analyzed program is not a demonstrated mitigation for an analyst.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2646 ## Summary Analyzing a function that uses `x ||= 2` or `x ??= 2` reports both `x` and its direct return as the exact literal `2`. The incoming argument can survive either assignment, so that result loses a caller-dependent alternative. The same failure was observed with destructured parameters and catch bindings. Consumers that read these semantic values receive an incorrect constant. ## Steps to reproduce Use the source baseline below with its locked dependencies and pinned toolchain. Put this diagnostic test in `src/domain/javascript/logical-assignment-reproduction.test.ts`, then run `npm run test:focused -- src/domain/javascript/logical-assignment-reproduction.test.ts`. It calls the semantic entry point used by the application's parsed-source analysis. The independent focused run exercised these same two parameter inputs, plus the related forms and controls described below. The diagnostic assertions record the current erroneous result. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; it("reports the observed values after logical parameter assignment", () => { for (const operator of ["||=", "??="]) { const source = `function read(x) { x ${operator} 2; return x; }`; const ir = analyzeJavaScriptSemantics(source); const binding = ir.bindings.find((item) => item.name === "x"); const returned = ir.callables.find((item) => item.name === "read") ?.returnSites[0]?.value; console.log(JSON.stringify({ operator, binding: binding?.value, returned })); expect(binding?.value).toEqual({ status: "literal", value: 2 }); expect(returned).toEqual({ status: "literal", value: 2 }); } const readOr = (x: number) => { x ||= 2; return x; }; const readNullish = (x: number) => { x ??= 2; return x; }; expect(readOr(5)).toBe(5); expect(readNullish(5)).toBe(5); }); ``` ## Expected behavior The semantic result must preserve uncertainty when the input does not establish the parameter's incoming value. `||=` replaces only a falsy value; `??=` replaces only a null or undefined value. With the argument `5`, both functions retain and return `5`. Reporting only the RHS literal as the binding or return value therefore contradicts these operators. An unknown or another conservative representation that retains the incoming alternative satisfies the contract. ## Actual behavior The independent source test observed this value for both the binding and return site under each operator: ```json {"status":"literal","value":2} ``` The same output appeared in all six affected fixtures: each operator applied to an ordinary parameter, a destructured parameter, and a catch binding. All six had complete semantic coverage. The focused runner exited with status 0, with one diagnostic test passed. Direct execution of the owned parameter functions returned `5` for argument `5`, contradicting the analyzer's single literal. Controls distinguished the failure from a parser error or general inability to recover writes. `x = 2` produced literal `2`; `x &&= 2` stayed unknown; and `function read(x) { return x; }` stayed unknown. Runtime controls also confirmed that `||=` replaced `0` with `2`, while `??=` retained `0` and replaced null with `2`. ## Evidence - Expected source: The repository's `AGENTS.md`, Architecture and Cleanup section, requires interpretations to preserve uncertainty and source evidence. `src/domain/javascript/javascriptSemanticAnalysis.member_mutations.test.ts:123` checks conservative treatment of a short-circuit assignment. The owned runtime controls above establish the conditional operator behavior for the tested parameter input. - Failure source: Independent focused execution of `analyzeJavaScriptSemantics` produced the binding and direct-return values shown above. `src/domain/javascript/javascriptSemanticAnalysis.ts:829` substitutes the RHS as the initializer for `||=` and `??=`; its `addAssignment` at line 1240 retains that initializer. `src/domain/javascript/javascriptSemanticValues.ts:318` evaluates a sole initializer without retaining the unknown incoming binding. `src/domain/javascript/javascriptSemanticProjection.ts:218` exposes the binding value, and `src/domain/javascript/javascriptSemanticReturns.ts:39` evaluates the return identifier. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The production application path reaches this analysis in `src/application/javascript/JavaScriptArtifactAnalysis.ts:365` after parsing an admitted JavaScript source. The graph consumer in `src/application/javascript/JavaScriptSemanticGraphValueProjection.ts:23` converts a literal binding value into a literal node with a resolved `defines` relation. Those application and graph statements are source-derived; a complete CLI/MCP query and export comparison were not executed. Related: #2562 covers initialization skipped by an enclosing branch or loop. This report concerns logical operators retaining an incoming parameter or catch value even when the assignment statement is reached. ## Restoration check Reanalyze `function read(x) { x ||= 2; return x; }` and the corresponding `??=` input. Both the binding and return-site result must preserve the possible incoming value rather than report only literal `2`. Check the destructured-parameter and catch-binding forms against the same conditional-assignment contract. Preserve the discriminating controls: an unconditional `x = 2` remains literal `2`, and the unchanged parameter and `&&=` cases do not become exact literals. A successful diagnostic test that merely asserts the current literal output does not establish restoration. ## Runtime or environment Source checkout at commit a2d8b94dd50dab8d966239dd63ab84718aa8b4b4, tested with Node 24.18.0 / npm 11.16.0 and locked dependencies. The published rea-agents 6.4.0 package was not executed, so this report establishes the source behavior only. The fixtures use static JavaScript analysis and tiny owned functions; no external analysis provider is needed. Current-upstream source recheck at commit 3ae826ceba6f25b08bce50bcb51001209bc16b57 found the logical-assignment producer, binding and return evaluation, and graph value projection unchanged. Runtime reproduction was not rerun at this revision. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Six supported input forms produced incorrect exact binding and direct-return values. This is a bounded analysis correctness defect that can mislead consumers of recovered constants. The observations do not establish data loss, a universal workflow failure, or severe downstream harm. - Workaround status: unverified - Workaround basis: No public analysis option or alternative workflow was tested that preserves the result while avoiding the incorrect constant. The verified controls demonstrate unaffected syntax, but changing the analyzed program is not a demonstrated mitigation for an analyst.
bsakaguchi 2026-10-15 18:29:48 +00:00
  • closed this issue
  • added the
    bug
    label
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#947
No description provided.