All files / TRANSPILE/3-Render/codegen/analysis MemberChainAnalyzer.ts

100% Statements 7/7
90% Branches 9/10
100% Functions 1/1
100% Lines 7/7

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57                                                                                  27x 12x   15x   27x   27x 8x   7x          
/**
 * MemberChainAnalyzer - Analyzes member access chains for bit access patterns
 *
 * Issue #644: Extracted from CodeGenerator to reduce file size.
 *
 * Used to detect bit access at the end of member chains, e.g.:
 * - grid[2][3].flags[0] - detects that [0] is bit access on flags
 * - s.arr[1][3] - detects that [3] is bit access on an array field's element
 *
 * ## It reads the typer's chain, not its own (#1668, C12)
 *
 * What the final subscript indexes is the one operand typer's answer: the
 * last step of the target's chain says the type before the subscript and the
 * subscript's kind, as 2.1's bit rules and the read path already read them.
 * This class walked the chain itself, through the render state's struct
 * fields, with its own count of how many subscripts an array field takes. That
 * count was wrong for an element of an array field, so `s.arr[1][3] <- true`
 * was emitted as `s.arr[1][3] = true;`, and it knew only C-Next integer names,
 * so a C header's `uint8_t` field was never a bit target. What is left here is
 * the decision; the write itself is `AssignmentHandlerUtils.writeBits`, the
 * one every bit handler uses (#1668 review: this class rendered the base
 * from the source spelling, so a renamed local wrote the global it shadows).
 */
 
import IBitAccessAnalysis from "../../../../types/IBitAccessAnalysis";
import TPlannedTargetOp from "../../../../types/TPlannedTargetOp";
import type IChainStep from "../../../../types/IChainStep";
import OperandTyper from "../../../../utils/OperandTyper";
 
class MemberChainAnalyzer {
  /**
   * Whether the chain's final subscript writes a bit, or a bit range, of an
   * integer or a float.
   *
   * @param lastStep - The typer's step for the final op, or undefined
   * @param ops - The chain's ops
   */
  static analyze(
    lastStep: IChainStep | undefined,
    ops: readonly TPlannedTargetOp[],
  ): IBitAccessAnalysis {
    if (ops.at(-1)?.kind !== "subscript") {
      return { isBitAccess: false };
    }
    const indexed = lastStep?.before ?? null;
    const isBits =
      lastStep?.subscript === "bit_single" ||
      lastStep?.subscript === "bit_range";
    if (!isBits || indexed === null) {
      return { isBitAccess: false };
    }
    return { isBitAccess: OperandTyper.hasWritableBits(indexed) };
  }
}
 
export default MemberChainAnalyzer;