@afokapu/atdd-bun 0.10.67 → 0.10.69

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.
@@ -430,12 +430,40 @@ function liveReturns(fn) {
430
430
  // arms of a conditional, as a member/spread of it, an object property or array element, an
431
431
  // `Object.freeze` argument, or a `const` bound to it. Logical, comma, arithmetic, unary (`void`, `!`)
432
432
  // and calls to anything else replace or may skip the value, so they never carry it.
433
+ // A runner result may be declared before a try and assigned exactly once in the
434
+ // try body. The catch must itself exit (throw or return); otherwise a swallowed error would
435
+ // let the later return observe an unassigned value. Do not generalize this to ordinary
436
+ // `let` assignments: conditional or multiply-written values are not a proof chain.
437
+ function caughtSingleAssignment(ctx, identifier) {
438
+ const declaration = declarationOf(ctx, identifier);
439
+ if (!declaration || !ts.isVariableDeclaration(declaration) || declaration.initializer ||
440
+ !ts.isVariableDeclarationList(declaration.parent) || !(declaration.parent.flags & ts.NodeFlags.Let)) return null;
441
+ let fn = declaration.parent;
442
+ while (fn && !ts.isFunctionLike(fn)) fn = fn.parent;
443
+ if (!fn || !fn.body) return null;
444
+ const writes = [];
445
+ walk(fn.body, (node) => {
446
+ if (!ts.isBinaryExpression(node) || node.operatorToken.kind !== ts.SyntaxKind.EqualsToken ||
447
+ !ts.isIdentifier(unwrap(node.left)) || symbolOf(ctx, unwrap(node.left)) !== symbolOf(ctx, identifier) || dead(node, fn.body)) return;
448
+ let inCaughtTry = false;
449
+ for (let parent = node.parent; parent && parent !== fn.body; parent = parent.parent) {
450
+ if (ts.isTryStatement(parent) && parent.tryBlock && node.pos >= parent.tryBlock.pos && node.end <= parent.tryBlock.end && parent.catchClause &&
451
+ parent.catchClause.block.statements.some(statement => ts.isThrowStatement(statement) || ts.isReturnStatement(statement))) {
452
+ inCaughtTry = true;
453
+ break;
454
+ }
455
+ }
456
+ if (inCaughtTry) writes.push(node.right);
457
+ }, false);
458
+ return writes.length === 1 ? writes[0] : null;
459
+ }
460
+
433
461
  function carries(ctx, expression, matches, seen = new Set()) {
434
462
  const node = unwrap(expression);
435
463
  if (!node) return false;
436
464
  if (matches(node)) return true;
437
465
  if (ts.isIdentifier(node)) {
438
- const initializer = constInitializer(ctx, node);
466
+ const initializer = constInitializer(ctx, node) ?? caughtSingleAssignment(ctx, node);
439
467
  // `seen` guards cycles along this path only; sibling branches may resolve the same const.
440
468
  if (!initializer || seen.has(initializer)) return false;
441
469
  seen.add(initializer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afokapu/atdd-bun",
3
- "version": "0.10.67",
3
+ "version": "0.10.69",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/afokapu/atdd-bun.git"