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

94.44% Statements 17/18
85% Branches 17/20
100% Functions 4/4
94.44% Lines 17/18

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                                                32x 7x     25x   32x                           20x       20x             32x         32x   32x 7x     25x 7x               18x       18x   9x 9x       9x          
/**
 * ADR-029: the single formatter for a callback typedef's parameter list.
 *
 * The `.c` and the `.h` each used to build this string themselves, and they
 * disagreed: the header dropped `const` and array dimensions, so
 * `void (*onReceive_fp)(const Message*)` in the implementation met
 * `void (*onReceive_fp)(Message*)` in the header. Nothing caught it while the
 * `.c` did not include its own header; once it does (#1164), the two meet in
 * one translation unit and the compiler rejects the redeclaration.
 *
 * Both paths now format from here, so there is one answer to "what does this
 * typedef look like?" rather than two that happened to agree for simple cases.
 */
 
import type ICallbackTypedefParameter from "../../../../types/ICallbackTypedefParameter";
 
class CallbackTypedefFormatter {
  /**
   * Format the parameter list between the parentheses of a callback typedef.
   */
  static formatParameterList(
    parameters: readonly ICallbackTypedefParameter[],
    isCppMode: boolean,
  ): string {
    if (parameters.length === 0) {
      return "void";
    }
 
    return parameters
      .map((parameter) =>
        CallbackTypedefFormatter.formatParameter(parameter, isCppMode),
      )
      .join(", ");
  }
 
  /**
   * Format a complete callback typedef declaration.
   */
  static format(
    returnType: string,
    typedefName: string,
    parameters: readonly ICallbackTypedefParameter[],
    isCppMode: boolean,
  ): string {
    const parameterList = CallbackTypedefFormatter.formatParameterList(
      parameters,
      isCppMode,
    );
    return `typedef ${returnType} (*${typedefName})(${parameterList});`;
  }
 
  private static formatParameter(
    parameter: ICallbackTypedefParameter,
    isCppMode: boolean,
  ): string {
    const constModifier = parameter.isConst ? "const " : "";
 
    // ADR-030: an opaque handle is a pointer in every mode, exactly as
    // `ParameterSignatureBuilder` spells the prototype -- and for an array,
    // each element is (#996).
    const handleSuffix = parameter.isOpaqueHandle ? "*" : "";
 
    if (parameter.isArray) {
      return `${constModifier}${parameter.type}${handleSuffix} ${parameter.name ?? ""}${parameter.arrayDims ?? ""}`;
    }
 
    if (parameter.isOpaqueHandle) {
      return `${constModifier}${parameter.type}${handleSuffix}`;
    }
 
    // ADR-045: `string<N>` is `const char*` at a parameter position -- the same
    // shape `ParameterSignatureBuilder._buildStringParam` gives the prototype.
    // Checked BEFORE the plain branch below, which returns the type alone and
    // would drop the `const`, leaving the typedef incompatible with the very
    // function it was derived from.
    Iif (parameter.isString) {
      return `${constModifier}${parameter.type}`;
    }
 
    if (parameter.isStruct) {
      // ADR-006: struct parameters become pointers in C, references in C++.
      const pointerOrReference = isCppMode ? "&" : "*";
      return `${constModifier}${parameter.type}${pointerOrReference}`;
    }
 
    // ADR-029: callback parameters are already function pointers.
    return parameter.type;
  }
}
 
export default CallbackTypedefFormatter;