All files / utils StructFieldFacts.ts

83.33% Statements 10/12
70% Branches 7/10
100% Functions 2/2
77.77% Lines 7/9

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 58 59 60 61                                                                  63x 63x 62x   1x 1x                           18x 18x              
import type ICodeGenSymbols from "../types/ICodeGenSymbols";
import ScopeUtils from "./ScopeUtils";
 
/**
 * What a struct's declared FIELDS say, from a per-file symbol view.
 *
 * ## Why this is not three lines at each caller (#1456)
 *
 * The lookup is not a lookup. `keyFor` falls back from a dotted source name
 * (`Scope.Thing`) to the transpiled C name (`Scope__Thing`), because a
 * scope-declared struct is recorded under the latter and named by the former.
 * That fallback is a DECISION, and it had one implementation reached only
 * through `CodeGenState` -- so an analyzer that wanted it had to read render
 * state to get it.
 *
 * Both callers share this rather than each keeping a copy: `CodeGenState`
 * delegates for codegen, and 2.1 Analyze calls it with the view its
 * `IAnalysisContext` carries. A change to how a scoped struct is keyed reaches
 * both, which is the property CLAUDE.md asks for -- the unit is the decision,
 * not the data.
 */
class StructFieldFacts {
  /**
   * The key `structFields` records this struct under, or undefined.
   *
   * Tries the name as written first, then the transpiled C name. Never spells
   * the latter by hand: `getTranspiledCName` is the single encoder, and the
   * whole PATH is the scope rather than just its last segment.
   */
  static keyFor(
    symbols: ICodeGenSymbols | null,
    structName: string,
  ): string | undefined {
    const fields = symbols?.structFields;
    if (fields === undefined) return undefined;
    if (fields.has(structName)) return structName;
 
    const cut = structName.lastIndexOf(".");
    Eif (cut === -1) return undefined;
    const key = ScopeUtils.getTranspiledCName({
      scopePath: structName.slice(0, cut),
      name: structName.slice(cut + 1),
    });
    return fields.has(key) ? key : undefined;
  }
 
  /** The declared type of one field, or undefined. */
  static typeOf(
    symbols: ICodeGenSymbols | null,
    structName: string,
    fieldName: string,
  ): string | undefined {
    const key = StructFieldFacts.keyFor(symbols, structName);
    return key === undefined
      ? undefined
      : symbols?.structFields.get(key)?.get(fieldName);
  }
}
 
export default StructFieldFacts;