Files
scriptc/tests/harness/coverage.test.ts
Malte UblandClaude Opus 5.5 77faf0c9e6 Accept the ICU-answered Intl forms and narrow their refusals
The forms the first slice answers now lower: x.toLocaleString() (and an
explicit undefined locale) to icu.numFormatDefault, the argument-free
Date#toLocaleString/toLocaleTimeString to the ICU date formatters, and
Intl.Segmenter({ granularity: "word" }) to the ICU word iterator (a
literal language tag is the locale; the empty string is the default).
Sentence segmentation keeps its refusing construction in JavaScript
dependencies, the grapheme segmenter stays the engine-free fast path,
and the code notes the deletion I4 schedules.

The refusals that named these forms follow: the four stdlib hints, the
Intl identifier hint, the Date method hint and the Segmenter option
messages list what lowers and what still does not (locale negotiation,
options bags, toLocaleDateString, formatter values). The library mode
refuses the icu.* libCalls, whose ambient locale state and data file a
library archive cannot carry, and the ambient process.config comment says
what a compiled binary can honestly answer.

An argument that IS undefined (exactly undefined, or void) means the
absent form and lowers; one whose type merely includes undefined may hold
a locale or an options bag at run time, and the constructor cannot read
either, so it is refused rather than silently treated as absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:49:10 -07:00

