@nudojs/core 1.15.2 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,18 @@
1
- import "./chunk-US532PZA.js";
1
+ import "./chunk-II73GZOY.js";
2
2
  import {
3
+ DependencyMemo,
3
4
  INTERFACE_SOURCE_ABBR,
4
5
  NudoSidecarError,
6
+ absWithStableTermIds,
5
7
  actionsForIssue,
6
8
  buildArgsFromAssume,
7
9
  canSkipLiteralCallScan,
10
+ checkArg,
11
+ checkCall,
8
12
  collectAbsFreeVars,
13
+ contractSegmentLocs,
14
+ ctorParamCountOf,
15
+ deferredExecutionSpans,
9
16
  effectiveInterface,
10
17
  evictGeneralizeMemoForPaths,
11
18
  execNudoModule,
@@ -27,28 +34,36 @@ import {
27
34
  fnTopLevelLocalNames,
28
35
  formatCheckReport,
29
36
  formatConstraint,
37
+ formatDiagnostics,
30
38
  formatEffectiveInterfaceDisplay,
31
39
  formatGithubAnnotations,
32
40
  formatGitlabCodeQuality,
33
41
  formatInterfaceTierLine,
34
- generalizeAll,
35
42
  generalizeFromAst,
36
43
  generatedExportNames,
37
44
  getGeneralizeMemoSize,
45
+ guardLoadModule,
38
46
  hashSource,
47
+ instanceForMethodBridge,
39
48
  interfaceDiagCount,
40
49
  interfaceSourceOf,
41
50
  interfaceTierOf,
42
51
  isNodeModulesPath,
52
+ listClassEntryNames,
43
53
  listFnDirectiveScopes,
44
54
  listFunctionNames,
45
55
  listTopFunctions,
46
56
  loadModuleDepsFingerprint,
57
+ loadModuleId,
47
58
  localNamedExports,
59
+ moduleMapId,
60
+ nodeSpan,
48
61
  normPath,
49
62
  parseEnvPayload,
50
63
  parseMockModulePayload,
51
64
  parseNudoImportPayload,
65
+ projectAbsPath,
66
+ reachableTopBindings,
52
67
  refineDiagCount,
53
68
  refineToIndexedFull,
54
69
  resetGeneralizeMemo,
@@ -56,6 +71,7 @@ import {
56
71
  resetNudoModuleExecCache,
57
72
  resetSidecarLoadFailureCache,
58
73
  resolveDepPath,
74
+ resolveExportSlotCall,
59
75
  scanBudgetDecl,
60
76
  scanCaseArgSpans,
61
77
  scanCaseTags,
@@ -66,6 +82,7 @@ import {
66
82
  serializeCheckJsonMulti,
67
83
  setInterfaceDiagCollector,
68
84
  setRefineDiagCollector,
85
+ sidecarAutoBindAllowed,
69
86
  sidecarClosureFingerprint,
70
87
  sidecarPathOf,
71
88
  stableAnalyzeKeySource,
@@ -74,7 +91,7 @@ import {
74
91
  takeInterfaceDiagsSince,
75
92
  takeRefineDiags,
76
93
  takeRefineDiagsSince
77
- } from "./chunk-NXZSRLCT.js";
94
+ } from "./chunk-4MRLOJMI.js";
78
95
  import {
79
96
  $add,
80
97
  $arguments,
@@ -84,7 +101,6 @@ import {
84
101
  $arrWithHoles,
85
102
  $assignRecord,
86
103
  $async,
87
- $asyncReturn,
88
104
  $await,
89
105
  $bitand,
90
106
  $bitnot,
@@ -108,7 +124,6 @@ import {
108
124
  $fnVal,
109
125
  $for,
110
126
  $forInKeys,
111
- $forIter,
112
127
  $forOf,
113
128
  $fork,
114
129
  $ge,
@@ -163,8 +178,6 @@ import {
163
178
  $sub,
164
179
  $super,
165
180
  $switch,
166
- $thisGet,
167
- $thisSet,
168
181
  $throw,
169
182
  $toNumber,
170
183
  $toNumeric,
@@ -183,7 +196,6 @@ import {
183
196
  $updateAdd,
184
197
  $updateSub,
185
198
  $ushr,
186
- $while,
187
199
  $whileSeq,
188
200
  $yield,
189
201
  CONSTRAINT_BUILDERS,
@@ -193,6 +205,7 @@ import {
193
205
  DIAG,
194
206
  FORK_TRUNCATION_LABEL,
195
207
  HOST_EFFECT_LABEL_PREFIX,
208
+ LAZY_TEMPLATE_DEPTH,
196
209
  LOOP_TRUNCATION_LABEL,
197
210
  MATH_FOLD_ERROR_LABEL,
198
211
  MAX_EVAL_CALL_DEPTH,
@@ -213,8 +226,8 @@ import {
213
226
  absOnly,
214
227
  absShapeKey,
215
228
  absToString,
229
+ accessorReadDomain,
216
230
  add,
217
- alphaOf,
218
231
  and,
219
232
  andC,
220
233
  any,
@@ -227,11 +240,11 @@ import {
227
240
  asAbs,
228
241
  asAbsVal,
229
242
  assignSourceSlots,
243
+ assumeConsumed,
230
244
  assumeFinite,
231
245
  attachContractFaces,
232
246
  attachFnImpl,
233
247
  beginCollectionFork,
234
- betaOf,
235
248
  bigintLit,
236
249
  bindImports,
237
250
  bindingsOf,
@@ -262,7 +275,6 @@ import {
262
275
  constraintToEntryAbs,
263
276
  contractFacesFromEffectiveInterface,
264
277
  contractParamNameSet,
265
- createHofCollectCtx,
266
278
  ctorArgDefinitelyInvalid,
267
279
  ctorNameOfRecv,
268
280
  currentExecPhi,
@@ -345,6 +357,7 @@ import {
345
357
  isDefinitelyTrue,
346
358
  isErrorCtorName,
347
359
  isExactLit,
360
+ isHiddenInternalSlot,
348
361
  isIntFlag,
349
362
  isMapAbs,
350
363
  isNudoBreak,
@@ -362,6 +375,7 @@ import {
362
375
  isSetAbs,
363
376
  isStrPrim,
364
377
  isSymbolAbs,
378
+ isThrowsIgnored,
365
379
  joinAbs,
366
380
  joinFunctions,
367
381
  joinObjects,
@@ -415,6 +429,7 @@ import {
415
429
  never,
416
430
  not,
417
431
  notAbs,
432
+ noteAssumeConsumed,
418
433
  noteCollectionWrite,
419
434
  noteEvalCallRecord,
420
435
  noteEvalFallback,
@@ -424,6 +439,7 @@ import {
424
439
  numLit,
425
440
  numVar,
426
441
  number,
442
+ numericBoundsImply,
427
443
  obj,
428
444
  objOf,
429
445
  objectProtoBrand,
@@ -443,7 +459,6 @@ import {
443
459
  predVars,
444
460
  primToTypeof,
445
461
  projectFlatMapResult,
446
- promoteParamShape,
447
462
  propertyKeyOf,
448
463
  protoBrandAbs,
449
464
  protoOfRecv,
@@ -460,6 +475,7 @@ import {
460
475
  relationFn,
461
476
  requiredFnArity,
462
477
  resetAbsCallBudget,
478
+ resetAssumeConsumed,
463
479
  resetEvalCallBudget,
464
480
  resetParseSourceCache,
465
481
  runTranspiled,
@@ -521,11 +537,8 @@ import {
521
537
  transpileSource,
522
538
  trueConstraint,
523
539
  tryMakeRegexAbs,
524
- tryPromoteDirectCall,
525
- tryPromoteForOfIteratee,
526
- tryPromoteHofCallback,
527
- tryPromoteReceiverAsArr,
528
540
  tryRunTranspiled,
541
+ tryRunTranspiledDetailed,
529
542
  typeofAbs,
530
543
  undefAbs,
531
544
  union,
@@ -536,7 +549,7 @@ import {
536
549
  withExecPhi,
537
550
  withInvariantSpecs,
538
551
  withVar
539
- } from "./chunk-WKIRPUHZ.js";
552
+ } from "./chunk-ARIQO5XX.js";
540
553
 
541
554
  // src/environment.ts
542
555
  function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
@@ -625,150 +638,6 @@ function mock() {
625
638
  return { kind: "mock-helper" };
626
639
  }
627
640
 
628
- // src/algebra/phi.ts
629
- var phiStack = [pTrue];
630
- function currentPhi() {
631
- return phiStack[phiStack.length - 1] ?? pTrue;
632
- }
633
- function pushPhi(p) {
634
- phiStack.push(p);
635
- }
636
- function popPhi() {
637
- if (phiStack.length > 1) phiStack.pop();
638
- }
639
- function resetPhi() {
640
- phiStack = [pTrue];
641
- }
642
- function withPhiConstraint(extra, body) {
643
- pushPhi(extra);
644
- try {
645
- body();
646
- } finally {
647
- popPhi();
648
- }
649
- }
650
- function describePhi() {
651
- const p = currentPhi();
652
- return p.op === "true" ? "\u22A4" : "(phi)";
653
- }
654
-
655
- // src/algebra/diagnostics.ts
656
- function checkArg(arg, expect, phi = pTrue) {
657
- if (!expect || expect.op === "true") return void 0;
658
- if (arg.shape.k === "unknown" && !arg.term) {
659
- return {
660
- severity: "warning",
661
- code: DIAG.argOpaque,
662
- message: `argument type is unknown; cannot verify constraint ${predToString(expect)}`,
663
- suggestion: "add a call site or @nudo:case, or provide preconditions via --assume"
664
- };
665
- }
666
- const combined = arg.pred && arg.pred.op !== "true" ? phi.op === "true" ? arg.pred : { op: "and", args: [phi, arg.pred] } : phi;
667
- if (implies(combined, expect)) return void 0;
668
- const lvR = litValue(arg);
669
- if (lvR.ok && (expect.op === "gt" || expect.op === "ge" || expect.op === "lt" || expect.op === "le")) {
670
- const ok = decideLitCmp(lvR.value, expect);
671
- if (ok === true) return void 0;
672
- if (ok === false) {
673
- return {
674
- severity: "error",
675
- code: DIAG.constraintViolated,
676
- message: `argument ${JSON.stringify(lvR.value)} does not satisfy ${predToString(expect)}`,
677
- suggestion: `use a value satisfying the constraint, or relax the function precondition`
678
- };
679
- }
680
- }
681
- return {
682
- severity: "warning",
683
- code: DIAG.constraintUnproven,
684
- message: `cannot prove the argument satisfies ${predToString(expect)}`,
685
- suggestion: `not enough information; use --assume or add a case`
686
- };
687
- }
688
- function decideLitCmp(lv, p) {
689
- if (typeof lv !== "number" || p.b.op !== "lit" || typeof p.b.value !== "number") {
690
- return void 0;
691
- }
692
- const n = p.b.value;
693
- switch (p.op) {
694
- case "gt":
695
- return lv > n;
696
- case "ge":
697
- return lv >= n;
698
- case "lt":
699
- return lv < n;
700
- case "le":
701
- return lv <= n;
702
- default:
703
- return void 0;
704
- }
705
- }
706
- function checkCall(source, fnName, args, phi = pTrue) {
707
- const diags = [];
708
- const g = generalizeFromAst(fnName, source);
709
- if (!g) {
710
- diags.push({
711
- severity: "error",
712
- code: DIAG.fnNotFound,
713
- message: `function ${fnName} not found`
714
- });
715
- return diags;
716
- }
717
- if (args.length !== g.params.length) {
718
- diags.push({
719
- severity: "error",
720
- code: DIAG.argCount,
721
- message: `${fnName} expects ${g.params.length} argument(s), got ${args.length}`,
722
- fn: fnName
723
- });
724
- return diags;
725
- }
726
- const required = extractParamRequirements(g);
727
- args.forEach((arg, i) => {
728
- const req = required.get(i);
729
- const d = checkArg(arg, req, phi);
730
- if (d) diags.push({ ...d, fn: fnName, argIndex: i });
731
- });
732
- if (g.symbolic.conf === "partial" || g.symbolic.conf === "opaque") {
733
- diags.push({
734
- severity: "info",
735
- code: DIAG.partialResult,
736
- message: `${fnName}(...) result confidence ${g.symbolic.conf}`,
737
- suggestion: "add a call site or constraint to improve precision",
738
- fn: fnName
739
- });
740
- }
741
- return diags;
742
- }
743
- function extractParamRequirements(g) {
744
- const map = /* @__PURE__ */ new Map();
745
- const pred = g.symbolic.pred;
746
- if (!pred || pred.op === "true") return map;
747
- const paramIndex = /* @__PURE__ */ new Map();
748
- g.typeParams.forEach((t, i) => paramIndex.set(t.id, i));
749
- const consider = (p) => {
750
- if (p.op === "and") {
751
- p.args.forEach(consider);
752
- return;
753
- }
754
- if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.a.op === "var" && paramIndex.has(p.a.id) && p.b.op === "lit") {
755
- map.set(paramIndex.get(p.a.id), p);
756
- }
757
- };
758
- consider(pred);
759
- return map;
760
- }
761
- function formatDiagnostics(diags) {
762
- if (diags.length === 0) return "No issues found.";
763
- return diags.map((d) => {
764
- const loc = d.fn ? `${d.fn}${d.argIndex !== void 0 ? `[${d.argIndex}]` : ""}: ` : "";
765
- let s = `[${d.severity}] ${loc}${d.message} (${d.code})`;
766
- if (d.suggestion) s += `
767
- \u2192 ${d.suggestion}`;
768
- return s;
769
- }).join("\n");
770
- }
771
-
772
641
  // src/algebra/check.ts
773
642
  import { isBuiltin } from "module";
774
643
 
@@ -794,12 +663,43 @@ function isValueSetUnknown(ret) {
794
663
  if (ret.shape.k === "any") return true;
795
664
  return false;
796
665
  }
797
- function isBoundedEvidenceMissing(ret, constraint) {
666
+ function isBoundedEvidenceMissing(ret, constraint, phi) {
798
667
  if (constraint.preds.length === 0 && !constraint.members) return false;
799
668
  if (ret.term?.op === "lit") return false;
800
669
  if (ret.pred && ret.pred.op !== "true") return false;
670
+ if (phiSuppliesConstraint(phi, constraint, ret)) return false;
801
671
  return true;
802
672
  }
673
+ function phiSuppliesConstraint(phi, constraint, ret) {
674
+ if (!phi || phi.op === "true") return false;
675
+ const item = retAnchorTerm(ret);
676
+ if (!hasSameItemBoundEvidence(phi, item)) return false;
677
+ return implies(phi, instantiateOnTerm(constraint, item));
678
+ }
679
+ function hasSameItemBoundEvidence(p, item) {
680
+ if (!p) return false;
681
+ switch (p.op) {
682
+ case "gt":
683
+ case "ge":
684
+ case "lt":
685
+ case "le":
686
+ return sameItemTerm(item, p.a);
687
+ case "and":
688
+ case "or":
689
+ return p.args.some((a) => hasSameItemBoundEvidence(a, item));
690
+ case "not":
691
+ return hasSameItemBoundEvidence(p.arg, item);
692
+ default:
693
+ return false;
694
+ }
695
+ }
696
+ function sameItemTerm(item, left) {
697
+ if (termEquals(item, left)) return true;
698
+ if (left.op === "app" && left.fn === "length" && left.args.length === 1) {
699
+ return termEquals(item, left.args[0]);
700
+ }
701
+ return false;
702
+ }
803
703
  function flattenSumArms(ret) {
804
704
  if (ret.shape.k !== "sum") return [ret];
805
705
  const out = [];
@@ -956,7 +856,11 @@ function assertImpliesSingle(arm, constraint, opts, phi) {
956
856
  }
957
857
  return r.status === "unprovable" ? unprovable(`array element: ${r.reason}`) : proved();
958
858
  }
959
- if (constraint.fn) return proved();
859
+ if (constraint.fn) {
860
+ return unprovable(
861
+ "fn-shaped return contract cannot be enforced (declare the returned value as the fn's returns constraint)"
862
+ );
863
+ }
960
864
  return assertScalar(arm, constraint, phi);
961
865
  }
962
866
  function isAnyConstraint(c) {
@@ -981,7 +885,7 @@ function assertShapeFields(ret, constraint, opts) {
981
885
  }
982
886
  continue;
983
887
  }
984
- const r = assertImplies(slot.value, field.constraint, opts);
888
+ const r = assertImplies(accessorReadDomain(ret, key) ?? slot.value, field.constraint, opts);
985
889
  if (r.status === "disproved") {
986
890
  return disproved(`field ${key}: ${r.reason}`);
987
891
  }
@@ -1022,10 +926,14 @@ function assertScalar(ret, constraint, phi) {
1022
926
  }
1023
927
  if (constraintPred.op !== "true") {
1024
928
  if (implies(retPred, constraintPred)) return proved();
1025
- if (ret.pred && ret.pred.op !== "true" && numericBoundsImplyLocal(ret.pred, constraintPred)) {
929
+ if (phi.op !== "true" && ret.pred && ret.pred.op !== "true" && implies(ret.pred, constraintPred)) {
1026
930
  return proved();
1027
931
  }
1028
- if (isBoundedEvidenceMissing(ret, constraint)) {
932
+ if (ret.pred && ret.pred.op !== "true" && numericBoundsImply(ret.pred, constraintPred)) {
933
+ return proved();
934
+ }
935
+ if (phiSuppliesConstraint(phi, constraint, ret)) return proved();
936
+ if (isBoundedEvidenceMissing(ret, constraint, phi)) {
1029
937
  return unprovable(
1030
938
  `cannot prove return satisfies ${formatConstraint(constraint)} (no bound evidence on the return value)`
1031
939
  );
@@ -1044,7 +952,7 @@ function assertScalar(ret, constraint, phi) {
1044
952
  }
1045
953
  }
1046
954
  if (constraintPred.op !== "true" || constraint.preds.length > 0) {
1047
- if (isBoundedEvidenceMissing(ret, constraint)) {
955
+ if (isBoundedEvidenceMissing(ret, constraint, phi)) {
1048
956
  return unprovable(
1049
957
  `cannot prove return satisfies ${formatConstraint(constraint)} (no bound evidence on the return value)`
1050
958
  );
@@ -1055,36 +963,6 @@ function assertScalar(ret, constraint, phi) {
1055
963
  }
1056
964
  return proved();
1057
965
  }
1058
- function numericBoundsImplyLocal(src, tgt) {
1059
- const one = (p) => p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le";
1060
- if (!one(src) || !one(tgt)) return false;
1061
- if (src.op !== tgt.op) {
1062
- if (src.op === "gt" && tgt.op === "ge") {
1063
- return sameTermSide(src, tgt) && litGE(src.b, tgt.b);
1064
- }
1065
- if (src.op === "lt" && tgt.op === "le") {
1066
- return sameTermSide(src, tgt) && litLE(src.b, tgt.b);
1067
- }
1068
- return false;
1069
- }
1070
- if (!sameTermSide(src, tgt)) return false;
1071
- const sb = src.b.op === "lit" ? src.b.value : void 0;
1072
- const tb = tgt.b.op === "lit" ? tgt.b.value : void 0;
1073
- if (typeof sb !== "number" || typeof tb !== "number") return false;
1074
- if (src.op === "gt" || src.op === "ge") return sb >= tb;
1075
- return sb <= tb;
1076
- }
1077
- function sameTermSide(a, b) {
1078
- return a.a.op === b.a.op && termEquals(a.a, b.a);
1079
- }
1080
- function litGE(a, b) {
1081
- if (a.op !== "lit" || b.op !== "lit") return false;
1082
- return typeof a.value === "number" && typeof b.value === "number" && a.value >= b.value;
1083
- }
1084
- function litLE(a, b) {
1085
- if (a.op !== "lit" || b.op !== "lit") return false;
1086
- return typeof a.value === "number" && typeof b.value === "number" && a.value <= b.value;
1087
- }
1088
966
  function primOfAbs(a) {
1089
967
  if (a.shape.k === "prim") return a.shape.type;
1090
968
  const lvR = litValue(a);
@@ -1127,6 +1005,104 @@ function isLengthSelfTerm(t) {
1127
1005
  return t.op === "app" && t.fn === "length" && t.args.length === 1 && t.args[0].op === "var" && t.args[0].id === SELF;
1128
1006
  }
1129
1007
 
1008
+ // src/algebra/check-return.ts
1009
+ function checkReturnConstraint(fnName, cName, constraint, ret, audit = {}) {
1010
+ const {
1011
+ loc,
1012
+ source = "directive",
1013
+ assumeDisplay,
1014
+ invariantDisplay,
1015
+ consumedAssume,
1016
+ phi,
1017
+ phiPremises
1018
+ } = audit;
1019
+ const proof = assertImplies(ret, constraint, phi ? { phi } : void 0);
1020
+ const phiUsedIdx = /* @__PURE__ */ new Set();
1021
+ if (phi && proof.status === "proved" && phiPremises && phiPremises.length > 0) {
1022
+ const withoutPhi = assertImplies(ret, constraint);
1023
+ if (withoutPhi.status !== "proved") {
1024
+ const solo = phiPremises.filter(
1025
+ (p) => phiSuppliesConstraint(p, constraint, ret)
1026
+ );
1027
+ const used = solo.length > 0 ? solo : [...phiPremises];
1028
+ for (const p of used) noteAssumeConsumed(p);
1029
+ phiPremises.forEach((p, i) => {
1030
+ if (used.includes(p)) phiUsedIdx.add(i);
1031
+ });
1032
+ }
1033
+ }
1034
+ const where = () => {
1035
+ if (loc?.line === void 0) return {};
1036
+ return loc.column === void 0 ? { line: loc.line } : { line: loc.line, column: loc.column };
1037
+ };
1038
+ const origin = source === "sidecar" ? `sidecar fn() return slot (${cName})` : `@nudo:contract return ${cName}`;
1039
+ if (proof.status === "proved") {
1040
+ const ledgerUsedIdx = /* @__PURE__ */ new Set();
1041
+ if (assumeDisplay && phiPremises) {
1042
+ const consumed = assumeConsumed();
1043
+ for (let i = 0; i < assumeDisplay.length; i++) {
1044
+ const premise = phiPremises[i];
1045
+ if (premise !== void 0 && consumed.includes(premise)) ledgerUsedIdx.add(i);
1046
+ }
1047
+ }
1048
+ const relied = [];
1049
+ const usedAssume = (assumeDisplay ?? []).filter(
1050
+ (d, i) => consumedAssume?.has(d) === true || ledgerUsedIdx.has(i) || phiUsedIdx.has(i)
1051
+ );
1052
+ if (usedAssume.length > 0) {
1053
+ relied.push({
1054
+ severity: "info",
1055
+ code: DIAG.assumeRelied,
1056
+ message: `${fnName}: return contract proved via @nudo:assume ${usedAssume.join("; ")}`,
1057
+ actual: `proof relies on user lemma${usedAssume.length > 1 ? "s" : ""}`,
1058
+ expected: "engine verifies the obligation, not the lemma itself",
1059
+ suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
1060
+ fn: fnName,
1061
+ ...where()
1062
+ });
1063
+ }
1064
+ if (invariantDisplay && invariantDisplay.length > 0) {
1065
+ relied.push({
1066
+ severity: "info",
1067
+ code: DIAG.invariantRelied,
1068
+ message: `${fnName}: return contract proved via @nudo:invariant ${invariantDisplay.join("; ")}`,
1069
+ actual: `proof relies on user invariant${invariantDisplay.length > 1 ? "s" : ""}`,
1070
+ expected: "engine verifies the obligation, not the loop invariant itself",
1071
+ suggestion: "@nudo:invariant is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
1072
+ fn: fnName,
1073
+ ...where()
1074
+ });
1075
+ }
1076
+ return relied;
1077
+ }
1078
+ const push = (severity, actual, expected, suggestion) => [
1079
+ {
1080
+ severity,
1081
+ code: severity === "error" ? DIAG.constraintViolated : DIAG.unprovenReturn,
1082
+ message: `${fnName}: return value \u22AD ${origin}`,
1083
+ actual,
1084
+ expected,
1085
+ suggestion,
1086
+ fn: fnName,
1087
+ ...where()
1088
+ }
1089
+ ];
1090
+ if (proof.status === "disproved") {
1091
+ return push(
1092
+ "error",
1093
+ formatAbs(ret),
1094
+ proof.reason,
1095
+ proof.reason.includes("nullish") ? `use nullable(${formatConstraint(constraint)}) or union(..., lit(null)) to allow nullish returns` : `return a value satisfying ${formatConstraint(constraint)}`
1096
+ );
1097
+ }
1098
+ return push(
1099
+ "warning",
1100
+ formatAbs(ret),
1101
+ formatConstraint(constraint),
1102
+ `${proof.reason}; add a precondition, narrow the return expression, or relax the return contract`
1103
+ );
1104
+ }
1105
+
1130
1106
  // src/algebra/projection.ts
1131
1107
  var PROJECTABLE_CONF = /* @__PURE__ */ new Set(["exact", "path"]);
1132
1108
  var ARR_FALLBACK_CONF = /* @__PURE__ */ new Set([
@@ -1162,7 +1138,7 @@ function absToConstraintInner(a, budget) {
1162
1138
  case "any":
1163
1139
  return a.pred?.op === "or" ? projectOrLiterals(a) : void 0;
1164
1140
  case "obj":
1165
- return projectObj(s.slots, s.index, s.open, budget);
1141
+ return projectObj(a, s, budget);
1166
1142
  case "arr":
1167
1143
  return projectArr(s.element, budget);
1168
1144
  case "sum":
@@ -1347,11 +1323,12 @@ function projectOrLiterals(a) {
1347
1323
  }
1348
1324
  return lits.length > 0 ? union(...lits) : void 0;
1349
1325
  }
1350
- function projectObj(slots, index, open, budget) {
1351
- if (index || open) return void 0;
1352
- const fields = {};
1353
- for (const [k, slot] of Object.entries(slots)) {
1354
- const fc = absToConstraint(slot.value, budget);
1326
+ function projectObj(a, s, budget) {
1327
+ if (s.index || s.open) return void 0;
1328
+ const fields = /* @__PURE__ */ Object.create(null);
1329
+ for (const [k, slot] of Object.entries(s.slots)) {
1330
+ if (isHiddenInternalSlot(k, slot)) continue;
1331
+ const fc = absToConstraint(accessorReadDomain(a, k) ?? slot.value, budget);
1355
1332
  if (!fc) return void 0;
1356
1333
  fields[k] = { constraint: fc, ...slot.optional ? { optional: true } : {} };
1357
1334
  }
@@ -1590,11 +1567,21 @@ function isDefaultExportName(source, fnName) {
1590
1567
  );
1591
1568
  return re.test(source);
1592
1569
  }
1570
+ function formatEntryParamShape(t) {
1571
+ if (t.shape.k === "unknown") {
1572
+ const term = t.term;
1573
+ if (term?.op === "lit" && (term.value === null || term.value === void 0)) {
1574
+ return formatShape(t);
1575
+ }
1576
+ return "unknown";
1577
+ }
1578
+ return formatShape(t, { showLits: true });
1579
+ }
1593
1580
  function formatEntrySigLine(name, g, throws) {
1594
1581
  const names = g.formals && g.formals.length > 0 ? formalParamSignatureNames(g.formals) : g.params;
1595
1582
  const ps = names.map((p, i) => {
1596
1583
  const t = g.typeParams[i]?.value;
1597
- const shown = !t ? "any" : t.shape.k === "unknown" ? "unknown" : formatShape(t);
1584
+ const shown = !t ? "any" : formatEntryParamShape(t);
1598
1585
  return `${p}: ${shown}`;
1599
1586
  }).join(", ");
1600
1587
  return `${name}(${ps}) => ${formatShape(g.symbolic)} throws ${throws}`;
@@ -1633,70 +1620,38 @@ function findFnLoc(file, name) {
1633
1620
  }
1634
1621
  return {};
1635
1622
  }
1623
+ function formatSigDetail(absVal, name) {
1624
+ return formatAbsMultiline(absWithStableTermIds(absVal), name);
1625
+ }
1636
1626
  function formatSigCached(absVal, name) {
1637
- const hit = sigFormatCache.get(absVal);
1627
+ let byName = sigFormatCache.get(absVal);
1628
+ if (!byName) {
1629
+ byName = /* @__PURE__ */ new Map();
1630
+ sigFormatCache.set(absVal, byName);
1631
+ }
1632
+ const hit = byName.get(name);
1638
1633
  if (hit) return hit;
1639
1634
  const out = {
1640
- display: formatAbs(absVal),
1641
- detail: formatAbsMultiline(absVal, name)
1635
+ // **人类面口径**(`formatShape`):与 `formatCheckReport` 的签名行**逐字
1636
+ // 同源**(Bug 413:机器面此前用 `formatAbs` ⇒ 把 term / pred 段渲进
1637
+ // 对外载荷,其中混着引擎内部计数器 id `len#N` ⇒ 载荷随参数列表位置漂移)。
1638
+ display: formatShape(absVal),
1639
+ detail: formatSigDetail(absVal, name)
1642
1640
  };
1643
- sigFormatCache.set(absVal, out);
1641
+ byName.set(name, out);
1644
1642
  return out;
1645
1643
  }
1646
1644
 
1647
1645
  // src/algebra/check-memo.ts
1648
- var checkReportMemo = /* @__PURE__ */ new Map();
1649
- var checkKeyDeps = /* @__PURE__ */ new Map();
1650
- var checkDepIndex = /* @__PURE__ */ new Map();
1651
- var loadModuleIds = /* @__PURE__ */ new WeakMap();
1652
- var nextLoadModuleId = 1;
1653
1646
  var MAX_CHECK_MEMO = 256;
1647
+ var checkReportMemo = new DependencyMemo(MAX_CHECK_MEMO);
1654
1648
  function resetCheckSourceMemo() {
1655
- checkReportMemo.clear();
1656
- checkKeyDeps.clear();
1657
- checkDepIndex.clear();
1649
+ checkReportMemo.reset();
1658
1650
  resetHashSourceCache();
1659
1651
  resetSidecarLoadFailureCache();
1660
1652
  }
1661
- function loadModuleId(fn2) {
1662
- if (!fn2) return 0;
1663
- let id = loadModuleIds.get(fn2);
1664
- if (id === void 0) {
1665
- id = nextLoadModuleId++;
1666
- loadModuleIds.set(fn2, id);
1667
- }
1668
- return id;
1669
- }
1670
- function checkDepsFingerprint(source, opts) {
1671
- if (!opts.loadModule || !opts.fromFile) {
1672
- return { fp: "-", paths: [], contents: [], truncated: false, readError: false };
1673
- }
1674
- return loadModuleDepsFingerprint(source, opts.loadModule, opts.fromFile);
1675
- }
1676
- function unindexCheckKey(key) {
1677
- const deps = checkKeyDeps.get(key);
1678
- if (!deps) return;
1679
- for (const p of deps) {
1680
- const set = checkDepIndex.get(p);
1681
- if (!set) continue;
1682
- set.delete(key);
1683
- if (set.size === 0) checkDepIndex.delete(p);
1684
- }
1685
- checkKeyDeps.delete(key);
1686
- }
1687
1653
  function evictCheckSourceMemoForPaths(paths) {
1688
- let n = 0;
1689
- for (const raw of paths) {
1690
- const p = stablePathKey(raw);
1691
- const keys = checkDepIndex.get(p);
1692
- if (!keys) continue;
1693
- for (const key of [...keys]) {
1694
- checkReportMemo.delete(key);
1695
- unindexCheckKey(key);
1696
- n++;
1697
- }
1698
- }
1699
- return n;
1654
+ return checkReportMemo.evictForPaths(paths);
1700
1655
  }
1701
1656
  function cloneCheckReport(r) {
1702
1657
  return {
@@ -1712,17 +1667,6 @@ function cloneCheckReport(r) {
1712
1667
  ...r.budget ? { budget: { ...r.budget } } : {}
1713
1668
  };
1714
1669
  }
1715
- var moduleMapIds = /* @__PURE__ */ new WeakMap();
1716
- var moduleMapIdSeq = 0;
1717
- function moduleMapId(m) {
1718
- if (!m) return "-";
1719
- let id = moduleMapIds.get(m);
1720
- if (id === void 0) {
1721
- id = ++moduleMapIdSeq;
1722
- moduleMapIds.set(m, id);
1723
- }
1724
- return `m${id}`;
1725
- }
1726
1670
  function checkMemoKey(filePath, source, identityOpts, deps, sidecarFp) {
1727
1671
  return [
1728
1672
  hashSource(source),
@@ -1741,41 +1685,29 @@ function checkMemoKey(filePath, source, identityOpts, deps, sidecarFp) {
1741
1685
  runTranspiledOptionsMemoKey(identityOpts.inject),
1742
1686
  // skips 必须进键:同 source 不同 skip 表(host 解析差异 / 测试注入)
1743
1687
  // 不得回放另一档报告。
1744
- skipsKey(identityOpts.skips)
1688
+ skipsKey(identityOpts.skips),
1689
+ // Bug 366:`from`(--from 的跨文件调用点证据)必须进键——同 source 不同
1690
+ // 证据不得互为陈旧命中:无证据那档会把有证据的 drift 吞掉,有证据那档
1691
+ // 会把 drift 回放进无证据的检查(假阳)。
1692
+ fromKey(identityOpts.from)
1745
1693
  ].join("|");
1746
1694
  }
1747
1695
  function skipsKey(skips) {
1748
1696
  if (!skips || skips.size === 0) return "-";
1749
1697
  return [...skips.entries()].map(([name, abs2]) => `${name}:${abs2 ? formatAbs(abs2) : "-"}`).sort().join(",");
1750
1698
  }
1699
+ function fromKey(from) {
1700
+ if (!from || from.length === 0) return "-";
1701
+ return from.map(
1702
+ (r) => `${r.fnName}:${r.args.map((a) => formatAbs(a)).join(",")}:${formatAbs(r.result)}`
1703
+ ).sort().join(";");
1704
+ }
1751
1705
  function checkMemoGet(key) {
1752
- if (!checkReportMemo.has(key)) return null;
1753
- const v2 = checkReportMemo.get(key);
1754
- checkReportMemo.delete(key);
1755
- checkReportMemo.set(key, v2);
1756
- return v2;
1706
+ const hit = checkReportMemo.lookup(key);
1707
+ return hit.hit ? hit.value : null;
1757
1708
  }
1758
1709
  function checkMemoSet(key, value, depPaths) {
1759
- if (checkReportMemo.size >= MAX_CHECK_MEMO) {
1760
- const oldest = checkReportMemo.keys().next().value;
1761
- if (oldest !== void 0) {
1762
- checkReportMemo.delete(oldest);
1763
- unindexCheckKey(oldest);
1764
- }
1765
- }
1766
- checkReportMemo.set(key, value);
1767
- if (depPaths.length > 0) {
1768
- const normDeps = [...new Set(depPaths.map(stablePathKey))];
1769
- checkKeyDeps.set(key, normDeps);
1770
- for (const p of normDeps) {
1771
- let set = checkDepIndex.get(p);
1772
- if (!set) {
1773
- set = /* @__PURE__ */ new Set();
1774
- checkDepIndex.set(p, set);
1775
- }
1776
- set.add(key);
1777
- }
1778
- }
1710
+ checkReportMemo.set(key, value, depPaths);
1779
1711
  }
1780
1712
 
1781
1713
  // src/algebra/check-may-throw.ts
@@ -1785,8 +1717,17 @@ function collectEntryMayThrows(source, fnName, g, file, phi, opts = {}) {
1785
1717
  return runWithMayThrowSession(() => {
1786
1718
  setMayThrowCollector((e) => effects.push(e));
1787
1719
  try {
1788
- const full = evalThrowsOf(source, fnName, entryArgs, opts, phi);
1789
- if (!full) return effects;
1720
+ const outcome = evalThrowsOutcome(source, fnName, entryArgs, opts, phi, file);
1721
+ if (outcome.kind === "unanalyzable") {
1722
+ if (!unsupportedFallsInside(file, fnName, outcome.reason, outcome.loc)) return effects;
1723
+ effects.push({
1724
+ kind: "Error",
1725
+ cause: `entry not analyzed (${outcome.reason}) \u21D2 may-throw undeterminable`
1726
+ });
1727
+ return effects;
1728
+ }
1729
+ if (outcome.kind !== "analyzed") return effects;
1730
+ const full = outcome.result;
1790
1731
  if (full.throws && full.throws.shape.k !== "never") {
1791
1732
  for (const tName of throwAbsToKinds(full.throws)) {
1792
1733
  if (!effects.some((e) => e.kind === tName && e.cause.startsWith("throw"))) {
@@ -1828,7 +1769,13 @@ function invokeAsThrows(fn2, phi = pTrue) {
1828
1769
  throw e;
1829
1770
  }
1830
1771
  }
1831
- function evalThrowsOf(source, fnName, args, opts = {}, phi = pTrue) {
1772
+ function declaredThrowsForEntry(source, fnName) {
1773
+ const own = extractDeclaredThrows(source, fnName);
1774
+ if (own !== void 0) return own;
1775
+ if (fnName.includes(".")) return void 0;
1776
+ return extractDeclaredThrows(source, `${fnName}.constructor`);
1777
+ }
1778
+ function evalThrowsOutcome(source, fnName, args, opts = {}, phi = pTrue, file) {
1832
1779
  const runKey = `${source}|${runTranspiledOptionsMemoKey(opts.inject)}|${runTranspiledOptionsMemoKey(opts.modules ? { modules: opts.modules } : void 0)}`;
1833
1780
  if (evalRunMemo.size >= MAX_CHECK_MEMO) {
1834
1781
  const oldest = evalRunMemo.keys().next().value;
@@ -1836,9 +1783,16 @@ function evalThrowsOf(source, fnName, args, opts = {}, phi = pTrue) {
1836
1783
  }
1837
1784
  let exports = evalRunMemo.get(runKey);
1838
1785
  if (exports === void 0) {
1839
- const run = tryRunTranspiled(source, evalAnalyzeOpts(opts));
1840
- if (run === void 0) return void 0;
1841
- exports = run;
1786
+ const attempt = tryRunTranspiledDetailed(source, evalAnalyzeOpts(opts));
1787
+ if (attempt.run === void 0) {
1788
+ const reason = attempt.fallback?.reason ?? "transpile failed";
1789
+ if (reason.startsWith("unsupported:")) {
1790
+ const loc = attempt.fallback?.loc;
1791
+ return loc ? { kind: "unanalyzable", reason, loc } : { kind: "unanalyzable", reason };
1792
+ }
1793
+ return { kind: "absent" };
1794
+ }
1795
+ exports = attempt.run;
1842
1796
  evalRunMemo.set(runKey, exports);
1843
1797
  }
1844
1798
  const isAbsVal2 = (v2) => !!v2 && typeof v2 === "object" && "shape" in v2 && "conf" in v2;
@@ -1850,29 +1804,317 @@ function evalThrowsOf(source, fnName, args, opts = {}, phi = pTrue) {
1850
1804
  }
1851
1805
  };
1852
1806
  try {
1853
- if (Object.hasOwn(exports, fnName)) return call(fnName, args);
1807
+ if (!fnName.includes(".")) {
1808
+ const clsAbs = classAbsOf(exports, file, fnName);
1809
+ if (isAbsVal2(clsAbs) && isClassValueAbs(clsAbs)) {
1810
+ return {
1811
+ kind: "analyzed",
1812
+ result: classEntryThrows(clsAbs, file, fnName, phi)
1813
+ };
1814
+ }
1815
+ }
1816
+ if (Object.hasOwn(exports, fnName)) {
1817
+ const r = call(fnName, args);
1818
+ return r ? { kind: "analyzed", result: r } : { kind: "absent" };
1819
+ }
1854
1820
  if (fnName.includes(".")) {
1855
1821
  const [clsName, methodName] = fnName.split(".", 2);
1856
- const clsAbs = exports[clsName ?? ""];
1822
+ const clsAbs = classAbsOf(exports, file, clsName ?? "");
1857
1823
  if (isAbsVal2(clsAbs)) {
1858
- return invokeAsThrows(() => $staticInvoke(clsAbs, methodName ?? "", args), phi);
1824
+ if (classMethodIsStatic(file, clsName ?? "", methodName ?? "") === false) {
1825
+ return {
1826
+ kind: "analyzed",
1827
+ result: invokeAsThrows(() => {
1828
+ const inst = instanceForMethodBridge(
1829
+ clsAbs,
1830
+ clsName ?? "",
1831
+ ctorAnyArgs(file, clsName ?? ""),
1832
+ runWithMayThrowSession
1833
+ );
1834
+ return $invoke(inst, methodName ?? "", args);
1835
+ }, phi)
1836
+ };
1837
+ }
1838
+ return {
1839
+ kind: "analyzed",
1840
+ result: invokeAsThrows(() => $staticInvoke(clsAbs, methodName ?? "", args), phi)
1841
+ };
1859
1842
  }
1860
- return void 0;
1843
+ return { kind: "absent" };
1861
1844
  }
1862
1845
  if ("default" in exports) {
1863
1846
  const d = exports["default"];
1864
- if (typeof d === "function") return call("default", args);
1847
+ if (typeof d === "function") {
1848
+ const r = call("default", args);
1849
+ return r ? { kind: "analyzed", result: r } : { kind: "absent" };
1850
+ }
1865
1851
  if (isAbsVal2(d)) {
1866
- if (d.shape.k === "fn") return call("default", args);
1852
+ if (d.shape.k === "fn") {
1853
+ const r = call("default", args);
1854
+ return r ? { kind: "analyzed", result: r } : { kind: "absent" };
1855
+ }
1867
1856
  if (d.shape.k === "obj") {
1868
- return invokeAsThrows(() => $invoke(d, fnName, args), phi);
1857
+ return {
1858
+ kind: "analyzed",
1859
+ result: invokeAsThrows(() => $invoke(d, fnName, args), phi)
1860
+ };
1869
1861
  }
1870
1862
  }
1871
1863
  }
1872
- return void 0;
1864
+ return { kind: "absent" };
1873
1865
  } catch {
1866
+ return { kind: "absent" };
1867
+ }
1868
+ }
1869
+ function classAbsOf(exports, file, clsName) {
1870
+ const slot = file ? resolveExportSlotCall(file, clsName, exports) : void 0;
1871
+ if (slot && slot.kind !== "cjs-member") return exports[slot.key];
1872
+ return exports[clsName];
1873
+ }
1874
+ function isClassValueAbs(v2) {
1875
+ return v2.shape.k === "brand" && v2.shape.ctor === true;
1876
+ }
1877
+ function classEntryThrows(clsAbs, file, clsName, phi) {
1878
+ return invokeAsThrows(
1879
+ () => runWithMayThrowSession(() => $new(clsAbs, ctorAnyArgs(file, clsName))),
1880
+ phi
1881
+ );
1882
+ }
1883
+ function classMethodIsStatic(file, clsName, methodName) {
1884
+ if (!file) return void 0;
1885
+ for (const stmt of file.program.body) {
1886
+ const s = stmt;
1887
+ let decl = stmt;
1888
+ if (s.type === "ExportNamedDeclaration" || s.type === "ExportDefaultDeclaration") {
1889
+ decl = s.declaration;
1890
+ }
1891
+ const c = decl;
1892
+ if (!c || c.type !== "ClassDeclaration") continue;
1893
+ if (c.id?.name !== clsName) continue;
1894
+ for (const m of c.body?.body ?? []) {
1895
+ const mem = m;
1896
+ if (mem.kind === "constructor") continue;
1897
+ if (classMemberName(mem) !== methodName) continue;
1898
+ return mem.static === true;
1899
+ }
1900
+ return void 0;
1901
+ }
1902
+ return void 0;
1903
+ }
1904
+ function ctorAnyArgs(file, clsName) {
1905
+ const n = file ? ctorParamCountOf(file, clsName) : 0;
1906
+ return Array.from({ length: n }, () => abs({ k: "any" }, void 0, pTrue, "path"));
1907
+ }
1908
+ function classMemberName(m) {
1909
+ const key = m.key;
1910
+ if (!key) return void 0;
1911
+ return key.name ?? (typeof key.value === "string" ? key.value : void 0);
1912
+ }
1913
+ function isExportsTarget(node) {
1914
+ const n = node;
1915
+ if (!n) return false;
1916
+ if (n.type === "Identifier") return n.name === "exports";
1917
+ if (n.type !== "MemberExpression") return false;
1918
+ const obj2 = n.object;
1919
+ const prop = n.property;
1920
+ return obj2?.type === "Identifier" && obj2.name === "module" && prop?.name === "exports";
1921
+ }
1922
+ function cjsExportName(left) {
1923
+ const l = left;
1924
+ if (!l || l.type !== "MemberExpression" || l.computed === true) return void 0;
1925
+ if (!isExportsTarget(l.object)) return void 0;
1926
+ const prop = l.property;
1927
+ return prop?.name;
1928
+ }
1929
+ function keyNameOf(node) {
1930
+ const n = node;
1931
+ if (!n) return void 0;
1932
+ if (n.type === "Identifier" && typeof n.name === "string") return n.name;
1933
+ if (n.type === "StringLiteral" && typeof n.value === "string") return n.value;
1934
+ return void 0;
1935
+ }
1936
+ function entryValueNode(node) {
1937
+ const n = node;
1938
+ if (!n) return void 0;
1939
+ return n.type === "FunctionDeclaration" || n.type === "FunctionExpression" || n.type === "ArrowFunctionExpression" || n.type === "ClassDeclaration" || n.type === "ClassExpression" ? node : void 0;
1940
+ }
1941
+ function classMethodEntryNodes(cls, methodName) {
1942
+ const members = cls.body?.body ?? [];
1943
+ let method;
1944
+ for (const m of members) {
1945
+ if (m.kind === "constructor") continue;
1946
+ if (classMemberName(m) !== methodName) continue;
1947
+ method = m;
1948
+ break;
1949
+ }
1950
+ if (!method) return void 0;
1951
+ const isStatic = method.static === true;
1952
+ const nodes = [method];
1953
+ for (const m of members) {
1954
+ if (m === method) continue;
1955
+ if (m.kind === "constructor") {
1956
+ if (!isStatic) nodes.push(m);
1957
+ continue;
1958
+ }
1959
+ if (m.type === "StaticBlock") {
1960
+ if (isStatic) nodes.push(m);
1961
+ continue;
1962
+ }
1963
+ if (m.type === "ClassProperty" || m.type === "ClassPrivateProperty") {
1964
+ if (m.static === true !== isStatic) continue;
1965
+ nodes.push(m.value ?? m);
1966
+ }
1967
+ }
1968
+ return nodes;
1969
+ }
1970
+ function cjsEntryNodes(file, expression, fnName, depth) {
1971
+ const e = expression;
1972
+ if (!e || e.type !== "AssignmentExpression" || e.operator !== "=") return void 0;
1973
+ const right = e.right;
1974
+ if (!right) return void 0;
1975
+ const fromValue = (node) => {
1976
+ const value = entryValueNode(node);
1977
+ if (value) return [value];
1978
+ const ident = node;
1979
+ if (ident?.type === "Identifier" && ident.name) {
1980
+ return entryNodesOf(file, ident.name, depth + 1);
1981
+ }
1982
+ return void 0;
1983
+ };
1984
+ if (cjsExportName(e.left) === fnName) return fromValue(right);
1985
+ if (!isExportsTarget(e.left)) return void 0;
1986
+ if (right.type === "ObjectExpression") {
1987
+ for (const p of right.properties ?? []) {
1988
+ const prop = p;
1989
+ if (keyNameOf(prop.key) !== fnName) continue;
1990
+ if (prop.type === "ObjectMethod") return [prop];
1991
+ return fromValue(prop.value);
1992
+ }
1874
1993
  return void 0;
1875
1994
  }
1995
+ if (right.type === "Identifier" && right.name === fnName) {
1996
+ return entryNodesOf(file, fnName, depth + 1);
1997
+ }
1998
+ return void 0;
1999
+ }
2000
+ function entryNodesOf(file, fnName, depth = 0) {
2001
+ if (!file || depth > 3) return void 0;
2002
+ for (const stmt of file.program.body) {
2003
+ const s = stmt;
2004
+ if (s.type === "ExpressionStatement") {
2005
+ const hit = cjsEntryNodes(file, s.expression, fnName, depth);
2006
+ if (hit) return hit;
2007
+ continue;
2008
+ }
2009
+ let decl = stmt;
2010
+ if (s.type === "ExportNamedDeclaration" || s.type === "ExportDefaultDeclaration") {
2011
+ decl = s.declaration;
2012
+ }
2013
+ const d = decl;
2014
+ if (!d || typeof d !== "object") continue;
2015
+ if (fnName.includes(".")) {
2016
+ const [clsName, methodName] = fnName.split(".", 2);
2017
+ if (d.type !== "ClassDeclaration" || d.id?.name !== clsName) continue;
2018
+ return classMethodEntryNodes(d, methodName ?? "");
2019
+ }
2020
+ if (d.type === "FunctionDeclaration" && d.id?.name === fnName) return [d];
2021
+ if (fnName === "default" && s.type === "ExportDefaultDeclaration") {
2022
+ const t = d.type;
2023
+ if (t === "FunctionDeclaration" || t === "ArrowFunctionExpression" || t === "FunctionExpression" || t === "ClassDeclaration") {
2024
+ return [d];
2025
+ }
2026
+ }
2027
+ if (d.type === "ClassDeclaration" && d.id?.name === fnName) return [d];
2028
+ if (d.type === "VariableDeclaration") {
2029
+ for (const dec of d.declarations ?? []) {
2030
+ const id = dec.id;
2031
+ if (id?.name === fnName) return [dec];
2032
+ }
2033
+ }
2034
+ }
2035
+ return void 0;
2036
+ }
2037
+ function locInSpan(loc, span) {
2038
+ if (!loc || !span) return false;
2039
+ if (loc.line < span.startLine || loc.line > span.endLine) return false;
2040
+ if (loc.line === span.startLine && loc.column < span.startColumn) return false;
2041
+ if (loc.line === span.endLine && loc.column > span.endColumn) return false;
2042
+ return true;
2043
+ }
2044
+ function isModuleInitLoc(file, loc) {
2045
+ if (!loc) return false;
2046
+ if (!locInSpan(loc, nodeSpan(file.program))) return false;
2047
+ for (const span of deferredExecutionSpans(file)) {
2048
+ if (locInSpan(loc, span)) return false;
2049
+ }
2050
+ return true;
2051
+ }
2052
+ function definiteModuleThrowOf(file) {
2053
+ return file ? scanDefiniteThrowSeq(file.program.body) : void 0;
2054
+ }
2055
+ function scanDefiniteThrowSeq(stmts) {
2056
+ for (const stmt of stmts) {
2057
+ const s = stmt;
2058
+ switch (s.type) {
2059
+ case "ThrowStatement": {
2060
+ const at = s.loc?.start;
2061
+ if (at?.line === void 0) return void 0;
2062
+ const kind = thrownKindOf(s.argument);
2063
+ return {
2064
+ line: at.line,
2065
+ column: at.column ?? 0,
2066
+ ...kind !== void 0 ? { kind } : {}
2067
+ };
2068
+ }
2069
+ // 裸块 = 无条件的语句序列 ⇒ 继续下钻
2070
+ case "BlockStatement": {
2071
+ const hit = scanDefiniteThrowSeq(s.body ?? []);
2072
+ if (hit) return hit;
2073
+ continue;
2074
+ }
2075
+ case "ReturnStatement":
2076
+ return void 0;
2077
+ case "IfStatement":
2078
+ case "SwitchStatement":
2079
+ case "TryStatement":
2080
+ case "ForStatement":
2081
+ case "ForInStatement":
2082
+ case "ForOfStatement":
2083
+ case "WhileStatement":
2084
+ case "DoWhileStatement":
2085
+ case "LabeledStatement":
2086
+ case "WithStatement":
2087
+ return void 0;
2088
+ default:
2089
+ continue;
2090
+ }
2091
+ }
2092
+ return void 0;
2093
+ }
2094
+ function thrownKindOf(arg) {
2095
+ const a = arg;
2096
+ const callee = a?.callee;
2097
+ if (callee?.type !== "Identifier" || typeof callee.name !== "string") return void 0;
2098
+ if (a?.type === "NewExpression") return callee.name;
2099
+ return a?.type === "CallExpression" && callee.name.endsWith("Error") ? callee.name : void 0;
2100
+ }
2101
+ var TS_ONLY_STATEMENT_REASON_PREFIX = "unsupported:statement:TS";
2102
+ function unsupportedFallsInside(file, fnName, reason, loc) {
2103
+ if (!file || !loc) return false;
2104
+ if (!reason.startsWith(TS_ONLY_STATEMENT_REASON_PREFIX) && isModuleInitLoc(file, loc)) {
2105
+ return true;
2106
+ }
2107
+ const entryNodes = entryNodesOf(file, fnName);
2108
+ if (!entryNodes || entryNodes.length === 0) return false;
2109
+ for (const node of entryNodes) {
2110
+ if (locInSpan(loc, nodeSpan(node))) return true;
2111
+ }
2112
+ for (const node of entryNodes) {
2113
+ for (const reach of reachableTopBindings(file, node, { includeClasses: true })) {
2114
+ if (locInSpan(loc, nodeSpan(reach.node))) return true;
2115
+ }
2116
+ }
2117
+ return false;
1876
2118
  }
1877
2119
 
1878
2120
  // src/algebra/check-assign.ts
@@ -1884,6 +2126,8 @@ function widenForAssign(a) {
1884
2126
  const el = els.reduce((x, y) => joinAbs(x, y));
1885
2127
  return abs({ k: "arr", element: el }, void 0, void 0, a.conf);
1886
2128
  }
2129
+ const prim = samePrimOfSum(s.members);
2130
+ if (prim) return abs({ k: "prim", type: prim }, void 0, void 0, a.conf);
1887
2131
  return a;
1888
2132
  }
1889
2133
  if (s.k === "tuple") {
@@ -1935,14 +2179,40 @@ function widenForAssign(a) {
1935
2179
  }
1936
2180
  return a;
1937
2181
  }
2182
+ function samePrimOfSum(members) {
2183
+ let out;
2184
+ for (const m of members) {
2185
+ const t = primNameOf(m);
2186
+ if (!t) return void 0;
2187
+ if (out === void 0) out = t;
2188
+ else if (out !== t) return void 0;
2189
+ }
2190
+ return out;
2191
+ }
2192
+ function primNameOf(a) {
2193
+ if (a.shape.k === "prim") return a.shape.type;
2194
+ const lv = a.term?.op === "lit" ? a.term.value : void 0;
2195
+ if (typeof lv === "string") return "string";
2196
+ if (typeof lv === "number") return "number";
2197
+ if (typeof lv === "boolean") return "boolean";
2198
+ if (typeof lv === "bigint") return "bigint";
2199
+ return void 0;
2200
+ }
2201
+ function isAbsLike(v2) {
2202
+ return !!v2 && typeof v2 === "object" && "shape" in v2 && "conf" in v2;
2203
+ }
2204
+ function shapeOnly(a) {
2205
+ return abs(a.shape, void 0, void 0, a.conf);
2206
+ }
1938
2207
  function structuralAssignIssues(records) {
1939
2208
  const out = [];
1940
2209
  for (const r of records) {
1941
2210
  if (!r.prev) continue;
2211
+ if (!isAbsLike(r.prev) || !isAbsLike(r.next)) continue;
1942
2212
  if (r.conditional) continue;
1943
2213
  if (r.next.shape.k === "unknown" && !r.next.term) continue;
1944
2214
  if (r.prev.shape.k === "unknown" && !r.prev.term) continue;
1945
- const leq = leqAbs(widenForAssign(r.next), widenForAssign(r.prev));
2215
+ const leq = leqAbs(widenForAssign(r.next), shapeOnly(widenForAssign(r.prev)));
1946
2216
  if (!leq.ok) {
1947
2217
  out.push({
1948
2218
  severity: "error",
@@ -1961,18 +2231,339 @@ function structuralAssignIssues(records) {
1961
2231
  }
1962
2232
 
1963
2233
  // src/algebra/check-case-scan.ts
1964
- function projectCaseArgField(arg, fieldPath) {
1965
- let cur = arg;
1966
- for (const seg of fieldPath) {
1967
- if (!cur) return void 0;
1968
- if (cur.shape.k === "brand") {
1969
- cur = cur.shape.shape;
2234
+ function parseNumericLiteral(text) {
2235
+ const s = text.trim();
2236
+ if (s === "") return void 0;
2237
+ let sign = 1;
2238
+ let body = s;
2239
+ if (body.startsWith("-")) {
2240
+ sign = -1;
2241
+ body = body.slice(1).trimStart();
2242
+ } else if (body.startsWith("+")) {
2243
+ body = body.slice(1).trimStart();
2244
+ }
2245
+ if (body === "") return void 0;
2246
+ if (body.endsWith("n")) {
2247
+ const d = body.slice(0, -1);
2248
+ if (d.includes("_")) return void 0;
2249
+ if (/^0[xX][0-9a-fA-F]+$/.test(d)) return BigInt(d) * BigInt(sign);
2250
+ if (/^0[bB][01]+$/.test(d)) return BigInt(d) * BigInt(sign);
2251
+ if (/^0[oO][0-7]+$/.test(d)) return BigInt(d) * BigInt(sign);
2252
+ if (/^[0-9]+$/.test(d)) return BigInt(d) * BigInt(sign);
2253
+ return void 0;
2254
+ }
2255
+ if (body.includes("__")) return void 0;
2256
+ const clean = body.replace(/_/g, "");
2257
+ if (clean === "") return void 0;
2258
+ if (/^0[xX][0-9a-fA-F]+$/.test(clean)) return sign * Number(BigInt(clean));
2259
+ if (/^0[bB][01]+$/.test(clean)) return sign * Number(BigInt(clean));
2260
+ if (/^0[oO][0-7]+$/.test(clean)) return sign * Number(BigInt(clean));
2261
+ if (clean === "Infinity") return sign * Infinity;
2262
+ if (clean === "NaN") return NaN;
2263
+ if (/^0[0-7]+$/.test(clean) && !body.includes("_")) return sign * parseInt(clean.slice(1), 8);
2264
+ if (/^(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/.test(clean)) return sign * Number(clean);
2265
+ return void 0;
2266
+ }
2267
+ function parseStringLiteralValue(text) {
2268
+ const t = text.trim();
2269
+ if (t.length < 2) return void 0;
2270
+ const q = t[0];
2271
+ if (q !== '"' && q !== "'") return void 0;
2272
+ if (t[t.length - 1] !== q) return void 0;
2273
+ const inner = t.slice(1, -1);
2274
+ for (let i = 0; i < inner.length; i++) {
2275
+ const ch = inner[i];
2276
+ if (ch === "\\") {
2277
+ i++;
2278
+ continue;
2279
+ }
2280
+ if (ch === q) return void 0;
2281
+ }
2282
+ let out = "";
2283
+ for (let i = 0; i < inner.length; i++) {
2284
+ const ch = inner[i];
2285
+ if (ch !== "\\") {
2286
+ out += ch;
2287
+ continue;
2288
+ }
2289
+ const n = inner[i + 1];
2290
+ if (n === void 0) return void 0;
2291
+ i++;
2292
+ switch (n) {
2293
+ case "n":
2294
+ out += "\n";
2295
+ break;
2296
+ case "t":
2297
+ out += " ";
2298
+ break;
2299
+ case "r":
2300
+ out += "\r";
2301
+ break;
2302
+ case "b":
2303
+ out += "\b";
2304
+ break;
2305
+ case "f":
2306
+ out += "\f";
2307
+ break;
2308
+ case "v":
2309
+ out += "\v";
2310
+ break;
2311
+ case "0":
2312
+ out += "\0";
2313
+ break;
2314
+ case "x": {
2315
+ const hex = inner.slice(i + 1, i + 3);
2316
+ if (!/^[0-9a-fA-F]{2}$/.test(hex)) return void 0;
2317
+ out += String.fromCharCode(parseInt(hex, 16));
2318
+ i += 2;
2319
+ break;
2320
+ }
2321
+ case "u": {
2322
+ if (inner[i + 1] === "{") {
2323
+ const end = inner.indexOf("}", i + 1);
2324
+ if (end < 0) return void 0;
2325
+ const hex = inner.slice(i + 2, end);
2326
+ if (!/^[0-9a-fA-F]{1,6}$/.test(hex)) return void 0;
2327
+ out += String.fromCodePoint(parseInt(hex, 16));
2328
+ i = end;
2329
+ } else {
2330
+ const hex = inner.slice(i + 1, i + 5);
2331
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return void 0;
2332
+ out += String.fromCharCode(parseInt(hex, 16));
2333
+ i += 4;
2334
+ }
2335
+ break;
2336
+ }
2337
+ case "\n":
2338
+ break;
2339
+ // 行连接
2340
+ case "\r":
2341
+ if (inner[i + 1] === "\n") i++;
2342
+ break;
2343
+ default:
2344
+ out += n;
2345
+ break;
2346
+ }
2347
+ }
2348
+ return out;
2349
+ }
2350
+ function parseScalarArgAbs(s) {
2351
+ const t = s.trim();
2352
+ if (t === "") return void 0;
2353
+ if (t === "true") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: true }, conf: "exact" };
2354
+ if (t === "false") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: false }, conf: "exact" };
2355
+ if (t === "null") return { shape: { k: "unknown" }, term: { op: "lit", value: null }, conf: "exact" };
2356
+ if (t === "undefined") return { shape: { k: "unknown" }, term: { op: "lit", value: void 0 }, conf: "exact" };
2357
+ const num2 = parseNumericLiteral(t);
2358
+ if (num2 !== void 0) {
2359
+ return typeof num2 === "bigint" ? bigintLit(num2) : numLit(num2);
2360
+ }
2361
+ const str2 = parseStringLiteralValue(t);
2362
+ if (str2 !== void 0) {
2363
+ return {
2364
+ shape: { k: "prim", type: "string" },
2365
+ term: { op: "lit", value: str2 },
2366
+ conf: "exact"
2367
+ };
2368
+ }
2369
+ return void 0;
2370
+ }
2371
+ function splitCaseArgsText(text) {
2372
+ const result = [];
2373
+ let current = "";
2374
+ let depth = 0;
2375
+ let inString = null;
2376
+ for (let i = 0; i < text.length; i++) {
2377
+ const ch = text[i];
2378
+ if (inString) {
2379
+ current += ch;
2380
+ if (ch === inString) inString = null;
2381
+ continue;
2382
+ }
2383
+ if (ch === '"' || ch === "'") {
2384
+ inString = ch;
2385
+ current += ch;
2386
+ continue;
2387
+ }
2388
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
2389
+ if (ch === ")" || ch === "]" || ch === "}") depth--;
2390
+ if (ch === "," && depth === 0) {
2391
+ result.push(current.trim());
2392
+ current = "";
2393
+ continue;
2394
+ }
2395
+ current += ch;
2396
+ }
2397
+ if (current.trim()) result.push(current.trim());
2398
+ return result;
2399
+ }
2400
+ function stripCaseContinuation(text) {
2401
+ return text.split("\n").map((line) => line.replace(/^\s*\*\s?/, "")).join("\n");
2402
+ }
2403
+ function findTopLevelColon(text) {
2404
+ let found = -1;
2405
+ let depth = 0;
2406
+ let inString = null;
2407
+ for (let i = 0; i < text.length && found < 0; i++) {
2408
+ const ch = text[i];
2409
+ if (inString) {
2410
+ if (ch === inString) inString = null;
2411
+ continue;
2412
+ }
2413
+ if (ch === '"' || ch === "'") {
2414
+ inString = ch;
2415
+ continue;
2416
+ }
2417
+ if (ch === "(" || ch === "[" || ch === "{") {
2418
+ depth++;
2419
+ continue;
1970
2420
  }
1971
- if (cur.shape.k !== "obj") return void 0;
1972
- cur = getSlot(cur.shape.slots, seg)?.value;
2421
+ if (ch === ")" || ch === "]" || ch === "}") {
2422
+ depth--;
2423
+ continue;
2424
+ }
2425
+ if (ch === ":" && depth === 0) found = i;
2426
+ }
2427
+ return found;
2428
+ }
2429
+ function parseStructureArgAbs(text) {
2430
+ const t = text.trim();
2431
+ if (t.startsWith("{") && t.endsWith("}")) {
2432
+ const body = t.slice(1, -1).trim();
2433
+ const slots = {};
2434
+ if (body !== "") {
2435
+ for (const entry of splitCaseArgsText(body)) {
2436
+ if (entry === "") continue;
2437
+ const colon = findTopLevelColon(entry);
2438
+ if (colon < 0) return void 0;
2439
+ const keyText = entry.slice(0, colon).trim();
2440
+ if (keyText === "" || keyText.includes("[")) return void 0;
2441
+ const key = keyText.replace(/^["']|["']$/g, "");
2442
+ if (key === "") return void 0;
2443
+ const value = parseCaseArgAbs(entry.slice(colon + 1));
2444
+ if (!value) return void 0;
2445
+ slots[key] = { value };
2446
+ }
2447
+ }
2448
+ return abs({ k: "obj", slots }, void 0, void 0, "exact");
2449
+ }
2450
+ if (t.startsWith("[") && t.endsWith("]")) {
2451
+ const body = t.slice(1, -1).trim();
2452
+ const elements = [];
2453
+ if (body !== "") {
2454
+ for (const el of splitCaseArgsText(body)) {
2455
+ if (el === "") continue;
2456
+ const value = parseCaseArgAbs(el);
2457
+ if (!value) return void 0;
2458
+ elements.push(value);
2459
+ }
2460
+ }
2461
+ return abs({ k: "tuple", elements }, void 0, void 0, "exact");
2462
+ }
2463
+ return void 0;
2464
+ }
2465
+ function parseCaseArgAbs(text) {
2466
+ return parseScalarArgAbs(text) ?? parseStructureArgAbs(text);
2467
+ }
2468
+ function isConstraintBuilderArg(text) {
2469
+ return CONSTRAINT_EXPR_RE.test(text.trim());
2470
+ }
2471
+ function derefDeepConstraint(c) {
2472
+ let cur = c;
2473
+ for (let i = 0; i < LAZY_TEMPLATE_DEPTH && cur.lazy; i++) {
2474
+ cur = derefConstraint(cur);
1973
2475
  }
1974
2476
  return cur;
1975
2477
  }
2478
+ function witnessNoInfo(w) {
2479
+ const k = w.shape.k;
2480
+ return k === "never" || (k === "unknown" || k === "any") && !w.term;
2481
+ }
2482
+ function witnessIsStructure(w) {
2483
+ const k = w.shape.k;
2484
+ return k === "obj" || k === "arr" || k === "tuple";
2485
+ }
2486
+ function witnessPrimType(w) {
2487
+ const shape2 = w.shape.k === "brand" ? w.shape.shape.shape : w.shape;
2488
+ if (shape2.k === "prim") return shape2.type;
2489
+ return void 0;
2490
+ }
2491
+ function constraintIsAnyLike(c) {
2492
+ return c.prim === void 0 && c.preds.length === 0 && (c.members?.length ?? 0) === 0 && !c.fields && !c.element && !c.fn;
2493
+ }
2494
+ function witnessViolations(c, w, path, depth) {
2495
+ if (depth > 6) return [];
2496
+ const cc = derefDeepConstraint(c);
2497
+ if (cc.members && cc.members.length > 0) {
2498
+ let first = [];
2499
+ for (const m of cc.members) {
2500
+ const vs = witnessViolations(m, w, path, depth + 1);
2501
+ if (vs.length === 0) return [];
2502
+ if (first.length === 0) first = vs;
2503
+ }
2504
+ return first;
2505
+ }
2506
+ if (cc.fields) {
2507
+ const shape2 = w.shape.k === "brand" ? w.shape.shape.shape : w.shape;
2508
+ if (shape2.k !== "obj") {
2509
+ if (witnessNoInfo(w)) return [];
2510
+ return [{ actual: w, expected: formatConstraint(cc), path, missing: false }];
2511
+ }
2512
+ const out = [];
2513
+ for (const [key, field] of Object.entries(cc.fields)) {
2514
+ const slot = getSlot(shape2.slots, key);
2515
+ const fieldPath = path === "" ? key : `${path}.${key}`;
2516
+ if (!slot) {
2517
+ if (!field.optional && !field.constraint.isOptional) {
2518
+ out.push({
2519
+ actual: w,
2520
+ expected: `missing field ${fieldPath}`,
2521
+ path: fieldPath,
2522
+ missing: true
2523
+ });
2524
+ }
2525
+ continue;
2526
+ }
2527
+ out.push(...witnessViolations(field.constraint, slot.value, fieldPath, depth + 1));
2528
+ }
2529
+ return out;
2530
+ }
2531
+ if (cc.element) {
2532
+ const shape2 = w.shape.k === "brand" ? w.shape.shape.shape : w.shape;
2533
+ const elements = shape2.k === "tuple" ? shape2.elements : shape2.k === "arr" ? [shape2.element] : void 0;
2534
+ if (!elements) {
2535
+ if (witnessNoInfo(w)) return [];
2536
+ return [{ actual: w, expected: formatConstraint(cc), path, missing: false }];
2537
+ }
2538
+ const out = [];
2539
+ elements.forEach((el, i) => {
2540
+ out.push(...witnessViolations(cc.element, el, `${path}[${i}]`, depth + 1));
2541
+ });
2542
+ return out;
2543
+ }
2544
+ if (cc.fn) return [];
2545
+ if (witnessIsStructure(w)) {
2546
+ if (constraintIsAnyLike(cc)) return [];
2547
+ return [{ actual: w, expected: formatConstraint(cc), path, missing: false }];
2548
+ }
2549
+ const argLit = w.term?.op === "lit" ? w.term.value : void 0;
2550
+ if (w.term?.op !== "lit") return [];
2551
+ if (typeof argLit !== "number" && typeof argLit !== "string" && typeof argLit !== "boolean" && argLit !== null && argLit !== void 0) {
2552
+ return [];
2553
+ }
2554
+ if (cc.prim) {
2555
+ const prim = witnessPrimType(w);
2556
+ if (prim !== void 0 && prim !== cc.prim) {
2557
+ return [
2558
+ { actual: w, expected: `typeof ${path} = "${cc.prim}"`, path, missing: false }
2559
+ ];
2560
+ }
2561
+ }
2562
+ if (!literalMeetsConstraint(argLit, cc)) {
2563
+ return [{ actual: w, expected: formatConstraint(cc), path, missing: false }];
2564
+ }
2565
+ return [];
2566
+ }
1976
2567
  function scanCaseInconsistency(source, knownFns, opts) {
1977
2568
  const out = [];
1978
2569
  const directiveSrc = stripStringsKeepComments(source);
@@ -1980,57 +2571,6 @@ function scanCaseInconsistency(source, knownFns, opts) {
1980
2571
  const hasContractOrigin = directiveSrc.includes("@nudo:contract") || opts.sidecarPresent === true;
1981
2572
  if (!hasContractOrigin) return out;
1982
2573
  const file = opts.file ?? parseSource(source);
1983
- const parseLitArg = (s) => {
1984
- const t = s.trim();
1985
- if (t === "") return void 0;
1986
- if (t === "true") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: true }, conf: "exact" };
1987
- if (t === "false") return { shape: { k: "prim", type: "boolean" }, term: { op: "lit", value: false }, conf: "exact" };
1988
- if (t === "null") return { shape: { k: "unknown" }, term: { op: "lit", value: null }, conf: "exact" };
1989
- if (t === "undefined") return { shape: { k: "unknown" }, term: { op: "lit", value: void 0 }, conf: "exact" };
1990
- if (/^-?\d+(\.\d+)?$/.test(t)) return numLit(Number(t));
1991
- const str2 = t.match(/^(['"])([\s\S]*)\1$/);
1992
- if (str2) {
1993
- return {
1994
- shape: { k: "prim", type: "string" },
1995
- term: { op: "lit", value: str2[2] },
1996
- conf: "exact"
1997
- };
1998
- }
1999
- return void 0;
2000
- };
2001
- const parseCaseArgs = (raw) => {
2002
- const m = raw.match(/@nudo:case\s+"[^"]+"\s*\(([\s\S]*)\)/);
2003
- if (!m) return void 0;
2004
- const inner = m[1].trim();
2005
- if (inner === "") return [];
2006
- const parts = [];
2007
- let depth = 0;
2008
- let cur = "";
2009
- let quote = null;
2010
- for (let i = 0; i < inner.length; i++) {
2011
- const ch = inner[i];
2012
- if (quote) {
2013
- cur += ch;
2014
- if (ch === quote && inner[i - 1] !== "\\") quote = null;
2015
- continue;
2016
- }
2017
- if (ch === '"' || ch === "'") {
2018
- quote = ch;
2019
- cur += ch;
2020
- continue;
2021
- }
2022
- if (ch === "(" || ch === "[" || ch === "{") depth++;
2023
- if (ch === ")" || ch === "]" || ch === "}") depth--;
2024
- if (ch === "," && depth === 0) {
2025
- parts.push(cur);
2026
- cur = "";
2027
- continue;
2028
- }
2029
- cur += ch;
2030
- }
2031
- if (cur.trim()) parts.push(cur);
2032
- return parts;
2033
- };
2034
2574
  const checkCaseAgainstReqs = (fnName, caseName, args, line) => {
2035
2575
  const g = generalizeFromAst(fnName, source, {
2036
2576
  ...file ? { file } : {},
@@ -2072,20 +2612,45 @@ function scanCaseInconsistency(source, knownFns, opts) {
2072
2612
  ]);
2073
2613
  }
2074
2614
  if (reqs.length === 0) return;
2075
- const absArgs = args.map((a) => parseLitArg(a) ?? abs({ k: "unknown" }, void 0, void 0, "partial"));
2615
+ const absArgs = args.map((a) => parseCaseArgAbs(a));
2076
2616
  for (const req of reqs) {
2077
2617
  const idx = req[0];
2078
2618
  const entry = req[1];
2079
2619
  const fieldPath = req[2];
2080
- if (entry.constraint.fields) continue;
2620
+ const paramName = entry.param || paramNames[idx] || `arg${idx}`;
2621
+ if (idx >= args.length) {
2622
+ out.push({
2623
+ severity: "warning",
2624
+ code: DIAG.directiveSyntax,
2625
+ message: `${fnName} case "${caseName}": no witness argument for contract slot ${paramName} (${args.length} argument(s) given)`,
2626
+ actual: `${args.length} argument(s)`,
2627
+ expected: `${idx + 1} argument(s)`,
2628
+ suggestion: `add a witness argument for ${paramName}, or drop the contract on ${paramName}`,
2629
+ fn: fnName,
2630
+ line
2631
+ });
2632
+ continue;
2633
+ }
2634
+ if (absArgs[idx] === void 0) {
2635
+ if (isConstraintBuilderArg(args[idx])) continue;
2636
+ out.push({
2637
+ severity: "warning",
2638
+ code: DIAG.directiveSyntax,
2639
+ message: `${fnName} case "${caseName}": witness argument ${idx + 1} could not be parsed \u2014 contract slot ${paramName} was not checked`,
2640
+ actual: args[idx],
2641
+ expected: "a literal witness (number / string / boolean / null / undefined / object literal / array literal)",
2642
+ suggestion: `use a literal witness for ${paramName}, or relax the refine on ${paramName}`,
2643
+ fn: fnName,
2644
+ line
2645
+ });
2646
+ continue;
2647
+ }
2081
2648
  let arg = absArgs[idx];
2082
- if (!arg) continue;
2083
2649
  if (fieldPath) {
2084
- const projected = projectCaseArgField(arg, fieldPath);
2650
+ const projected = projectAbsPath(arg, fieldPath);
2085
2651
  if (!projected) {
2086
2652
  const k = arg.shape.k;
2087
2653
  if (k !== "unknown" && k !== "any") {
2088
- const paramName = entry.param || paramNames[idx] || `arg${idx}`;
2089
2654
  const field = fieldPath.join(".");
2090
2655
  out.push({
2091
2656
  severity: "error",
@@ -2102,6 +2667,22 @@ function scanCaseInconsistency(source, knownFns, opts) {
2102
2667
  }
2103
2668
  arg = projected;
2104
2669
  }
2670
+ const cc = derefDeepConstraint(entry.constraint);
2671
+ if (cc.fields || witnessIsStructure(arg)) {
2672
+ for (const v2 of witnessViolations(cc, arg, "", 0)) {
2673
+ out.push({
2674
+ severity: "error",
2675
+ code: DIAG.caseInconsistency,
2676
+ message: `${fnName} case "${caseName}": witness \u22AD contract`,
2677
+ actual: formatAbs(v2.actual),
2678
+ expected: v2.expected,
2679
+ suggestion: v2.missing ? `add the missing field ${v2.path} to the case argument (contract slot ${paramName})` : `change the case argument, or relax the refine on ${paramName}`,
2680
+ fn: fnName,
2681
+ line
2682
+ });
2683
+ }
2684
+ continue;
2685
+ }
2105
2686
  const argLit = arg.term?.op === "lit" ? arg.term.value : void 0;
2106
2687
  const argIsLit = arg.term?.op === "lit";
2107
2688
  if (!argIsLit || typeof argLit !== "number" && typeof argLit !== "string" && typeof argLit !== "boolean" && argLit !== null && argLit !== void 0) {
@@ -2110,7 +2691,6 @@ function scanCaseInconsistency(source, knownFns, opts) {
2110
2691
  const hasEqOr = (entry.constraint.members?.length ?? 0) > 0 || entry.constraint.preds.some((p) => p.op === "eq");
2111
2692
  if (hasEqOr) {
2112
2693
  if (!literalMeetsConstraint(argLit, entry.constraint)) {
2113
- const paramName = entry.param || paramNames[idx] || `arg${idx}`;
2114
2694
  out.push({
2115
2695
  severity: "error",
2116
2696
  code: DIAG.caseInconsistency,
@@ -2126,7 +2706,6 @@ function scanCaseInconsistency(source, knownFns, opts) {
2126
2706
  }
2127
2707
  if (typeof argLit !== "number") continue;
2128
2708
  if (isIntFlag(entry.constraint) && !Number.isInteger(argLit)) {
2129
- const paramName = entry.param || paramNames[idx] || `arg${idx}`;
2130
2709
  out.push({
2131
2710
  severity: "error",
2132
2711
  code: DIAG.caseInconsistency,
@@ -2148,7 +2727,6 @@ function scanCaseInconsistency(source, knownFns, opts) {
2148
2727
  if (p.op === "lt") ok = argLit < n;
2149
2728
  if (p.op === "le") ok = argLit <= n;
2150
2729
  if (!ok) {
2151
- const paramName = entry.param || paramNames[idx] || `arg${idx}`;
2152
2730
  out.push({
2153
2731
  severity: "error",
2154
2732
  code: DIAG.caseInconsistency,
@@ -2170,8 +2748,7 @@ function scanCaseInconsistency(source, knownFns, opts) {
2170
2748
  const text = scope.commentTexts[ci];
2171
2749
  const startLine = scope.commentStartLines[ci] ?? 0;
2172
2750
  for (const tag of scanCaseTags(text)) {
2173
- const caseArgs = parseCaseArgs(`@nudo:case "${tag.name}" (${tag.argsText})`);
2174
- if (!caseArgs) continue;
2751
+ const caseArgs = splitCaseArgsText(stripCaseContinuation(tag.argsText));
2175
2752
  checkCaseAgainstReqs(scope.name, tag.name, caseArgs, startLine);
2176
2753
  }
2177
2754
  }
@@ -2187,18 +2764,27 @@ function checkSource(filePath, source, phi = pTrue, opts = {}) {
2187
2764
  }
2188
2765
  function checkSourceInScope(filePath, source, phi, opts) {
2189
2766
  let callOpts = opts;
2767
+ const loadReadFailures = [];
2768
+ const loadFailureKeys = /* @__PURE__ */ new Set();
2769
+ let speculativeLoad;
2190
2770
  if (opts.loadModule && opts.fromFile) {
2191
2771
  const loadCache = /* @__PURE__ */ new Map();
2192
2772
  const raw = opts.loadModule;
2193
- const fromFile = opts.fromFile;
2773
+ const cachedLoad = (spec, from) => {
2774
+ const k = `${from}\0${spec}`;
2775
+ if (!loadCache.has(k)) loadCache.set(k, raw(spec, from));
2776
+ return loadCache.get(k);
2777
+ };
2194
2778
  callOpts = {
2195
2779
  ...opts,
2196
- loadModule: (spec, from) => {
2197
- const k = `${from}\0${spec}`;
2198
- if (!loadCache.has(k)) loadCache.set(k, raw(spec, from));
2199
- return loadCache.get(k);
2200
- }
2780
+ loadModule: guardLoadModule(cachedLoad, (failure) => {
2781
+ const key = `${failure.path}\0${failure.code}`;
2782
+ if (loadFailureKeys.has(key)) return;
2783
+ loadFailureKeys.add(key);
2784
+ loadReadFailures.push(failure);
2785
+ })
2201
2786
  };
2787
+ speculativeLoad = cachedLoad;
2202
2788
  }
2203
2789
  setRefineDiagCollector(null);
2204
2790
  setInterfaceDiagCollector(null);
@@ -2208,11 +2794,20 @@ function checkSourceInScope(filePath, source, phi, opts) {
2208
2794
  const useMemo = phi.op === "true";
2209
2795
  let memoKey;
2210
2796
  const stable = stableAnalyzeKeySource(source);
2211
- const depsFp = checkDepsFingerprint(stable, callOpts);
2212
- const sidecarFp = callOpts.loadModule && callOpts.fromFile ? sidecarClosureFingerprint(callOpts.fromFile, callOpts) : void 0;
2797
+ const depsFp = callOpts.loadModule && callOpts.fromFile ? loadModuleDepsFingerprint(stable, callOpts.loadModule, callOpts.fromFile, {
2798
+ ...speculativeLoad ? { ambientLoadModule: speculativeLoad } : {},
2799
+ ambientAllowed: (sidecarPath) => sidecarAutoBindAllowed(sidecarPath, opts.autoBind, opts.projectDir)
2800
+ }) : { fp: "-", paths: [], contents: [], truncated: false, readError: false };
2801
+ const sidecarFp = callOpts.loadModule && callOpts.fromFile ? sidecarClosureFingerprint(callOpts.fromFile, {
2802
+ ...callOpts,
2803
+ ...speculativeLoad ? { loadModule: speculativeLoad } : {}
2804
+ }) : void 0;
2213
2805
  const allowMemo = useMemo && !depsFp.truncated && // 读错误轮键不可信(#135):错误轮报告(nudo:interface-load)与真 miss 轮
2214
2806
  // (干净)若共享 miss 键会互为跨次陈旧命中——不读也不写 memo
2215
- !depsFp.readError && !(sidecarFp?.startsWith("trunc:") ?? false);
2807
+ !depsFp.readError && // Bug 499:本闸把读失败降级成 miss(指纹面看不到 readError)——但
2808
+ // 「不可读」轮与「真不存在」轮的报告不同(前者多一条读失败诊断),
2809
+ // 共用同一键会互为陈旧命中 ⇒ 读失败轮一律不碰 memo。
2810
+ loadReadFailures.length === 0 && !(sidecarFp?.startsWith("trunc:") ?? false);
2216
2811
  if (allowMemo) {
2217
2812
  memoKey = checkMemoKey(filePath, stable, opts, depsFp, sidecarFp);
2218
2813
  const hit = checkMemoGet(memoKey);
@@ -2225,7 +2820,10 @@ function checkSourceInScope(filePath, source, phi, opts) {
2225
2820
  const issues = [];
2226
2821
  const signatures = [];
2227
2822
  const file = parseSource(source);
2228
- const names = listTopFunctions(source, file);
2823
+ const names = [
2824
+ ...listTopFunctions(source, file),
2825
+ ...listClassEntryNames(source, file)
2826
+ ];
2229
2827
  const memoPaths = sidecarFp !== void 0 && callOpts.fromFile ? [.../* @__PURE__ */ new Set([...depsFp.paths, normPath(sidecarPathOf(callOpts.fromFile))])] : depsFp.paths;
2230
2828
  const truncated = /* @__PURE__ */ new Map();
2231
2829
  resetAbsCallBudget();
@@ -2239,7 +2837,7 @@ function checkSourceInScope(filePath, source, phi, opts) {
2239
2837
  makeSameFileFaceResolver(callOpts, filePath, source, file)
2240
2838
  );
2241
2839
  try {
2242
- attachCrossModuleContractFaces(callOpts, filePath);
2840
+ const crossModuleIssues = attachCrossModuleContractFaces(callOpts, filePath);
2243
2841
  const report = checkSourceInner(
2244
2842
  filePath,
2245
2843
  source,
@@ -2258,8 +2856,10 @@ function checkSourceInScope(filePath, source, phi, opts) {
2258
2856
  ...takeRefineDiagsSince(refineSince),
2259
2857
  ...takeInterfaceDiagsSince(ifaceSince)
2260
2858
  ]);
2261
- if (diagIssues.length > 0) {
2262
- report.issues.push(...diagIssues);
2859
+ const loadIssues = loadReadFailureIssues(loadReadFailures);
2860
+ const allIssues = [...diagIssues, ...loadIssues, ...crossModuleIssues];
2861
+ if (allIssues.length > 0) {
2862
+ report.issues.push(...allIssues);
2263
2863
  const errors = report.issues.filter((i) => i.severity === "error").length;
2264
2864
  const warnings = report.issues.filter((i) => i.severity === "warning").length;
2265
2865
  const infos = report.issues.filter((i) => i.severity === "info").length;
@@ -2271,7 +2871,9 @@ function checkSourceInScope(filePath, source, phi, opts) {
2271
2871
  functions: report.summary.functions
2272
2872
  };
2273
2873
  }
2274
- if (memoKey) checkMemoSet(memoKey, report, memoPaths);
2874
+ if (memoKey && loadReadFailures.length === 0) {
2875
+ checkMemoSet(memoKey, report, memoPaths);
2876
+ }
2275
2877
  return cloneCheckReport(report);
2276
2878
  } finally {
2277
2879
  setContractFaceResolver(prevFaceResolver);
@@ -2280,6 +2882,14 @@ function checkSourceInScope(filePath, source, phi, opts) {
2280
2882
  setAbsTruncationCollector(prevTrunc);
2281
2883
  }
2282
2884
  }
2885
+ function loadReadFailureIssues(failures) {
2886
+ return failures.map((f) => ({
2887
+ severity: "error",
2888
+ code: DIAG.moduleUnreadable,
2889
+ message: `cannot read module '${f.path}' (${f.code}): ${f.message}`,
2890
+ suggestion: "fix the file permissions (or the module path) \u2014 the dependency is present but unreadable, so its types are unknown"
2891
+ }));
2892
+ }
2283
2893
  function makeSameFileFaceResolver(opts, filePath, source, file) {
2284
2894
  const loadModule = opts.loadModule;
2285
2895
  const fromFile = opts.fromFile ?? filePath;
@@ -2307,24 +2917,48 @@ function makeSameFileFaceResolver(opts, filePath, source, file) {
2307
2917
  return faces;
2308
2918
  };
2309
2919
  }
2920
+ function unmatchedContractParams(eff, formalDisplay, formals) {
2921
+ const contractNames = contractParamNameSet(formals);
2922
+ const display = new Set(formalDisplay);
2923
+ const conflictNames = new Set(eff.conflict?.params ?? []);
2924
+ return eff.params.map((p) => p.param).filter(
2925
+ (p) => p && !display.has(p) && !contractNames.has(p) && !conflictNames.has(p)
2926
+ );
2927
+ }
2928
+ function paramMismatchIssue(name, unknownParams, formalDisplay, formals) {
2929
+ const contractNames = contractParamNameSet(formals);
2930
+ const surface = [...contractNames].filter((n) => !n.startsWith("_p") && n !== "_").join(", ");
2931
+ return {
2932
+ severity: "error",
2933
+ code: DIAG.interfaceParamMismatch,
2934
+ message: `${name}: contract parameter name(s) not in the formal parameter list (${unknownParams.join(", ")})`,
2935
+ actual: unknownParams.join(", "),
2936
+ expected: surface || formalDisplay.join(", ") || "(no params)",
2937
+ suggestion: `rename the @nudo:contract / sidecar binding parameter to one of: ${surface || formalDisplay.join(", ") || "(function has no params)"}`,
2938
+ fn: name
2939
+ };
2940
+ }
2310
2941
  function attachCrossModuleContractFaces(opts, filePath) {
2311
2942
  const loadModule = opts.loadModule;
2312
2943
  const fromFile = opts.fromFile ?? filePath;
2313
- if (!loadModule) return;
2944
+ if (!loadModule) return [];
2314
2945
  const tables = [];
2315
2946
  for (const t of [opts.modules, opts.inject?.modules]) {
2316
2947
  if (t && typeof t === "object" && !tables.includes(t)) tables.push(t);
2317
2948
  }
2318
- if (tables.length === 0) return;
2949
+ if (tables.length === 0) return [];
2319
2950
  const autoBind = opts.autoBind;
2320
2951
  const projectDir = opts.projectDir;
2952
+ const mismatchIssues = [];
2321
2953
  const facesCache = /* @__PURE__ */ new Map();
2322
2954
  const facesFor = (depSource, depPath, name, paramNames) => {
2323
2955
  const key = `${depPath}\0${name}`;
2324
- if (facesCache.has(key)) return facesCache.get(key);
2956
+ const hit = facesCache.get(key);
2957
+ if (hit) return hit;
2325
2958
  let faces;
2959
+ let eff;
2326
2960
  try {
2327
- const eff = effectiveInterface(depSource, name, {
2961
+ eff = effectiveInterface(depSource, name, {
2328
2962
  loadModule,
2329
2963
  fromFile: depPath,
2330
2964
  ...autoBind !== void 0 ? { autoBind } : {},
@@ -2333,22 +2967,36 @@ function attachCrossModuleContractFaces(opts, filePath) {
2333
2967
  if (eff) faces = contractFacesFromEffectiveInterface(eff, paramNames);
2334
2968
  } catch {
2335
2969
  faces = void 0;
2970
+ eff = void 0;
2336
2971
  }
2337
- facesCache.set(key, faces);
2338
- return faces;
2972
+ const out = { faces, eff };
2973
+ facesCache.set(key, out);
2974
+ return out;
2339
2975
  };
2340
2976
  const attachFnAbs = (v2, depSource, depPath, name) => {
2341
2977
  if (!v2 || typeof v2 !== "object" || !("shape" in v2)) return;
2342
2978
  const shape2 = v2.shape;
2343
2979
  if (shape2.k !== "fn") return;
2344
- let paramNames;
2980
+ let paramNames = [];
2981
+ let formals = [];
2345
2982
  try {
2346
- paramNames = extractFn(depSource, name)?.params ?? [];
2983
+ const meta = extractFn(depSource, name);
2984
+ paramNames = meta?.params ?? [];
2985
+ formals = meta?.formals ?? [];
2347
2986
  } catch {
2348
2987
  paramNames = [];
2988
+ formals = [];
2349
2989
  }
2350
- const faces = facesFor(depSource, depPath, name, paramNames);
2990
+ const { faces, eff } = facesFor(depSource, depPath, name, paramNames);
2351
2991
  if (faces) attachContractFaces(v2, faces);
2992
+ if (eff?.source === "handwritten") {
2993
+ const unknownParams = unmatchedContractParams(eff, paramNames, formals);
2994
+ if (unknownParams.length > 0) {
2995
+ mismatchIssues.push(
2996
+ paramMismatchIssue(name, unknownParams, paramNames, formals)
2997
+ );
2998
+ }
2999
+ }
2352
3000
  };
2353
3001
  for (const table of tables) {
2354
3002
  for (const [spec, mod2] of Object.entries(table)) {
@@ -2370,6 +3018,7 @@ function attachCrossModuleContractFaces(opts, filePath) {
2370
3018
  if (def !== void 0) attachFnAbs(def, depSource, depPath, "default");
2371
3019
  }
2372
3020
  }
3021
+ return mismatchIssues;
2373
3022
  }
2374
3023
  function sidecarDiagIssues(diags) {
2375
3024
  const seen = /* @__PURE__ */ new Set();
@@ -2379,7 +3028,14 @@ function sidecarDiagIssues(diags) {
2379
3028
  if (seen.has(k)) continue;
2380
3029
  seen.add(k);
2381
3030
  const severity = d.code === DIAG.contractSyntax || d.code === DIAG.directiveSyntax ? "warning" : "error";
2382
- out.push({ severity, code: d.code, message: d.message });
3031
+ out.push({
3032
+ severity,
3033
+ code: d.code,
3034
+ message: d.message,
3035
+ // Bug 374:位置透传(nudo:contract-syntax 不再落在文件第 1 行第 1 列)
3036
+ ...d.line !== void 0 ? { line: d.line } : {},
3037
+ ...d.column !== void 0 ? { column: d.column } : {}
3038
+ });
2383
3039
  }
2384
3040
  return out;
2385
3041
  }
@@ -2397,6 +3053,7 @@ function assumePremisesPhiOf(name, source, file, issues) {
2397
3053
  const preds = [];
2398
3054
  const display = [];
2399
3055
  for (const seg of segs) {
3056
+ const pos = seg.line !== void 0 ? { line: seg.line, ...seg.column !== void 0 ? { column: seg.column } : {} } : {};
2400
3057
  if (seg.kind === "bad") {
2401
3058
  issues.push({
2402
3059
  severity: "error",
@@ -2405,7 +3062,8 @@ function assumePremisesPhiOf(name, source, file, issues) {
2405
3062
  actual: seg.raw,
2406
3063
  expected: "<param> <= <param>",
2407
3064
  suggestion: "cross-variable lemmas: @nudo:assume d <= max \u2014 constant bounds: sidecar number().le(10)",
2408
- fn: name
3065
+ fn: name,
3066
+ ...pos
2409
3067
  });
2410
3068
  continue;
2411
3069
  }
@@ -2418,7 +3076,8 @@ function assumePremisesPhiOf(name, source, file, issues) {
2418
3076
  actual: bad.join(", "),
2419
3077
  expected: surface || "(no params/locals)",
2420
3078
  suggestion: `reference one of: ${surface || "(no params or top-level locals)"}`,
2421
- fn: name
3079
+ fn: name,
3080
+ ...pos
2422
3081
  });
2423
3082
  continue;
2424
3083
  }
@@ -2426,7 +3085,7 @@ function assumePremisesPhiOf(name, source, file, issues) {
2426
3085
  display.push(`${seg.lhs} <= ${seg.rhs}`);
2427
3086
  }
2428
3087
  if (preds.length === 0) return void 0;
2429
- return { phi: and(...preds), display };
3088
+ return { phi: and(...preds), display, premises: preds };
2430
3089
  }
2431
3090
  function invariantSpecsOf(name, source, file, issues) {
2432
3091
  const segs = extractInvariantSegs(source, name);
@@ -2441,6 +3100,7 @@ function invariantSpecsOf(name, source, file, issues) {
2441
3100
  const specs = [];
2442
3101
  const display = [];
2443
3102
  for (const seg of segs) {
3103
+ const pos = seg.line !== void 0 ? { line: seg.line, ...seg.column !== void 0 ? { column: seg.column } : {} } : {};
2444
3104
  if (seg.kind === "bad") {
2445
3105
  issues.push({
2446
3106
  severity: "warning",
@@ -2449,7 +3109,8 @@ function invariantSpecsOf(name, source, file, issues) {
2449
3109
  actual: seg.raw,
2450
3110
  expected: "<name><path> <rel> <number>",
2451
3111
  suggestion: "loop-carried bounds: @nudo:invariant dp[*][*] >= 0 \u2014 scalar: @nudo:invariant last >= 1",
2452
- fn: name
3112
+ fn: name,
3113
+ ...pos
2453
3114
  });
2454
3115
  continue;
2455
3116
  }
@@ -2461,7 +3122,8 @@ function invariantSpecsOf(name, source, file, issues) {
2461
3122
  actual: seg.name,
2462
3123
  expected: surface || "(no params/locals)",
2463
3124
  suggestion: `reference one of: ${surface || "(no params or top-level locals)"}`,
2464
- fn: name
3125
+ fn: name,
3126
+ ...pos
2465
3127
  });
2466
3128
  continue;
2467
3129
  }
@@ -2471,6 +3133,36 @@ function invariantSpecsOf(name, source, file, issues) {
2471
3133
  if (specs.length === 0) return void 0;
2472
3134
  return { specs, display };
2473
3135
  }
3136
+ function dedupeAssignRecords(records) {
3137
+ const seen = /* @__PURE__ */ new Set();
3138
+ const out = [];
3139
+ for (const r of records) {
3140
+ const key = JSON.stringify([
3141
+ r.name,
3142
+ r.line ?? "",
3143
+ r.column ?? "",
3144
+ r.conditional ? "1" : "0",
3145
+ assignValueKey(r.prev),
3146
+ assignValueKey(r.next)
3147
+ ]);
3148
+ if (seen.has(key)) continue;
3149
+ seen.add(key);
3150
+ out.push(r);
3151
+ }
3152
+ return out;
3153
+ }
3154
+ function assignValueKey(v2) {
3155
+ if (v2 === void 0) return "-";
3156
+ if (v2 === null) return "null";
3157
+ if (typeof v2 !== "object") return `host:${typeof v2}:${String(v2)}`;
3158
+ const o = v2;
3159
+ if (!("shape" in o) || !("conf" in o)) return `host:${typeof v2}`;
3160
+ try {
3161
+ return formatAbs(v2);
3162
+ } catch {
3163
+ return `abs:${o.shape?.k ?? "?"}:${o.conf ?? "?"}`;
3164
+ }
3165
+ }
2474
3166
  var OPAQUE_EVIDENCE_HINT = "add @nudo:case or a call site";
2475
3167
  var OPAQUE_ENV_HINT = "declare the runtime env to type native modules: '/// @nudo:env node' (or web/es) \u2014 see guides/env-harvest.md";
2476
3168
  function isBuiltinModuleSpec(spec) {
@@ -2505,9 +3197,12 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2505
3197
  eiCache.set(key, eff);
2506
3198
  return eff;
2507
3199
  };
3200
+ const records = [];
3201
+ const prevAssign = setEvalAssignCollector((r) => records.push(r));
2508
3202
  for (const name of names) {
2509
3203
  const assumePhi = assumePremisesPhiOf(name, source, file, issues);
2510
3204
  const inv = invariantSpecsOf(name, source, file, issues);
3205
+ resetAssumeConsumed();
2511
3206
  const runSeeded = (body) => {
2512
3207
  if (assumePhi === void 0 && inv === void 0) return body();
2513
3208
  return withAssumePremises(assumePhi?.phi ?? pTrue, () => withInvariantSpecs(inv?.specs ?? [], body));
@@ -2525,6 +3220,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2525
3220
  contractParam.set(p.param, constraintToEntryAbs(p.constraint, p.param));
2526
3221
  }
2527
3222
  }
3223
+ const skipFmt = formatSigCached(retAbs, name);
2528
3224
  signatures.push({
2529
3225
  name,
2530
3226
  params,
@@ -2533,8 +3229,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2533
3229
  return a ? formatShape(a) : "any";
2534
3230
  }),
2535
3231
  abs: retAbs,
2536
- display: formatAbs(retAbs),
2537
- detail: formatAbsMultiline(retAbs, name),
3232
+ display: skipFmt.display,
3233
+ detail: skipFmt.detail,
2538
3234
  conf: retAbs.conf,
2539
3235
  ...isEntryFn ? { entry: true } : {}
2540
3236
  });
@@ -2544,12 +3240,29 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2544
3240
  fromFile: refineFrom
2545
3241
  });
2546
3242
  const display = named?.name ?? formatConstraint(eff.returns.constraint);
2547
- returnContracts.set(name, { display, constraint: eff.returns.constraint });
3243
+ const contractSource = named ? "directive" : "sidecar";
3244
+ returnContracts.set(name, {
3245
+ display,
3246
+ constraint: eff.returns.constraint,
3247
+ source: contractSource
3248
+ });
2548
3249
  const retIssues = checkReturnConstraint(
2549
3250
  name,
2550
3251
  display,
2551
3252
  eff.returns.constraint,
2552
- declared
3253
+ declared,
3254
+ {
3255
+ // #369 / #372:issue 构造处带函数声明位置
3256
+ loc: findFnLoc(file, name),
3257
+ source: contractSource,
3258
+ // Bug 1094:返回点的路径前提 Φ(用户引理前提)——skip 分支同样
3259
+ // 透传(函数级公理在 skip 面也成立;证明通道按「同项 ∧ 蕴含」
3260
+ // 口径消费 ⇒ 不会凭空制造确定违约)。assumeDisplay 一并带上 ⇒
3261
+ // Φ 被消费时归因(nudo:assume-relied)仍然可见(不得静默消费)。
3262
+ phi: assumePhi?.phi,
3263
+ phiPremises: assumePhi?.premises,
3264
+ assumeDisplay: assumePhi?.display
3265
+ }
2553
3266
  );
2554
3267
  if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
2555
3268
  issues.push(...retIssues);
@@ -2587,7 +3300,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2587
3300
  };
2588
3301
  const effects = collectEntryMayThrows(source, name, synthetic, file, phi, opts);
2589
3302
  const throwsDisplayFallback = formatThrowsAbs(mayThrowEffectsToAbs(effects));
2590
- const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
3303
+ const declared = declaredThrowsForEntry(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
2591
3304
  const remaining = filterGateThrows(effects, declared, ignoreThrows);
2592
3305
  const gateDisplay = formatThrowsAbs(mayThrowEffectsToAbs(remaining));
2593
3306
  if (gateDisplay && remaining.length > 0) {
@@ -2632,7 +3345,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2632
3345
  if (isEntry && entryThrowsMode !== "off") {
2633
3346
  const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi, opts));
2634
3347
  throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
2635
- const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
3348
+ const declared = declaredThrowsForEntry(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
2636
3349
  const remaining = filterGateThrows(effects, declared, ignoreThrows);
2637
3350
  const gateDisplay = formatThrowsAbs(mayThrowEffectsToAbs(remaining));
2638
3351
  if (gateDisplay && remaining.length > 0) {
@@ -2658,7 +3371,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2658
3371
  const paramTypes = g.typeParams.map((t) => {
2659
3372
  if (!t.value) return "any";
2660
3373
  if (t.value.shape.k === "unknown") return "unknown";
2661
- return formatShape(t.value);
3374
+ return formatEntryParamShape(t.value);
2662
3375
  });
2663
3376
  signatures.push({
2664
3377
  name,
@@ -2678,7 +3391,12 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2678
3391
  // 的排除一致。
2679
3392
  (t) => t.value && t.value.shape.k === "unknown" && t.value.term?.op !== "lit"
2680
3393
  );
2681
- const budgetExplained = truncated.has(name) || g.symbolic?.conf === "opaque" || g.symbolic?.shape?.k === "sum" && g.symbolic.shape.members.every((m) => m.shape.k === "unknown" || m.shape.k === "any");
3394
+ const budgetExplained = truncated.has(name) || // Bug 389:`conf=opaque` 通常意味着「路径没覆盖」⇒ 已解释;但**因 env
3395
+ // 门禁而 opaque**(源码声明了 `@nudo:env` 而那个 env 没解析出全局 ⇒
3396
+ // 函数体 fail-closed)不是「已解释」——那正是「推断没成功」,唯一能提示
3397
+ // 它的 `nudo:unknown-inference` 不能被吞(否则用户把 env 名写错的那一刻,
3398
+ // 唯一的失败信号恰好消失)。
3399
+ g.symbolic?.conf === "opaque" && !g.envGated || g.symbolic?.shape?.k === "sum" && g.symbolic.shape.members.every((m) => m.shape.k === "unknown" || m.shape.k === "any");
2682
3400
  const symbolicTrueUnknown = g.symbolic?.shape?.k === "unknown" && g.symbolic.term?.op !== "lit";
2683
3401
  if ((symbolicTrueUnknown || unknownParamIdx >= 0) && !budgetExplained) {
2684
3402
  const where = symbolicTrueUnknown ? `${name} => unknown` : `${name} param ${g.params[unknownParamIdx] ?? `#${unknownParamIdx}`}: unknown`;
@@ -2688,32 +3406,32 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2688
3406
  message: `${name}: signature has true unknown (inference failed)`,
2689
3407
  actual: where,
2690
3408
  expected: "computable Abs (any = unconstrained, unknown = engine debt)",
2691
- suggestion: "add @nudo:case / env mock / refine, or confirm the body is algebraically evaluable",
3409
+ // Bug 1062(族 46):本诊断此前把「`@nudo:replace` 的目标文本没命中」
3410
+ // 说成「推断精度不够」⇒ 归因错误 ⇒ 用户只看到 unknown-inference。
3411
+ // service 侧已加「点名目标文本」的显式诊断(`nudo:directive-syntax`),
3412
+ // 这里补一句归因提示,把「指令没生效」这条可能性点出来。
3413
+ suggestion: "add @nudo:case / env mock / refine, or confirm the body is algebraically evaluable; if you used @nudo:replace, check the target text matches the source",
2692
3414
  fn: name
2693
3415
  });
2694
3416
  }
2695
3417
  if (hasRefineDirective || (exportedNames?.has(name) ?? false)) {
2696
3418
  const eff = effectiveInterfaceCached(name);
2697
3419
  if (eff?.source === "handwritten") {
2698
- const contractNames = contractParamNameSet(g.formals ?? []);
2699
- const formalDisplay = new Set(g.params);
2700
- const conflictNames = new Set(eff.conflict?.params ?? []);
2701
- const unknownParams = eff.params.map((p) => p.param).filter(
2702
- (p) => p && !formalDisplay.has(p) && !contractNames.has(p) && !conflictNames.has(p)
2703
- );
3420
+ const unknownParams = unmatchedContractParams(eff, g.params, g.formals ?? []);
2704
3421
  if (unknownParams.length > 0) {
2705
- const surface = [...contractNames].filter((n) => !n.startsWith("_p") && n !== "_").join(", ");
2706
- issues.push({
2707
- severity: "error",
2708
- code: DIAG.interfaceParamMismatch,
2709
- message: `${name}: contract parameter name(s) not in the formal parameter list (${unknownParams.join(", ")})`,
2710
- actual: unknownParams.join(", "),
2711
- expected: surface || g.params.join(", ") || "(no params)",
2712
- suggestion: `rename the @nudo:contract / sidecar binding parameter to one of: ${surface || g.params.join(", ") || "(function has no params)"}`,
2713
- fn: name
2714
- });
3422
+ issues.push(
3423
+ paramMismatchIssue(name, unknownParams, g.params, g.formals ?? [])
3424
+ );
2715
3425
  }
2716
3426
  }
3427
+ const contractLocs = contractSegmentLocs(source, name);
3428
+ const locOf = (param) => {
3429
+ const hit = contractLocs.find((c) => {
3430
+ const head = c.segment.trim().split(/\s+/)[0];
3431
+ return head === param;
3432
+ });
3433
+ return hit?.loc ? { line: hit.loc.line, ...hit.loc.column !== void 0 ? { column: hit.loc.column } : {} } : {};
3434
+ };
2717
3435
  if (eff?.conflict) {
2718
3436
  if (eff.conflict.params.length > 0) {
2719
3437
  issues.push({
@@ -2721,7 +3439,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2721
3439
  code: DIAG.interfaceConflict,
2722
3440
  message: `${name}: handwritten contract conjunction unsatisfiable (${eff.conflict.params.join(", ")})`,
2723
3441
  suggestion: "check whether the source @nudo:contract and the same-name sidecar binding have contradictory constant bounds",
2724
- fn: name
3442
+ fn: name,
3443
+ ...locOf(eff.conflict.params[0])
2725
3444
  });
2726
3445
  }
2727
3446
  if (eff.conflict.returns) {
@@ -2730,24 +3449,70 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2730
3449
  code: DIAG.interfaceConflict,
2731
3450
  message: `${name}: handwritten contract conjunction unsatisfiable on the return slot`,
2732
3451
  suggestion: "check whether the source @nudo:contract return and the sidecar fn() return constraint are contradictory (the return slot is not enforced when they are)",
2733
- fn: name
3452
+ fn: name,
3453
+ ...locOf("return")
2734
3454
  });
2735
3455
  }
2736
3456
  }
3457
+ const undecidable = eff?.undecidable;
3458
+ if (undecidable !== void 0 && undecidable.params.length > 0) {
3459
+ issues.push({
3460
+ severity: "warning",
3461
+ code: DIAG.interfaceConflict,
3462
+ message: `${name}: contract conjunction not computable (${undecidable.params.join(", ")}) \u2014 only one side is enforced`,
3463
+ suggestion: "structural contracts (shape / array / union / fn) cannot be combined in this slot; the other side's obligations are NOT enforced \u2014 merge them by hand",
3464
+ fn: name,
3465
+ ...locOf(undecidable.params[0])
3466
+ });
3467
+ }
3468
+ if (undecidable?.returns) {
3469
+ issues.push({
3470
+ severity: "warning",
3471
+ code: DIAG.interfaceConflict,
3472
+ message: `${name}: contract conjunction not computable on the return slot \u2014 only one side is enforced`,
3473
+ suggestion: "structural return constraints cannot be combined in this slot; the other side's obligations are NOT enforced \u2014 merge them by hand",
3474
+ fn: name,
3475
+ ...locOf("return")
3476
+ });
3477
+ }
2737
3478
  if (eff && eff.source === "handwritten" && eff.returns && !eff.conflict?.returns) {
2738
3479
  const named = extractRefineReturnFromSource(source, name, {
2739
3480
  loadModule: refineLoad,
2740
3481
  fromFile: refineFrom
2741
3482
  });
2742
3483
  const display = named?.name ?? formatConstraint(eff.returns.constraint);
2743
- returnContracts.set(name, { display, constraint: eff.returns.constraint });
3484
+ const contractSource = named ? "directive" : "sidecar";
3485
+ returnContracts.set(name, {
3486
+ display,
3487
+ constraint: eff.returns.constraint,
3488
+ source: contractSource
3489
+ });
3490
+ const consumedAssume = assumePhi ? new Set(
3491
+ assumePhi.display.filter(
3492
+ (_, i) => assumeConsumed().includes(assumePhi.premises[i])
3493
+ )
3494
+ ) : void 0;
2744
3495
  const retIssues = checkReturnConstraint(
2745
3496
  name,
2746
3497
  display,
2747
3498
  eff.returns.constraint,
2748
3499
  g.symbolic,
2749
- assumePhi?.display,
2750
- inv?.display
3500
+ {
3501
+ // #369 / #372:issue 构造处带函数声明位置
3502
+ loc: findFnLoc(file, name),
3503
+ source: contractSource,
3504
+ assumeDisplay: assumePhi?.display,
3505
+ invariantDisplay: inv?.display,
3506
+ consumedAssume,
3507
+ // Bug 1094:返回点的**路径前提 Φ** = 用户引理前提
3508
+ //(`@nudo:assume` 的前提 Φ)——证明通道的 Φ 侧。
3509
+ // 项身份:前提项用**真实形参名**(`termVar(name)`),泛化后的返回
3510
+ // 项在形参**有契约**时同样锚在真实名上(`constraintToEntryAbs`)
3511
+ // ⇒ 同项判定能对上;无契约时返回项锚在 α 名(A1…)⇒ 对不上 ⇒
3512
+ // Φ 侧不生效(保守少证,不假红)。
3513
+ phi: assumePhi?.phi,
3514
+ phiPremises: assumePhi?.premises
3515
+ }
2751
3516
  );
2752
3517
  if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
2753
3518
  issues.push(...retIssues);
@@ -2760,7 +3525,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2760
3525
  });
2761
3526
  }
2762
3527
  }
2763
- if (g.symbolic.conf === "opaque" && !truncated.has(name)) {
3528
+ if (g.symbolic.conf === "opaque" && !truncated.has(name) && !g.envGated) {
2764
3529
  issues.push({
2765
3530
  severity: "info",
2766
3531
  code: DIAG.opaqueResult,
@@ -2839,16 +3604,19 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2839
3604
  fn: label
2840
3605
  });
2841
3606
  }
2842
- const records = [];
2843
3607
  const varAbs = /* @__PURE__ */ new Map();
2844
3608
  const callRecords = [];
2845
3609
  const bCalls = [];
2846
- const prevAssign = setEvalAssignCollector((r) => records.push(r));
2847
3610
  const prevCall = setEvalCallCollector((r) => bCalls.push(r));
2848
3611
  let evalBindings;
2849
3612
  const evalAnalyze = evalAnalyzeOpts(opts);
3613
+ let moduleThrow;
2850
3614
  try {
2851
- const evalRun = tryRunTranspiled(source, evalAnalyze);
3615
+ const evalAttempt = tryRunTranspiledDetailed(source, evalAnalyze);
3616
+ const evalRun = evalAttempt.run;
3617
+ if (evalRun === void 0 && evalAttempt.fallback?.reason === "module-throw") {
3618
+ moduleThrow = definiteModuleThrowOf(file);
3619
+ }
2852
3620
  evalBindings = evalRun ? bindingsOf(evalRun) : void 0;
2853
3621
  if (evalBindings) {
2854
3622
  for (const [k, v2] of evalBindings) {
@@ -2871,6 +3639,22 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2871
3639
  setEvalCallCollector(prevCall);
2872
3640
  }
2873
3641
  void evalBindings;
3642
+ if (moduleThrow !== void 0 && entryThrowsMode !== "off") {
3643
+ const kind = moduleThrow.kind;
3644
+ if (kind === void 0 || !isThrowsIgnored(kind, ignoreThrows)) {
3645
+ const thrown = kind !== void 0 ? ` ${kind}` : "";
3646
+ issues.push({
3647
+ severity: entryThrowsMode === "warning" ? "warning" : "error",
3648
+ code: DIAG.moduleThrow,
3649
+ message: `module initializer always throws${thrown} \u2014 the module cannot be imported, so every export is unavailable (module not analyzed)`,
3650
+ actual: `throw at line ${moduleThrow.line}`,
3651
+ expected: "module initializer that can complete, or a conditional throw",
3652
+ suggestion: kind !== void 0 ? `guard the throw if the module should be importable; if throwing at load is intended, ignore it with --ignore-throws ${kind} (or --entry-throws off)` : "guard the throw if the module should be importable; --entry-throws off disables this gate",
3653
+ line: moduleThrow.line,
3654
+ column: moduleThrow.column
3655
+ });
3656
+ }
3657
+ }
2874
3658
  const callIssues = canSkipLiteralCallScan(source, file) ? [] : scanLiteralCalls(source, names, phi, {
2875
3659
  loadModule: opts.loadModule,
2876
3660
  // 与 generalize / 返回后置同一 fromFile 口径:ambient 侧车按真实
@@ -2896,15 +3680,22 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2896
3680
  rec.fnName,
2897
3681
  rc.display,
2898
3682
  rc.constraint,
2899
- rec.result
3683
+ rec.result,
3684
+ {
3685
+ // #369 / #372:issue 构造处带函数声明位置(被违约函数的声明处)
3686
+ loc: findFnLoc(file, rec.fnName),
3687
+ // #378:约束来源随 returnContracts 走(源码指令 vs 侧车 fn() 返回槽)
3688
+ source: rc.source
3689
+ }
2900
3690
  );
2901
3691
  if (retIssues.some((i) => i.severity === "error")) returnViolated.add(rec.fnName);
2902
3692
  issues.push(...retIssues);
2903
3693
  }
2904
3694
  }
2905
3695
  if (driftCandidates.length > 0) {
3696
+ const driftRecords = opts.from && opts.from.length > 0 ? [...callRecords, ...opts.from.map((r) => ({ fnName: r.fnName, args: r.args, result: r.result }))] : callRecords;
2906
3697
  issues.push(
2907
- ...interfaceDriftIssues(driftCandidates, callRecords, /* @__PURE__ */ (() => {
3698
+ ...interfaceDriftIssues(driftCandidates, driftRecords, /* @__PURE__ */ (() => {
2908
3699
  let driftRun;
2909
3700
  let driftInit = false;
2910
3701
  return (fnName, args) => {
@@ -2914,7 +3705,9 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2914
3705
  driftInit = true;
2915
3706
  }
2916
3707
  if (!driftRun || !(fnName in driftRun)) return void 0;
2917
- return callTranspiledExportFull(driftRun, fnName, args).result;
3708
+ return callTranspiledExportFull(driftRun, fnName, args, {
3709
+ preferLocal: true
3710
+ }).result;
2918
3711
  } catch {
2919
3712
  return void 0;
2920
3713
  }
@@ -2934,7 +3727,16 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2934
3727
  ...opts.inject ? { inject: opts.inject } : {}
2935
3728
  })
2936
3729
  );
2937
- issues.push(...structuralAssignIssues(records));
3730
+ try {
3731
+ issues.push(...structuralAssignIssues(dedupeAssignRecords(records)));
3732
+ } catch (err) {
3733
+ issues.push({
3734
+ severity: "warning",
3735
+ code: DIAG.internal,
3736
+ message: `assign channel check failed: ${err.message}`,
3737
+ suggestion: "this is an internal error; please report it together with the checked source file"
3738
+ });
3739
+ }
2938
3740
  const errors = issues.filter((i) => i.severity === "error").length;
2939
3741
  const warnings = issues.filter((i) => i.severity === "warning").length;
2940
3742
  const infos = issues.filter((i) => i.severity === "info").length;
@@ -2948,60 +3750,6 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2948
3750
  budget: { ...budget }
2949
3751
  };
2950
3752
  }
2951
- function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay, invariantDisplay) {
2952
- const proof = assertImplies(ret, constraint);
2953
- if (proof.status === "proved") {
2954
- const relied = [];
2955
- if (assumeDisplay && assumeDisplay.length > 0) {
2956
- relied.push({
2957
- severity: "info",
2958
- code: DIAG.assumeRelied,
2959
- message: `${fnName}: return contract proved via @nudo:assume ${assumeDisplay.join("; ")}`,
2960
- actual: `proof relies on user lemma${assumeDisplay.length > 1 ? "s" : ""}`,
2961
- expected: "engine verifies the obligation, not the lemma itself",
2962
- suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
2963
- fn: fnName
2964
- });
2965
- }
2966
- if (invariantDisplay && invariantDisplay.length > 0) {
2967
- relied.push({
2968
- severity: "info",
2969
- code: DIAG.invariantRelied,
2970
- message: `${fnName}: return contract proved via @nudo:invariant ${invariantDisplay.join("; ")}`,
2971
- actual: `proof relies on user invariant${invariantDisplay.length > 1 ? "s" : ""}`,
2972
- expected: "engine verifies the obligation, not the loop invariant itself",
2973
- suggestion: "@nudo:invariant is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
2974
- fn: fnName
2975
- });
2976
- }
2977
- return relied;
2978
- }
2979
- const push = (severity, actual, expected, suggestion) => [
2980
- {
2981
- severity,
2982
- code: severity === "error" ? DIAG.constraintViolated : DIAG.unprovenReturn,
2983
- message: `${fnName}: return value \u22AD @nudo:contract return ${cName}`,
2984
- actual,
2985
- expected,
2986
- suggestion,
2987
- fn: fnName
2988
- }
2989
- ];
2990
- if (proof.status === "disproved") {
2991
- return push(
2992
- "error",
2993
- formatAbs(ret),
2994
- proof.reason,
2995
- proof.reason.includes("nullish") ? `use nullable(${formatConstraint(constraint)}) or union(..., lit(null)) to allow nullish returns` : `return a value satisfying ${formatConstraint(constraint)}`
2996
- );
2997
- }
2998
- return push(
2999
- "warning",
3000
- formatAbs(ret),
3001
- formatConstraint(constraint),
3002
- `${proof.reason}; add a precondition, narrow the return expression, or relax the return contract`
3003
- );
3004
- }
3005
3753
  export {
3006
3754
  $add,
3007
3755
  $arguments,
@@ -3011,7 +3759,6 @@ export {
3011
3759
  $arrWithHoles,
3012
3760
  $assignRecord,
3013
3761
  $async,
3014
- $asyncReturn,
3015
3762
  $await,
3016
3763
  $bitand,
3017
3764
  $bitnot,
@@ -3035,7 +3782,6 @@ export {
3035
3782
  $fnVal,
3036
3783
  $for,
3037
3784
  $forInKeys,
3038
- $forIter,
3039
3785
  $forOf,
3040
3786
  $fork,
3041
3787
  $ge,
@@ -3086,12 +3832,9 @@ export {
3086
3832
  $shl,
3087
3833
  $shr,
3088
3834
  $spread,
3089
- $staticInvoke,
3090
3835
  $sub,
3091
3836
  $super,
3092
3837
  $switch,
3093
- $thisGet,
3094
- $thisSet,
3095
3838
  $throw,
3096
3839
  $toNumber,
3097
3840
  $toNumeric,
@@ -3110,7 +3853,6 @@ export {
3110
3853
  $updateAdd,
3111
3854
  $updateSub,
3112
3855
  $ushr,
3113
- $while,
3114
3856
  $whileSeq,
3115
3857
  $yield,
3116
3858
  CONSTRAINT_BUILDERS,
@@ -3137,7 +3879,6 @@ export {
3137
3879
  absToString,
3138
3880
  actionsForIssue,
3139
3881
  add,
3140
- alphaOf,
3141
3882
  and,
3142
3883
  andC,
3143
3884
  any,
@@ -3153,7 +3894,6 @@ export {
3153
3894
  assumeFinite,
3154
3895
  attachFnImpl,
3155
3896
  beginCollectionFork,
3156
- betaOf,
3157
3897
  bigintLit,
3158
3898
  bindImports,
3159
3899
  bindingsOf,
@@ -3189,14 +3929,11 @@ export {
3189
3929
  constraintToEntryAbs,
3190
3930
  contractParamNameSet,
3191
3931
  createEnvironment,
3192
- createHofCollectCtx,
3193
3932
  ctorArgDefinitelyInvalid,
3194
3933
  ctorNameOfRecv,
3195
3934
  currentExecPhi,
3196
- currentPhi,
3197
3935
  definitelyNotNullishShape,
3198
3936
  derefConstraint,
3199
- describePhi,
3200
3937
  div,
3201
3938
  drainPromiseMicros,
3202
3939
  effectiveInterface,
@@ -3266,7 +4003,6 @@ export {
3266
4003
  formatShapeSlot,
3267
4004
  ge,
3268
4005
  geNum,
3269
- generalizeAll,
3270
4006
  generalizeFromAst,
3271
4007
  generatedExportNames,
3272
4008
  getAbsProperty,
@@ -3397,14 +4133,12 @@ export {
3397
4133
  phiAnd,
3398
4134
  pick,
3399
4135
  popCollectionArm,
3400
- popPhi,
3401
4136
  powAbs,
3402
4137
  predEquals,
3403
4138
  predToString,
3404
4139
  predVars,
3405
4140
  primToTypeof,
3406
4141
  projectFlatMapResult,
3407
- promoteParamShape,
3408
4142
  propertyKeyOf,
3409
4143
  protoBrandAbs,
3410
4144
  protoOfRecv,
@@ -3412,7 +4146,6 @@ export {
3412
4146
  pureFnNameOf,
3413
4147
  pushCollectionArm,
3414
4148
  pushLoopExit,
3415
- pushPhi,
3416
4149
  pushThrowExit,
3417
4150
  queuePromiseMicro,
3418
4151
  refineAbsForRelTrue,
@@ -3428,7 +4161,6 @@ export {
3428
4161
  resetGeneralizeMemo,
3429
4162
  resetNudoModuleExecCache,
3430
4163
  resetParseSourceCache,
3431
- resetPhi,
3432
4164
  resetSidecarLoadFailureCache,
3433
4165
  runTranspiled,
3434
4166
  runTranspiledOptionsMemoKey,
@@ -3499,10 +4231,6 @@ export {
3499
4231
  transpileSource,
3500
4232
  trueConstraint,
3501
4233
  tryMakeRegexAbs,
3502
- tryPromoteDirectCall,
3503
- tryPromoteForOfIteratee,
3504
- tryPromoteHofCallback,
3505
- tryPromoteReceiverAsArr,
3506
4234
  tryRunTranspiled,
3507
4235
  typeofAbs,
3508
4236
  undefAbs,
@@ -3511,6 +4239,5 @@ export {
3511
4239
  ushrAbs,
3512
4240
  v,
3513
4241
  withExecPhi,
3514
- withPhiConstraint,
3515
4242
  withVar
3516
4243
  };