fix(javascript): preserve path reasons, module coverage, and property provenance (#5651)

* fix(javascript): preserve selected path host resolution reasons

Classify ELOOP and ENAMETOOLONG while resolving the caller-selected input so the path and host code stay on the invalid-request issue.

Refs #5623

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): count summary coverage in module units

Use the module population for both examined and total counts. Non-module graph nodes remain in the application coverage section.

Refs #5464

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): connect direct property reads to return and argument flow

Record the unwrapped value expression and alias the object projection's read occurrence so a direct member read stays in backward provenance.

Refs #5585, #5648

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): count return wrappers against expression depth

Evaluate the original return expression so erased TypeScript and Flow wrappers stay inside the expression-depth budget. Keep the unwrapped node for identity and value ranges.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): describe ELOOP as unresolved symlink traversal

A host can return ELOOP for a cycle or for a chain past its traversal limit. Keep the selected path and code without asserting that a cycle exists.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* test(javascript): skip symlink traversal fixtures on Windows

Keep the overlong-name case portable. Windows does not produce ELOOP for these fixtures, matching the other path-failure tests.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): describe ENAMETOOLONG as a path or component limit

realpath reports that code for a long component and for a long full path. Keep the selected path and host code without claiming only a filename was too long.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* style(javascript): format return-depth and object-read changes

oxfmt rejected the return-depth test and the object-read index. No behavior change.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* test(ida): expect unchanged invalid requests to be nonretryable

The shared projector no longer marks invalid_request retryable. This lookup assertion was not updated with that contract.

Co-authored-by: morluto <morluto@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-21 03:45:10 +08:00
1 parent b718ad166d
commit 53e0943223
12 files changed
+315 -7

No files matched your search

@@ -503,6 +503,37 @@ describe("semantic expression depth bounds", () => {
});
});
it("counts erased TypeScript wrappers on a return against expression depth", () => {
const source = "function answer() { return true; }";
const parsed = parseJavaScriptSource(source);
const declaration = parsed?.program.body[0];
const statement = t.isFunctionDeclaration(declaration)
? declaration.body.body[0]
: undefined;
const argument = t.isReturnStatement(statement)
? statement.argument
: undefined;
if (parsed === null || !t.isReturnStatement(statement) || argument == null)
throw new Error("Expected a returned value");
let expression = argument;
for (let index = 0; index < 300; index += 1)
expression = t.tsAsExpression(expression, t.tsBooleanKeyword());
statement.argument = expression;
const ir = analyzeParsedJavaScriptSemantics(parsed);
const site = ir.callables.find((item) => item.name === "answer")
?.returnSites[0];
expect(site?.value).toMatchObject({
status: "unknown",
resourceLimit: "expression-depth",
});
const literalStart = source.indexOf("true");
expect(site?.valueExpressionLocation).toEqual({
start: { line: 1, column: literalStart },
end: { line: 1, column: literalStart + "true".length },
});
});
it("counts erased TypeScript wrappers against expression depth", () => {
const parsed = parseJavaScriptSource("const answer = true;");
const declaration = parsed?.program.body[0];