1094 lines
47 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { execFileSync, spawnSync } from "node:child_process";
import { globSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import {
analyze,
compile,
coverageEnvelope,
coveragePasses,
renderCoverage,
renderExplainDynamic,
renderExplainIsland,
} from "@scriptc/compiler";
import { shardSelect, shardSuffix } from "./shard.js";
const repoRoot = join(import.meta.dirname, "../..");
const fixture = (name: string) => join(repoRoot, "tests/coverage-fixtures", name);
function report(path: string): string {
const { coverage } = analyze(path);
return renderCoverage(
{ ...coverage, file: coverage.file.replace(repoRoot + "/", "") },
{ root: repoRoot },
);
}
test("mixed program: percentage and grouped blockers", async () => {
await expect(report(fixture("mixed.ts"))).toMatchFileSnapshot("__snapshots__/coverage-mixed.txt");
});
test("unsupported operations appear as native compilation blockers", async () => {
// constructs no flag fixes stay under "blockers".
await expect(report(fixture("dynamic-mix.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-dynamic-mix.txt",
);
});
test("unreached blockers report in their own group", async () => {
// Bodies nothing on the entry path reaches lower only for analysis:
// their blockers render dim, under "in unreached code" — and the same
// program BUILDS (the differential dead-strip tests pin that side).
await expect(report(fixture("unreached.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-unreached.txt",
);
});
test("WebAssembly modules the build can see report with their sources", async () => {
// A resolved path, a resolved .so shared library and an inline literal
// (glue.js), a packaged .wasm (wasm-fixture-pkg), and package.json's
// scriptc.wasm list. A resolved .so that is not a module is not reported.
await expect(report(fixture("wasm/main.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-wasm.txt",
);
const { coverage } = analyze(fixture("wasm/main.ts"), { wasm: [fixture("wasm/add.wasm")] });
const modules = coverage.wasmModules ?? [];
expect(
modules.map((m) => [
m.path?.replace(repoRoot + "/", "") ?? "inline",
m.status,
m.unsupported,
m.sources.map((s) => s.kind),
]),
).toEqual([
["inline", "valid", [], ["inline"]],
["tests/coverage-fixtures/wasm/add.wasm", "valid", [], ["path", "config"]],
["tests/coverage-fixtures/wasm/ext.so", "valid", [], ["path"]],
[
"tests/coverage-fixtures/wasm/node_modules/wasm-fixture-pkg/lib/core.wasm",
"invalid",
[],
["package"],
],
["tests/coverage-fixtures/wasm/build/generated.wasm", "unsupported", ["simd"], ["config"]],
]);
const envelope = coverageEnvelope(coverage, { compilerVersion: "test" });
expect(envelope.wasmModules?.map((m) => m.sha256.length)).toEqual([64, 64, 64, 64, 64]);
});
test("divergences report as warnings, never as blockers", async () => {
const { coverage } = analyze(fixture("divergences.ts"));
expect(coverage.diagnostics).toEqual([]);
expect((coverage.divergences ?? []).map((d) => d.code)).toEqual([
"SC6001",
"SC6001",
"SC6002",
"SC6003",
"SC6004",
]);
expect(coveragePasses(coverage, "blockers")).toBe(true);
expect(coveragePasses(coverage, "divergences")).toBe(false);
await expect(report(fixture("divergences.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-divergences.txt",
);
});
test("record width copies report at every storing site", async () => {
const { coverage } = analyze(fixture("width-copies.ts"));
expect(coverage.diagnostics).toEqual([]);
expect((coverage.divergences ?? []).map((d) => d.code)).toEqual([
"SC6001",
"SC6001",
"SC6001",
"SC6001",
"SC6001",
]);
await expect(report(fixture("width-copies.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-width-copies.txt",
);
});
test("ordinary code reports no divergence", () => {
const { coverage } = analyze(fixture("no-divergences.ts"));
expect(coverage.diagnostics).toEqual([]);
expect(coverage.divergences ?? []).toEqual([]);
});
test("likely-hot functions with untyped values report as a performance warning", async () => {
const { coverage } = analyze(fixture("dynamic-tier/hot.js"));
expect(coverage.diagnostics).toEqual([]);
expect(coverage.divergences ?? []).toEqual([]);
expect((coverage.performance ?? []).map((d) => d.code)).toEqual(["SC6101"]);
// Not a blocker or a divergence: only --fail-on=dynamic fails on it.
expect(coveragePasses(coverage, "divergences")).toBe(true);
expect(coveragePasses(coverage, "dynamic")).toBe(false);
const envelope = coverageEnvelope(coverage, { compilerVersion: "test" });
expect(envelope.diagnostics.map((d) => [d.code, d.category, d.severity])).toEqual([
["SC6101", "performance", "warning"],
]);
await expect(report(fixture("dynamic-tier/hot.js"))).toMatchFileSnapshot(
"__snapshots__/coverage-dynamic-tier.txt",
);
});
test("the performance warning names the untyped value of each function", () => {
const { coverage } = analyze(fixture("dynamic-tier/typed-hot.ts"));
expect(coverage.performance?.[0]?.message.split("\n")).toEqual([
"4 functions that likely run often use untyped values, which runs slower than typed code:",
" regionTotals (typed-hot.ts:5): `items` is `any`, in a loop",
" entryCount (typed-hot.ts:26): `key` is `any`, in a loop",
" countRegions (typed-hot.ts:11): `parsed` holds an untyped `JSON.parse` result, in a loop",
" weights (typed-hot.ts:18): `rows` is `any`, in a loop",
]);
});
test("untyped setup code that runs once reports no performance warning", () => {
const { coverage } = analyze(fixture("dynamic-tier/quiet.ts"));
expect(coverage.diagnostics).toEqual([]);
expect(coverage.performance).toBeUndefined();
expect(coveragePasses(coverage, "dynamic")).toBe(true);
});
test("the performance warning names function expressions as the source does", () => {
const { coverage } = analyze(fixture("dynamic-tier/function-names.js"));
expect(coverage.performance?.[0]?.message.split("\n").slice(1)).toEqual([
" sum (function-names.js:5): untyped JavaScript file, in a loop",
" count (function-names.js:13): untyped JavaScript file, in a loop",
" scale (function-names.js:20): untyped JavaScript file, in a loop",
" anonymous function (function-names.js:27): untyped JavaScript file, in a loop",
]);
});
test("the performance warning names neither type names nor generated helpers", () => {
const { coverage } = analyze(fixture("dynamic-tier/unnamed.ts"));
expect(coverage.diagnostics).toEqual([]);
expect(coverage.performance).toBeUndefined();
});
test("dynamic-tier code in npm packages is summarized by package", () => {
const { coverage } = analyze(fixture("dynamic-tier/package/main.ts"));
expect(coverage.performance?.[0]?.message.split("\n")).toEqual([
"1 function that likely runs often uses untyped values, which runs slower than typed code:",
" sumAmounts (main.ts:6): `rows` is `any`, in a loop",
" untyped code in npm packages: untyped-lib (1 function)",
]);
});
test("statements of a signature-blocked function count as not static", () => {
const { coverage } = analyze(fixture("blocked-signature.ts"));
const un = coverage.unreached?.stats;
expect(coverage.stats.functionsSkipped + (un?.functionsSkipped ?? 0)).toBe(1);
// Two top-level statements plus the four the blocked body holds.
expect(coverage.stats.statementsTotal + (un?.statementsTotal ?? 0)).toBe(6);
expect(coverage.stats.statementsFailed + (un?.statementsFailed ?? 0)).toBe(5);
});
test("the body of a signature-blocked function reports its diagnostics", () => {
const { coverage } = analyze(fixture("blocked-signature-body.ts"));
const sites = (diags: readonly { code: string; message: string }[]) =>
diags.map((d) => `${d.code} ${d.message}`).sort();
// measure is called: its body's diagnostics join the reached ones.
expect(sites(coverage.diagnostics)).toEqual([
"SC2004 uses of 'extra' inherit the blocker on its declaration",
"SC2004 uses of 'measure' inherit the blocker on its declaration",
"SC2004 uses of 'target' inherit the blocker on its declaration",
"SC2020 'WeakRef<object>' is part of the standard library types but has no scriptc lowering yet",
"SC2020 'new WeakRef' is part of the standard library types but has no scriptc lowering yet",
]);
// Nothing calls unused or described: their signatures and bodies report
// as unreached. described's declared `this` is its receiver, so
// `this.label` reports nothing.
expect(sites(coverage.unreached?.diagnostics ?? [])).toEqual([
"SC2004 uses of 'extra' inherit the blocker on its declaration",
"SC2004 uses of 'target' inherit the blocker on its declaration",
"SC2020 'WeakRef<object>' is part of the standard library types but has no scriptc lowering yet",
"SC2020 'WeakRef<object>' is part of the standard library types but has no scriptc lowering yet",
"SC2020 'new WeakRef' is part of the standard library types but has no scriptc lowering yet",
]);
// The bodies' statements still count once each, as not static.
const un = coverage.unreached?.stats;
expect(coverage.stats.functionsSkipped + (un?.functionsSkipped ?? 0)).toBe(3);
expect(coverage.stats.statementsTotal + (un?.statementsTotal ?? 0)).toBe(7);
expect(coverage.stats.statementsFailed + (un?.statementsFailed ?? 0)).toBe(7);
});
test("deferred fences in a module's top-level code report as running at startup", () => {
const { coverage, sourceTexts } = analyze(fixture("startup-fences.js"));
expect(
(coverage.runtimeFences ?? []).map((d) => [d.code, d.loc.start, d.startup === true]),
).toEqual([
["SC1090", expect.any(Number), true],
["SC1090", expect.any(Number), false],
]);
const out = report(fixture("startup-fences.js"));
expect(out).toMatch(
/runs at startup {3}1 site .*\n.*'instanceof' right-hand sides other than classes declared in the program\n\s+at tests\/coverage-fixtures\/startup-fences\.js:5:30/,
);
expect(out).toMatch(
/deferred to runtime {3}1 site .*\n.*'instanceof' right-hand sides other than classes declared in the program\n\s+at tests\/coverage-fixtures\/startup-fences\.js:7:27/,
);
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope.diagnostics.map((d) => [d.scope, d.line, d.startup])).toEqual([
["deferred", 5, true],
["deferred", 7, undefined],
]);
});
test("a failed unreached JavaScript statement is always explained", () => {
const { coverage } = analyze(fixture("unreached-js-fence.js"));
const failed =
coverage.stats.statementsFailed + (coverage.unreached?.stats.statementsFailed ?? 0);
expect(failed).toBe(1);
const explained = [
...coverage.diagnostics,
...(coverage.runtimeFences ?? []),
...(coverage.unreached?.diagnostics ?? []),
...(coverage.unreached?.runtimeFences ?? []),
];
expect(explained.map((d) => d.code)).toEqual(["SC1090"]);
expect(report(fixture("unreached-js-fence.js"))).toContain(
"at tests/coverage-fixtures/unreached-js-fence.js:7:",
);
});
test("blocker groups carry a location and a hint", () => {
const out = report(fixture("mixed.ts"));
expect(out).toMatch(/at tests\/coverage-fixtures\/mixed\.ts:\d+:\d+/);
expect(out).toContain("hint: ");
});
test("TypeScript errors caused by scriptc's type world say so", () => {
const { coverage, sourceTexts } = analyze(fixture("type-world/main.ts"));
expect(coverage.preflightFailed).toBe(true);
const world = coverage.diagnostics.filter((d) => d.code === "SC0001" && d.typeWorld === true);
expect(world.map((d) => d.hint)).toEqual([
expect.stringContaining('ES2025 library regardless of the project\'s "lib"'),
expect.stringContaining("'Deno' is another runtime's global"),
]);
const out = renderCoverage(coverage, { sourceTexts });
expect(out).toContain("come from scriptc's type world");
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope.phase).toBe("preflight");
expect(envelope.diagnostics.every((d) => d.category === "environment")).toBe(true);
});
test("coercing parseFloat and Number.parse* calls name their argument condition", () => {
const { coverage } = analyze(fixture("parse-coercions.ts"));
expect(coverage.diagnostics.map((d) => [d.code, d.message])).toEqual([
["SC2012", "'parseFloat' with a 'boolean' argument cannot compile statically"],
["SC2012", "'Number.parseInt' with a 'number' argument cannot compile statically"],
]);
for (const d of coverage.diagnostics) expect(d.hint).toContain("convert the argument explicitly");
});
test("the diagnostics envelope is versioned and located", () => {
const { coverage, sourceTexts } = analyze(fixture("mixed.ts"));
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope).toMatchObject({
schema: "scriptc-diagnostics",
schemaVersion: 1,
command: "coverage",
success: false,
phase: "compile",
stats: { statementsTotal: 13, statementsStatic: 12, statementsFailed: 1, percentStatic: 92 },
});
expect(envelope.groups).toHaveLength(1);
expect(envelope.diagnostics[0]).toMatchObject({
code: "SC1031",
category: "unsupported",
severity: "error",
scope: "reached",
line: 13,
column: 7,
group: envelope.groups[0]!.id,
});
expect(envelope.diagnostics[0]!.hint.length).toBeGreaterThan(0);
});
test("CLI: --print=diagnostics and --fail-on set the output and exit status", () => {
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
const run = (...args: string[]) =>
spawnSync(process.execPath, ["--import", "tsx", scriptcCli, "coverage", ...args], {
cwd: repoRoot,
encoding: "utf8",
env: { ...process.env, SCRIPTC_NO_CACHE: "1" },
});
const plain = run(fixture("mixed.ts"));
expect(plain.status).toBe(0);
const gated = run(fixture("mixed.ts"), "--fail-on=blockers");
expect(gated.status).toBe(1);
const json = run(fixture("mixed.ts"), "--print=diagnostics", "--fail-on=blockers");
expect(json.status).toBe(1);
const envelope = JSON.parse(json.stdout) as { schemaVersion: number; success: boolean };
expect(envelope).toMatchObject({ schemaVersion: 1, success: false });
expect(run(fixture("divergences.ts"), "--fail-on=blockers").status).toBe(0);
expect(run(fixture("divergences.ts"), "--fail-on=divergences").status).toBe(1);
// Unreached blockers cannot fail a build, so they never fail the gate.
expect(run(fixture("unreached.ts"), "--fail-on=blockers").status).toBe(0);
const bad = run(fixture("mixed.ts"), "--fail-on=everything");
expect(bad.status).toBe(1);
expect(bad.stderr).toContain('unknown --fail-on value "everything"');
});
test("CLI: build --print=diagnostics reports failures and divergence warnings as JSON", () => {
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
const outDir = mkdtempSync(join(tmpdir(), "scriptc-build-envelope-"));
try {
const build = (file: string, ...args: string[]) =>
spawnSync(
process.execPath,
[
"--import",
"tsx",
scriptcCli,
"build",
file,
"--emit=ir",
"-o",
join(outDir, "out.ir.json"),
...args,
],
{ cwd: repoRoot, encoding: "utf8", env: { ...process.env, SCRIPTC_NO_CACHE: "1" } },
);
const failed = build(fixture("mixed.ts"), "--print=diagnostics");
expect(failed.status).toBe(1);
expect(JSON.parse(failed.stdout)).toMatchObject({
command: "build",
success: false,
phase: "compile",
diagnostics: [{ code: "SC1031", line: 13, column: 7 }],
});
const warned = build(fixture("divergences.ts"), "--print=diagnostics");
expect(warned.status).toBe(0);
const envelope = JSON.parse(warned.stdout) as {
success: boolean;
artifact: string;
diagnostics: { code: string; severity: string }[];
};
expect(envelope.success).toBe(true);
expect(envelope.artifact).toBe(join(outDir, "out.ir.json"));
expect(envelope.diagnostics.map((d) => d.severity)).toEqual(Array(5).fill("warning"));
const human = build(fixture("divergences.ts"));
expect(human.status).toBe(0);
expect(human.stderr).toContain("warning SC6001");
expect(human.stderr).toContain("5 warnings: the program compiled");
} finally {
rmSync(outDir, { recursive: true, force: true });
}
});
test("coverage counts the statements of dynamic-tier functions", async () => {
expect(analyze(join(repoRoot, "tests/corpus/400-fib.ts")).coverage.stats.statementsDynamic).toBe(
0,
);
const { coverage, sourceTexts } = analyze(fixture("tier-demotions.ts"));
expect(coverage.stats.statementsDynamic).toBe(1);
expect(coverage.tiers).toBeUndefined();
expect(coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts }).stats).toMatchObject({
statementsTotal: 5,
statementsStatic: 3,
statementsDynamic: 1,
statementsFailed: 2,
});
await expect(report(fixture("tier-demotions.ts"))).toMatchFileSnapshot(
"__snapshots__/coverage-tier-demotions.txt",
);
});
test("--explain-dynamic attributes each refusal to the unit the dynamic tier would take", async () => {
const { coverage, sourceTexts } = analyze(fixture("tier-demotions.ts"), { explainDynamic: true });
const tiers = coverage.tiers!;
expect(tiers.mode).toBe("auto");
expect(tiers.statementsDynamic).toBe(1);
expect(tiers.dynamicFunctions.map((fn) => fn.name)).toContain("readName");
const requests = [...tiers.requests].sort((a, b) => a.loc.start - b.loc.start);
expect(
requests.map((r) => [r.code, r.seed, r.scope, r.unit.kind, r.unit.name, r.enclosing.name]),
).toEqual([
["SC1090", "reflection", "reached", "class", "Point", "<top level>"],
["SC2020", "operation", "reached", "function", "precise", "precise"],
]);
expect(tiers.units.map((u) => [u.kind, u.name, u.requests, u.sites])).toEqual([
["class", "Point", 1, 0],
["function", "<top level>", 0, 1],
["function", "precise", 1, 1],
]);
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope.tiers?.requests[0]).toMatchObject({ unit: { kind: expect.any(String) } });
const relative = JSON.parse(JSON.stringify(tiers).replaceAll(repoRoot + "/", "")) as typeof tiers;
await expect(renderExplainDynamic(relative)).toMatchFileSnapshot(
"__snapshots__/coverage-explain-dynamic.txt",
);
expect(renderExplainDynamic(relative, { query: "Point" })).toContain("class Point");
expect(renderExplainDynamic(relative, { query: "Point" })).not.toContain("binding n");
expect(renderExplainDynamic(relative, { query: "nothing" })).toContain(
"no demoted unit matches 'nothing'",
);
});
test("--explain-dynamic lists the type failures localized failure would turn into dynamic slots", async () => {
const { coverage } = analyze(fixture("localized-failure.ts"), { explainDynamic: true });
const report = coverage.tiers!.localizedFailure;
// Locals, fields and parameters degrade (stages L1 and L2), and the
// 'any' field compiles; only the refused WeakRef field and its
// construction remain.
expect(coverage.dynamicSlots?.map((s) => [s.slot, s.name])).toEqual([
["field", "tools"],
["param", "progress"],
["local", "callbacks"],
]);
expect(report.errors).toBe(2);
expect(report.typeFailures).toEqual({ degradable: 0, refused: 1 });
expect(report.slots).toEqual([]);
expect(report.cascades).toBe(1);
expect(report.remainingByCode).toEqual({ SC1090: 1, SC2020: 1 });
const relative = JSON.parse(
JSON.stringify(coverage.tiers).replaceAll(repoRoot + "/", ""),
) as typeof coverage.tiers;
await expect(renderExplainDynamic(relative!)).toMatchFileSnapshot(
"__snapshots__/coverage-explain-localized-failure.txt",
);
});
test("a local or module global whose declared type does not compile becomes a dynamic slot", async () => {
const program = join(repoRoot, "tests/corpus/localized-failure-locals.ts");
const { coverage, sourceTexts } = analyze(program);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.dynamicSlots?.map((s) => [s.slot, s.name, s.declaredType, s.code])).toEqual([
["global", "current", "Callbacks | undefined", "SC2009"],
["global", "all", "(number | Callbacks)[]", "SC2009"],
["local", "local", "Callbacks | undefined", "SC2009"],
]);
// The loop over `all` is top-level code: its dynamic operations count too.
expect(coverage.dynamicSlots?.find((s) => s.name === "all")?.operations).toBeGreaterThan(0);
expect(coverage.performance?.filter((d) => d.code === "SC6104")).toHaveLength(3);
expect(coveragePasses(coverage, "blockers")).toBe(true);
expect(coveragePasses(coverage, "dynamic")).toBe(true);
expect(coveragePasses(coverage, "dynamic-slots")).toBe(false);
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope.dynamicSlots?.map((s) => [s.kind, s.name, s.line])).toEqual([
["global", "current", 10],
["global", "all", 36],
["local", "local", 14],
]);
expect(envelope.diagnostics.filter((d) => d.code === "SC6104").map((d) => d.severity)).toEqual([
"warning",
"warning",
"warning",
]);
const text = renderCoverage(coverage, { sourceTexts, root: repoRoot });
expect(text).toContain("dynamic slots 3");
expect(text).toContain("module global 'current' (declared 'Callbacks | undefined')");
// --tier=static-only keeps every refusal.
const strict = analyze(program, { tier: "static-only" }).coverage;
expect(strict.dynamicSlots).toBeUndefined();
expect(strict.diagnostics.map((d) => d.code)).toContain("SC2009");
});
test("dynamic slots rank by their dynamic operations and name themselves in SC6101", () => {
const { coverage } = analyze(fixture("dynamic-slot-hot.ts"));
const slots = coverage.dynamicSlots ?? [];
expect(slots.find((s) => s.name === "plugins")?.partial).toBe(true);
expect(slots.some((s) => (s.operations ?? 0) > 0)).toBe(true);
const hot = coverage.performance?.find((d) => d.code === "SC6101");
expect(hot?.message).toContain("`hook` is a dynamic piece of `Plugin[]` (declared `Hook`");
});
test("a type that is a dynamic piece and a slot of its own counts toward its own slot", () => {
const { coverage } = analyze(fixture("dynamic-slot-shared.ts"));
const slots = coverage.dynamicSlots ?? [];
expect(slots.find((s) => s.name === "current")?.operations).toBeGreaterThan(0);
expect(slots.find((s) => s.name === "plugins")?.operations ?? 0).toBe(0);
const hot = coverage.performance?.find((d) => d.code === "SC6101");
expect(hot?.message).toContain("`current` is a dynamic slot (declared `Hook`");
});
test("dynamic slots of one declared type count their own operations", () => {
const { coverage } = analyze(fixture("dynamic-slot-same-type.ts"));
const slots = coverage.dynamicSlots ?? [];
const hot = slots.find((s) => s.name === "hot")?.operations ?? 0;
const cold = slots.find((s) => s.name === "cold")?.operations ?? 0;
expect(hot).toBeGreaterThan(cold);
const hotWarning = coverage.performance?.find((d) => d.code === "SC6101");
expect(hotWarning?.message).toContain("`hot` is a dynamic slot (declared `Hook`");
});
test("CLI: --tier and --explain-dynamic", () => {
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
const outDir = mkdtempSync(join(tmpdir(), "scriptc-tier-"));
const run = (...args: string[]) =>
spawnSync(process.execPath, ["--import", "tsx", scriptcCli, ...args], {
cwd: repoRoot,
encoding: "utf8",
env: { ...process.env, SCRIPTC_NO_CACHE: "1" },
});
try {
const explained = run(
"coverage",
fixture("tier-demotions.ts"),
"--explain-dynamic",
"--tier=static-only",
);
expect(explained.status).toBe(0);
expect(explained.stdout).toContain("in dynamic tier 1");
expect(explained.stdout).toContain("--tier=static-only");
expect(explained.stdout).toContain("function precise");
const one = run("coverage", fixture("tier-demotions.ts"), "--explain-dynamic=Point");
expect(one.stdout).toContain("class Point");
expect(one.stdout).not.toContain("function precise");
const bad = run("coverage", fixture("tier-demotions.ts"), "--tier=dynamic-only");
expect(bad.status).toBe(1);
expect(bad.stderr).toContain('unknown --tier value "dynamic-only"');
const misplaced = run("build", fixture("tier-demotions.ts"), "--explain-dynamic");
expect(misplaced.status).toBe(1);
expect(misplaced.stderr).toContain("--explain-dynamic is a coverage option");
// Until demotion lands, static-only compiles exactly like the default.
const ir = (name: string, ...args: string[]) => {
const out = join(outDir, name);
const built = run(
"build",
join(repoRoot, "tests/corpus/400-fib.ts"),
"--emit=ir",
"-o",
out,
...args,
);
expect(built.status).toBe(0);
return readFileSync(out, "utf8");
};
expect(ir("static-only.ir.json", "--tier=static-only")).toBe(ir("auto.ir.json"));
} finally {
rmSync(outDir, { recursive: true, force: true });
}
});
test("fully static program reports 100%", () => {
const out = report(join(repoRoot, "tests/corpus/400-fib.ts"));
expect(out).toContain("(100%)");
expect(out).toContain("fully static");
});
test("fully static JavaScript program reports 100%", () => {
const out = report(join(repoRoot, "tests/corpus/1590-js-unannotated.js"));
expect(out).toContain("(100%)");
expect(out).toContain("fully static");
});
test.each(["3062-runtime-optional-record-guards.ts", "3063-runtime-optional-class-guards.ts"])(
"runtime-optional guard receivers stay static: %s",
(name) => {
{
const { coverage } = analyze(join(repoRoot, "tests/corpus", name), {});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.stats.statementsFailed).toBe(0);
}
},
);
test("coverage preserves function-local class capture environments", () => {
const { coverage } = analyze(join(repoRoot, "tests/corpus/local-class-unknown.ts"));
expect(coverage.preflightFailed).toBe(false);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.stats.statementsFailed).toBe(0);
expect(coverage.unreached?.diagnostics ?? []).toEqual([]);
});
test("module stream aliases remain static in separately declared functions", () => {
const { coverage } = analyze(join(repoRoot, "tests/corpus/stdio-write-errors.mjs"));
expect(coverage.preflightFailed).toBe(false);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.runtimeFences ?? []).toEqual([]);
expect(coverage.stats.statementsFailed).toBe(0);
});
test("runtime-optional refined unions report blockers without crashing", () => {
{
const { coverage } = analyze(
join(repoRoot, "tests/diagnostics/runtime-optional-refined-union.ts"),
{},
);
expect(coverage.preflightFailed).toBe(false);
expect(coverage.diagnostics.map((d) => d.code)).toEqual(["SC1090"]);
expect(coverage.stats.statementsFailed).toBe(1);
}
});
test("a rejected class reached only through a receiver probe reports its own diagnostic once", () => {
const program = join(repoRoot, "tests/diagnostics/deferred-class-receiver/main.ts");
// The class's refusal, as the diagnostics corpus pins it (--tier=static-only).
const { coverage } = analyze(program, { tier: "static-only" });
const messages = coverage.diagnostics.map((d) => d.message);
expect(
messages.filter((m) => m.startsWith("'MapIterator<string>'-typed class fields")),
).toHaveLength(1);
expect(messages.some((m) => m.startsWith("constructing through a class value"))).toBe(true);
expect(coverage.unreached?.diagnostics ?? []).toEqual([]);
// Localized failure retries the class where the probe's statement commits
// the flush, with the field policy lifted, and it compiles.
expect(analyze(program).coverage.diagnostics).toEqual([]);
});
test("settled generic rest-order fences count each source statement once", () => {
const { coverage } = analyze(join(repoRoot, "tests/diagnostics/retained-generic-rest-order.ts"));
expect(coverage.diagnostics.map((d) => d.code)).toEqual(["SC1031", "SC2004"]);
expect(coverage.stats.statementsFailed).toBe(2);
});
test("CommonJS var and let exports that can change after the export keep their refusal", () => {
// JavaScript refusals defer to run time: they are the runtime fences.
const { coverage } = analyze(fixture("cjs-mutable-exports/main.js"));
const refused = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]
.map((d) => /exporting the mutable 'let' binding '(\w+)'/.exec(d.message)?.[1])
.filter((name) => name !== undefined)
.sort();
expect(refused).toEqual(["counter", "tab", "twice"]);
});
test("CommonJS namespace reads of exports a re-exported module assigns later keep their refusal", () => {
// wrapper.cjs re-exports impl.cjs, whose set() assigns `current` after
// evaluation; `version` never changes.
const { coverage } = analyze(fixture("cjs-namespace-reexports/main.mjs"));
const refused = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]
.map(
(d) =>
/reads of '(\w+)' through namespace imports of CommonJS modules whose functions assign exports/.exec(
d.message,
)?.[1],
)
.filter((name) => name !== undefined)
.sort();
expect(refused).toEqual(["current"]);
});
test("inferred JavaScript compiles natively", async () => {
// The js-gap fixture turns noImplicitAny off. Its inferred parameter
// uses checked native values alongside the JSDoc-typed neighbor.
await expect(report(fixture("js-gap/gap.js"))).toMatchFileSnapshot(
"__snapshots__/coverage-js-gap.txt",
);
});
test("JavaScript builtin aliases keep their deferred value fence", async () => {
await expect(report(fixture("js-builtin-callable-alias.js"))).toMatchFileSnapshot(
"__snapshots__/coverage-js-builtin-callable-alias.txt",
);
});
test("types only a package's .d.ts declares stay static through re-exports and .mjs entries", () => {
// shape-kinds re-exports types its .d.ts entry imports; row-module's
// runtime entry is an .mjs file. Without their declared types the
// program's uses of Form, Kind and Row run in the dynamic tier (12
// statements); with them only the packages' own JavaScript does (3).
// readRows's result keeps its declared number type, so `count - 1` is
// native arithmetic too.
const { coverage } = analyze(
join(repoRoot, "tests/corpus/7004-package-declared-type-reexports/main.ts"),
);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.stats.statementsDynamic).toBe(3);
});
test("any-typed checked-dynamic operators compile natively", () => {
const file = join(repoRoot, "tests/corpus/2856-dynamic-any-local-operators.ts");
const staticCoverage = analyze(file).coverage;
expect(staticCoverage.diagnostics).toEqual([]);
expect(staticCoverage.stats.statementsFailed).toBe(0);
});
test("npm packages compile by default", async () => {
// Every site of a package-declared value groups into one SC2013 line
// naming the package ("values from the 'mathkit' package ..."), inside
// the two views of the same program stay in sync.
await expect(
report(join(repoRoot, "tests/corpus/native-npm-imports/main.ts")),
).toMatchFileSnapshot("__snapshots__/coverage-npm-packages.txt");
});
test("import fences no longer stop analysis: percentage plus module blockers", async () => {
// The fenced module reports ONE grouped blocker (the import line plus
// every use of its bindings carry the same message); the rest of the
// program still counts. Lives in the node-types fixture: builtin
// specifiers only RESOLVE (typecheck) with @types/node adopted — the
// fallback world turns them into tsc errors, which stay fatal. The same
// file must FAIL a build (compile() keeps preflight fatal).
await expect(
report(join(repoRoot, "tests/fixtures/node-types/import-fences.ts")),
).toMatchFileSnapshot("__snapshots__/coverage-import-fences.txt");
});
test("external host declarations unblock application coverage without inventing runtime semantics", () => {
const root = fixture("external-types");
const entry = join(root, "main.ts");
const externalTypes = { "@native-sdk/core": join(root, "native-sdk-core.d.ts") };
const unmapped = analyze(entry).coverage;
expect(unmapped.preflightFailed).toBe(true);
expect(
unmapped.diagnostics.some((d) => d.code === "SC0001" && d.message.includes("@native-sdk/core")),
).toBe(true);
const { coverage } = analyze(entry, { externalTypes });
expect(coverage.preflightFailed).toBe(false);
expect(coverage.stats.statementsTotal).toBeGreaterThan(0);
expect(coverage.diagnostics.some((d) => d.code === "SC0001")).toBe(false);
expect(
coverage.diagnostics.some(
(d) =>
d.code === "SC1010" &&
d.message.includes("external host module") &&
d.message.includes("--external-types"),
),
).toBe(true);
const out = renderCoverage({
...coverage,
file: "tests/coverage-fixtures/external-types/main.ts",
});
expect(out).toContain("statements analyzed");
expect(out).toContain("@native-sdk/core");
});
test("external declaration barrels leave type-only local code fully analyzable", () => {
const root = fixture("external-types");
const { coverage } = analyze(join(root, "type-only.ts"), {
externalTypes: { "@native-sdk/core": join(root, "native-sdk-core.d.ts") },
});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.diagnostics).toEqual([]);
expect(coverage.stats.statementsFailed).toBe(0);
});
test("external host mappings fence dynamic imports", () => {
const root = fixture("external-types");
const { coverage } = analyze(join(root, "dynamic.ts"), {
externalTypes: { "@native-sdk/core": join(root, "native-sdk-core.d.ts") },
});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.stats.statementsFailed).toBeGreaterThan(0);
expect(
coverage.diagnostics.some(
(d) =>
d.code === "SC1010" &&
d.message.includes("@native-sdk/core") &&
d.message.includes("--external-types"),
),
).toBe(true);
});
test("external host mappings fence side-effect CommonJS requires", () => {
const root = fixture("external-types");
const { coverage } = analyze(join(root, "side-effect.cjs"), {
externalTypes: { "@native-sdk/core": join(root, "native-sdk-core.d.ts") },
});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.stats.statementsFailed).toBeGreaterThan(0);
const blockers = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])];
expect(
blockers.some(
(d) =>
d.code === "SC1010" &&
d.message.includes("@native-sdk/core") &&
d.message.includes("external host module"),
),
).toBe(true);
});
test("shared external declarations retain the specifier selected by each local facade", () => {
const root = fixture("external-types");
const declaration = join(root, "native-sdk-core.d.ts");
const { coverage } = analyze(join(root, "facade-consumer.ts"), {
externalTypes: {
"@native-sdk/core": declaration,
"@native-sdk/unused": declaration,
},
});
expect(coverage.preflightFailed).toBe(false);
const valueBlockers = coverage.diagnostics.filter(
(d) => d.code === "SC1010" && d.message.startsWith("values from"),
);
expect(valueBlockers.some((d) => d.message.includes("@native-sdk/core"))).toBe(true);
expect(valueBlockers.some((d) => d.message.includes("@native-sdk/unused"))).toBe(true);
});
test("external star facades do not claim local exports", () => {
const root = fixture("external-types");
const { coverage } = analyze(join(root, "mixed-star-consumer.ts"), {
externalTypes: { "@native-sdk/core": join(root, "native-sdk-core.d.ts") },
});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.stats.statementsTotal).toBe(2);
expect(coverage.stats.statementsFailed).toBe(0);
expect(
coverage.diagnostics.some(
(d) =>
d.code === "SC1010" && d.message.includes("the '@native-sdk/core' external host module"),
),
).toBe(true);
expect(
coverage.diagnostics.some((d) => d.code === "SC1010" && d.message.startsWith("values from")),
).toBe(false);
});
test("external host mappings take precedence over overlapping npm-static packages", () => {
const root = fixture("external-types");
const entry = join(repoRoot, "tests/fixtures/npm-static/slash-cli.ts");
const { coverage } = analyze(entry, {
npmStatic: ["slash"],
externalTypes: { slash: join(root, "slash-host.d.ts") },
});
expect(coverage.preflightFailed).toBe(false);
expect(coverage.npmStatic).toEqual([
{
package: "slash",
status: "refused",
detail: 'mapped as an external host module by --external-types ("slash")',
},
]);
expect(coverage.diagnostics.some((d) => d.code === "SC2013")).toBe(false);
expect(
coverage.diagnostics.some((d) => d.code === "SC1010" && d.message.includes("'slash'")),
).toBe(true);
});
test("CLI accepts repeatable --external-types mappings for coverage", () => {
const root = fixture("external-types");
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
const out = execFileSync(
process.execPath,
[
"--import",
"tsx",
scriptcCli,
"coverage",
join(root, "main.ts"),
"--external-types",
`@native-sdk/core=${join(root, "native-sdk-core.d.ts")}`,
"--external-types",
`@native-sdk/unused=${join(root, "native-sdk-core.d.ts")}`,
],
{ cwd: repoRoot, encoding: "utf8" },
);
expect(out).toContain("statements analyzed");
expect(out).toContain("@native-sdk/core");
expect(out).not.toContain("Cannot find module");
});
test("external type mappings reject TypeScript paths patterns", () => {
const root = fixture("external-types");
const entry = join(root, "type-only.ts");
const declaration = join(root, "native-sdk-core.d.ts");
expect(() => analyze(entry, { externalTypes: { "@native-sdk/*": declaration } })).toThrow(
"expected an exact bare package specifier",
);
const scriptcCli = join(repoRoot, "packages/cli/src/main.ts");
const cli = spawnSync(
process.execPath,
[
"--import",
"tsx",
scriptcCli,
"coverage",
entry,
"--external-types",
`@native-sdk/*=${declaration}`,
],
{ cwd: repoRoot, encoding: "utf8" },
);
expect(cli.status).toBe(1);
expect(cli.stderr).toContain("expected an exact bare package specifier");
});
test("external type mappings reject non-declaration and unreadable API paths", () => {
const root = fixture("external-types");
const entry = join(root, "type-only.ts");
expect(() =>
analyze(entry, { externalTypes: { "@native-sdk/core": join(root, "main.ts") } }),
).toThrow("expected a .d.ts, .d.mts, or .d.cts file");
expect(() =>
analyze(entry, { externalTypes: { "@native-sdk/core": join(root, "missing.d.ts") } }),
).toThrow("does not name a readable file");
});
test("type errors block analysis", () => {
const out = report(fixture("type-errors.ts"));
expect(out).toContain("not analyzable");
expect(out).toContain("1 TypeScript error");
});
test("type errors render the same code frame a build prints", () => {
const { coverage, sourceTexts } = analyze(fixture("type-errors.ts"));
const out = renderCoverage(coverage, { sourceTexts });
expect(out).toContain("not analyzable");
expect(out).toContain("error SC0001");
// The frame gutter with the offending line, exactly the build renderer.
expect(out).toMatch(/\d+ \| /);
expect(out).toContain("^");
});
test("bare '.' and '..' imports are relative edges (the vercel-CLI dialect)", () => {
// `from '..'` / `from '.'` resolve as directory imports to the target's
// index module — exactly what the project's bundler-resolution checker
// answered — so the graph loads and everything compiles statically.
const out = report(fixture("dot-parent/main.ts"));
expect(out).toContain("(100%)");
expect(out).toContain("fully static");
});
test("a cycle whose top level calls a builtin with a builtin function value is admitted", () => {
// vercel's link.ts cluster: `promisify(fs.readFile)` at a cycle member's
// top level. Callee and callable argument are both dts-rooted — no user
// code can run in the init window — so no SC1016 and the analysis
// renders statement counts (the statement itself may stay a
// statement-LEVEL blocker; that is a different fence).
const out = report(fixture("cycle-inert-builtin/main.ts"));
expect(out).not.toContain("circular imports");
expect(out).toContain("statements analyzed");
});
test("Node islands (report-only): one package per class, gap islands first", async () => {
// ni-gap (a fence inside the native subset), ni-eval (new Function),
// ni-ctor (a constructor function, carrying ni-dep), ni-bundled (not
// compilable as shipped), ni-typed (native, also reached from ni-ctor).
const entry = fixture("node-islands/main.ts");
await expect(report(entry)).toMatchFileSnapshot("__snapshots__/coverage-node-islands.txt");
const { coverage, sourceTexts } = analyze(entry);
const islands = coverage.nodeIslands!;
expect(islands.islands.map((i) => [i.package, i.class])).toEqual([
["ni-gap", "gap"],
["ni-eval", "runtime"],
["ni-ctor", "object-model"],
["ni-bundled", "packaging"],
]);
expect(islands.packages.map((p) => [p.package, p.status, p.islands])).toEqual([
["ni-dep", "inside", ["ni-ctor"]],
["ni-typed", "shared", ["ni-ctor"]],
]);
await expect(
renderExplainIsland(coverage.nodeIslands, "ni-ctor", { sourceTexts, root: repoRoot }),
).toMatchFileSnapshot("__snapshots__/coverage-node-islands-explain.txt");
expect(renderExplainIsland(coverage.nodeIslands, "ni-dep", { root: repoRoot })).toContain(
"only ni-ctor imports it",
);
// The machine-readable form carries the same decisions.
const envelope = coverageEnvelope(coverage, { compilerVersion: "test", sourceTexts });
expect(envelope.nodeIslands?.mode).toBe("would-be");
expect(envelope.nodeIslands?.islands.map((i) => [i.package, i.class, i.at?.line])).toEqual([
["ni-gap", "gap", 7],
["ni-eval", "runtime", 4],
["ni-ctor", "object-model", 9],
["ni-bundled", "packaging", undefined],
]);
// The gates: the blocker gate is today's build (which refuses
// ni-bundled with SC2013, and nothing else); the island gates fail on
// the would-be islands.
expect(coverage.diagnostics.every((d) => d.code === "SC2013")).toBe(true);
expect(coveragePasses(coverage, "island-gaps")).toBe(false);
expect(coveragePasses(coverage, "node-islands")).toBe(false);
});
test("unknown scriptc settings warn in a build and make the island report unavailable", async () => {
const entry = fixture("node-islands-config/main.ts");
const outDir = mkdtempSync(join(tmpdir(), "scriptc-island-config-"));
try {
const built = await compile(entry, {
outputKind: "ir",
outPath: join(outDir, "main.ir.json"),
outDir,
});
expect(built.ok).toBe(true);
if (!built.ok) return;
expect((built.warnings ?? []).map((d) => [d.code, d.message])).toEqual([
["SC8004", `package.json 'scriptc.packages.encoding' has the unknown value "unloded"`],
["SC8004", `package.json 'scriptc.packages.debug.run' has the unknown value "island"`],
["SC8004", `package.json 'scriptc.nodeIslands.auto' must be true or false, not "yes"`],
["SC8004", "package.json 'scriptc.nodeIslands.mode' is not a known Node island setting"],
]);
} finally {
rmSync(outDir, { recursive: true, force: true });
}
const { coverage } = analyze(entry);
expect(coverage.nodeIslands).toBeUndefined();
expect(renderCoverage({ ...coverage, file: "main.ts" }, { root: repoRoot })).toContain(
"not analyzable: the project's scriptc configuration has 4 unknown settings",
);
});
test("Node island policy from the command line: native, forced, off", () => {
const entry = fixture("node-islands/main.ts");
const forced = analyze(entry, {
nodeIslands: { native: ["ni-eval"], islands: ["ni-typed", "not-a-dependency"] },
}).coverage.nodeIslands!;
expect(forced.islands.map((i) => [i.package, i.class, i.decision])).toEqual([
["ni-gap", "gap", "auto"],
["ni-ctor", "object-model", "auto"],
["ni-bundled", "packaging", "auto"],
["ni-typed", "marked", "cli"],
]);
expect(forced.violations.map((v) => v.code)).toEqual(["SC8003"]);
expect(forced.unknownPackages).toEqual(["not-a-dependency"]);
const off = analyze(entry, { nodeIslands: { mode: "off" } }).coverage;
expect(off.nodeIslands!.islands).toEqual([]);
expect(off.nodeIslands!.violations.map((v) => v.code)).toEqual([
"SC8002",
"SC8002",
"SC8002",
"SC8002",
]);
expect(coveragePasses(off, "island-gaps")).toBe(false);
});
test("--strict-user-js reports R2 constructs in user JavaScript as SC8101–SC8105", async () => {
const entry = fixture("node-islands-user-js.cjs");
// Without the flag the user module is not part of any island report.
expect(analyze(entry).coverage.nodeIslands).toBeUndefined();
const { coverage } = analyze(entry, { nodeIslands: { strictUserJs: true } });
expect(coverage.diagnostics.map((d) => d.code).filter((c) => c.startsWith("SC81"))).toEqual([
"SC8101",
"SC8102",
"SC8103",
"SC8104",
"SC8105",
"SC8101",
]);
await expect(
renderCoverage(
{ ...coverage, file: coverage.file.replace(repoRoot + "/", "") },
{ root: repoRoot },
),
).toMatchFileSnapshot("__snapshots__/coverage-node-islands-user-js.txt");
});
test(`every corpus program is 100% static (corpus and coverage agree${shardSuffix()})`, async () => {
// The differential corpus compiles by definition; coverage must agree.
// The sweep runs minutes on slow machines and each analyze() blocks the
// worker thread — without the periodic yield, vitest's worker RPC starves
// ("Timeout calling onTaskUpdate") even while every check passes, and the
// default per-test timeout is far too small for a whole-corpus analysis.
let n = 0;
const flatEntries = shardSelect(
["ts", "js", "mjs", "cjs"]
.flatMap((ext) => globSync(join(repoRoot, `tests/corpus/*.${ext}`)))
.sort(),
(file) => file.slice(repoRoot.length + 1),
);
for (const file of flatEntries) {
const firstLine = readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
// `// @deferred-fences: N` on the first line: a JS program that
// DELIBERATELY carries N runtime-fence statements on untaken paths
// (the deferred-fence corpus) — it still passes the differential
// oracle because the fences never execute.
const deferred = /^\/\/ @deferred-fences:\s*(\d+)\s*$/.exec(firstLine);
const { coverage } = analyze(file, {});
expect.soft(coverage.diagnostics, file).toEqual([]);
// Corpus programs match Node by definition: a divergence warning on one
// is a false positive.
expect.soft(coverage.divergences ?? [], file).toEqual([]);
expect.soft(coverage.stats.statementsFailed, file).toBe(deferred ? Number(deferred[1]) : 0);
if (++n % 10 === 0) await new Promise((r) => setImmediate(r));
}
}, 600_000);