All files / TRANSPILE/3-Render/codegen/helpers StructInitFunction.ts

100% Statements 12/12
100% Branches 2/2
100% Functions 5/5
100% Lines 12/12

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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                      53x             25x                 23x                                                             14x         14x 19x     14x   14x                     438x 434x     4x   7x            
/**
 * StructInitFunction - the ADR-029 generated struct initializer.
 *
 * A struct with callback fields gets a generated `<Struct>_init(void)` that
 * returns the struct with every callback set to its default function. That is
 * a definition with external linkage, so MISRA C:2012 Rule 8.4 requires a
 * compatible declaration to be visible: the header declares it, the `.c`
 * defines it, and the `.c` includes that header (#1205).
 *
 * Both spellings live here. The declaration must not be produced by asking the
 * header's own data whether a struct has a callback field: the `.c` walks the
 * parse tree and only top-level structs reach this generator, while the
 * header's `structFields` map also holds scope-nested structs, which get no
 * init function at all (#1283). Re-deriving the predicate on the header side
 * would declare `Scope__Nested_init` for a function nobody defines. The
 * existence decision is therefore recorded where it is made and read
 * everywhere else; only the spelling lives in this module.
 */
import ComplianceAnnotations from "../../../2-Plan/ComplianceAnnotations";
import IStructFieldInit from "../types/IStructFieldInit";
 
/**
 * Compliance annotation for the emitted declarations (C-Next standard: codegen
 * whose shape is dictated by a safety standard says which rule shaped it).
 * One line above the block -- the declarations are contiguous and share a
 * single reason, so repeating it per prototype would add noise, not tracing.
 */
const RULE_8_4_ANNOTATION = ComplianceAnnotations.render(
  ComplianceAnnotations.INIT_PROTOTYPE,
);
 
class StructInitFunction {
  /** Generated C name, e.g. `Controller` -> `Controller_init`. */
  static cName(structName: string): string {
    return `${structName}_init`;
  }
 
  /**
   * The one signature spelling, without a trailing `;` or ` {`.
   * The definition and the prototype are both built from this, so they cannot
   * drift in return type, name or parameter list.
   */
  static signature(structName: string): string {
    return `${structName} ${StructInitFunction.cName(structName)}(void)`;
  }
 
  /**
   * The `.c` definition: zero the whole struct, then assign only the fields
   * whose correct value is not zero.
   *
   * #1568: this was a compound literal naming each field with that type's zero
   * initializer, and the zero came from the helper that answers for a
   * *declaration* position. A designated initializer is a stricter position in
   * both directions -- `.data = 0` for an array is
   * `-Wmissing-braces`, and `.ticks = {0}` for a scalar typedef from a C header
   * is `braces around scalar initializer`. Neither shape exists in the corpus,
   * so both compiled green.
   *
   * Zeroing the aggregate once removes the question instead of answering it per
   * field: arrays, foreign typedefs and nested structs are all covered by the
   * one brace, and no array-ness has to be re-derived here. That matters beyond
   * the bug -- the field declaration reads array-ness from three sources, so a
   * per-field initializer would have had to re-derive all three and drift from
   * them.
   *
   * @param structName - The struct being initialized
   * @param zeroBrace - Aggregate zero for the current mode, from the orchestrator
   * @param assignments - Fields whose value is not zero, in declaration order
   */
  static definition(
    structName: string,
    zeroBrace: string,
    assignments: readonly IStructFieldInit[],
  ): string {
    const lines: string[] = [
      `${StructInitFunction.signature(structName)} {`,
      `    ${structName} value = ${zeroBrace};`,
    ];
 
    for (const field of assignments) {
      lines.push(`    value.${field.fieldName} = ${field.initializer};`);
    }
 
    lines.push(`    return value;`, `}`, "");
 
    return lines.join("\n");
  }
 
  /**
   * The header declarations, annotated as a block.
   *
   * Takes the struct names whose init functions the `.c` actually emitted --
   * not a predicate over the header's own field data -- so a declaration can
   * never outlive its definition.
   */
  static prototypeLines(structNames: readonly string[]): string[] {
    if (structNames.length === 0) {
      return [];
    }
 
    return [
      RULE_8_4_ANNOTATION,
      ...structNames.map((name) => `${StructInitFunction.signature(name)};`),
    ];
  }
}
 
export default StructInitFunction;