ucn 5.1.1 → 5.2.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.
@@ -1854,6 +1854,34 @@ function jsAssignmentTargetOf(callNode) {
1854
1854
  return undefined;
1855
1855
  }
1856
1856
 
1857
+ /**
1858
+ * For-of iteration target of a call used as the iterable:
1859
+ * `for (const x of getItems())`. The loop variable holds an ELEMENT of the
1860
+ * producer's result, never the return value — provenance only (fix #294).
1861
+ */
1862
+ function jsIterTargetOf(callNode) {
1863
+ let n = callNode;
1864
+ let p = n.parent;
1865
+ if (p && p.type === 'await_expression') { n = p; p = n.parent; }
1866
+ if (!p || p.type !== 'for_in_statement') return undefined;
1867
+ if (p.childForFieldName('operator')?.text !== 'of') return undefined;
1868
+ const right = p.childForFieldName('right');
1869
+ if (!right || right.id !== n.id) return undefined;
1870
+ const left = p.childForFieldName('left');
1871
+ if (!left) return undefined;
1872
+ const names = [];
1873
+ if (left.type === 'identifier') {
1874
+ names.push(left.text);
1875
+ } else if (left.type === 'array_pattern') {
1876
+ for (let i = 0; i < left.namedChildCount; i++) {
1877
+ const c = left.namedChild(i);
1878
+ if (c.type === 'identifier') names.push(c.text);
1879
+ }
1880
+ }
1881
+ if (names.length === 0) return undefined;
1882
+ return { first: names[0], rest: names.slice(1) };
1883
+ }
1884
+
1857
1885
  /**
1858
1886
  * Type name from a new-expression constructor node: new Foo() or new pkg.Foo().
1859
1887
  */
@@ -2463,7 +2491,18 @@ function findCallsInCode(code, parser) {
2463
2491
  const resolvedName = typeof alias === 'string' ? alias : undefined;
2464
2492
  const resolvedNames = Array.isArray(alias) ? alias : undefined;
2465
2493
  const firstArg = getFirstStringArg(node);
2466
- const assignedTo = jsAssignmentTargetOf(node);
2494
+ let assignedTo = jsAssignmentTargetOf(node);
2495
+ let assignedIterFields = {};
2496
+ if (!assignedTo) {
2497
+ const iterTarget = jsIterTargetOf(node);
2498
+ if (iterTarget) {
2499
+ assignedTo = iterTarget.first;
2500
+ assignedIterFields = {
2501
+ assignedIter: true,
2502
+ ...(iterTarget.rest.length > 0 && { assignedTupleRest: iterTarget.rest }),
2503
+ };
2504
+ }
2505
+ }
2467
2506
  // MEDIUM-5: capture explicit method for fetch(url, { method }).
2468
2507
  const optionsMethod = funcNode.text === 'fetch'
2469
2508
  ? getOptionsMethod(node, 1)
@@ -2477,6 +2516,7 @@ function findCallsInCode(code, parser) {
2477
2516
  callEnd: node.endIndex,
2478
2517
  isMethod: false,
2479
2518
  ...(assignedTo && { assignedTo }),
2519
+ ...assignedIterFields,
2480
2520
  enclosingFunction,
2481
2521
  uncertain,
2482
2522
  ...(firstArg && { firstStringArg: firstArg.value, firstStringArgInterp: firstArg.interp }),
@@ -2658,7 +2698,19 @@ function findCallsInCode(code, parser) {
2658
2698
  !localVarTypes.has(receiver));
2659
2699
  const firstArg = getFirstStringArg(node);
2660
2700
  const argCount = getArgCount(node);
2661
- const assignedTo = jsAssignmentTargetOf(node);
2701
+ let assignedTo = jsAssignmentTargetOf(node);
2702
+ let assignedIterFields = {};
2703
+ if (!assignedTo) {
2704
+ const iterTarget = jsIterTargetOf(node);
2705
+ if (iterTarget) {
2706
+ assignedTo = iterTarget.first;
2707
+ assignedIterFields = {
2708
+ assignedIter: true,
2709
+ ...(iterTarget.rest.length > 0 &&
2710
+ { assignedTupleRest: iterTarget.rest }),
2711
+ };
2712
+ }
2713
+ }
2662
2714
  calls.push({
2663
2715
  name: propName,
2664
2716
  // Multi-line chains (builder.x()\n.y()) must report
@@ -2694,6 +2746,7 @@ function findCallsInCode(code, parser) {
2694
2746
  ...(receiverCallStart != null && { receiverCallStart }),
2695
2747
  ...(receiverCallEnd != null && { receiverCallEnd }),
2696
2748
  ...(assignedTo && { assignedTo }),
2749
+ ...assignedIterFields,
2697
2750
  enclosingFunction,
2698
2751
  uncertain,
2699
2752
  ...(firstArg && { firstStringArg: firstArg.value, firstStringArgInterp: firstArg.interp }),
@@ -2742,11 +2795,17 @@ function findCallsInCode(code, parser) {
2742
2795
  const propNode = arg.childForFieldName('property');
2743
2796
  const objNode = arg.childForFieldName('object');
2744
2797
  if (propNode && !SKIP_IDENTS.has(propNode.text)) {
2798
+ const hofRecv = objNode?.type === 'identifier' ? objNode.text : undefined;
2799
+ // Same typing evidence as the general-arg
2800
+ // member-value shape (fix #295) — tier must
2801
+ // not depend on argument position (#234).
2802
+ const hofRecvType = hofRecv ? localVarTypes.get(hofRecv) : undefined;
2745
2803
  calls.push({
2746
2804
  name: propNode.text,
2747
2805
  line: arg.startPosition.row + 1,
2748
2806
  isMethod: true,
2749
- receiver: objNode?.type === 'identifier' ? objNode.text : undefined,
2807
+ receiver: hofRecv,
2808
+ ...(hofRecvType && { receiverType: hofRecvType }),
2750
2809
  isFunctionReference: true,
2751
2810
  enclosingFunction
2752
2811
  });
@@ -2777,6 +2836,29 @@ function findCallsInCode(code, parser) {
2777
2836
  enclosingFunction
2778
2837
  });
2779
2838
  }
2839
+ // Method-value references (fix #295): `expectRaises(t.check, null)`
2840
+ // passes the method value t.check — one-hop identifier
2841
+ // receivers, typed from the same localVarTypes evidence
2842
+ // method calls use. No isPotentialCallback: the record
2843
+ // rides the MAIN path's full receiver physics (the HOF
2844
+ // member-value convention above).
2845
+ if (arg.type === 'member_expression') {
2846
+ const mvProp = arg.childForFieldName('property');
2847
+ const mvObj = arg.childForFieldName('object');
2848
+ if (mvProp && mvObj?.type === 'identifier' &&
2849
+ !SKIP_IDENTS.has(mvProp.text) && !SKIP_IDENTS.has(mvObj.text)) {
2850
+ const mvType = localVarTypes.get(mvObj.text);
2851
+ calls.push({
2852
+ name: mvProp.text,
2853
+ line: arg.startPosition.row + 1,
2854
+ isMethod: true,
2855
+ receiver: mvObj.text,
2856
+ ...(mvType && { receiverType: mvType }),
2857
+ isFunctionReference: true,
2858
+ enclosingFunction
2859
+ });
2860
+ }
2861
+ }
2780
2862
  // Scan object literal args for function refs in property values
2781
2863
  // e.g., doRequest({onSuccess: handleSuccess, onError: handleError})
2782
2864
  if (arg.type === 'object') {