All files / transpiler/output/codegen/helpers ParameterSignatureBuilder.ts

100% Statements 31/31
100% Branches 32/32
100% Functions 8/8
100% Lines 30/30

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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                                                    351x 4x       347x         47x       300x 174x       126x 8x       118x 115x       3x                       47x 58x     47x 3x       44x 4x     40x                 174x 174x               8x 8x                             115x   115x 115x             3x 3x                   170x 85x   85x          
/**
 * ParameterSignatureBuilder - Stateless builder for C/C++ parameter signatures
 *
 * Takes a normalized IParameterInput and produces the final parameter string.
 * All decisions (const, pass-by-value, etc.) are pre-computed in the input.
 *
 * This is the single source of truth for parameter formatting, used by both:
 * - CodeGenerator (via ParameterInputAdapter.fromAST)
 * - BaseHeaderGenerator (via ParameterInputAdapter.fromSymbol)
 */
 
import IParameterInput from "../types/IParameterInput";
 
/**
 * Static helper class for building C/C++ parameter signature strings.
 */
class ParameterSignatureBuilder {
  /**
   * Build a parameter signature string from normalized input.
   *
   * @param param - Normalized parameter input (all decisions pre-computed)
   * @param refSuffix - '*' for C mode (pointer), '&' for C++ mode (reference)
   * @returns The formatted parameter string (e.g., "const uint32_t* value")
   */
  static build(param: IParameterInput, refSuffix: string): string {
    // Callback parameters: just typedef name and param name
    if (param.isCallback && param.callbackTypedefName) {
      return `${param.callbackTypedefName} ${param.name}`;
    }
 
    // Array parameters with dimensions
    if (
      param.isArray &&
      param.arrayDimensions &&
      param.arrayDimensions.length > 0
    ) {
      return this._buildArrayParam(param);
    }
 
    // Pass-by-value parameters (ISR, float, enum, small primitives)
    if (param.isPassByValue) {
      return this._buildPassByValueParam(param);
    }
 
    // Non-array string: string<N> -> const char* name
    if (param.isString && !param.isArray) {
      return this._buildStringParam(param);
    }
 
    // Known struct or known primitive: pass by reference
    if (param.isPassByReference) {
      return this._buildRefParam(param, refSuffix);
    }
 
    // Unknown types: pass by value (standard C semantics)
    return this._buildUnknownParam(param);
  }
 
  /**
   * Build array parameter signature.
   * Examples:
   * - u32[10] arr -> const uint32_t arr[10]
   * - u8[4][4] matrix -> const uint8_t matrix[4][4]
   * - string<32>[5] names -> const char names[5][33]
   * - string[5] names -> char* names[5] (unbounded string array)
   */
  private static _buildArrayParam(param: IParameterInput): string {
    const constPrefix = this._getConstPrefix(param);
    const dims = param.arrayDimensions!.map((d) => `[${d}]`).join("");
 
    // Unbounded string arrays use char* (array of char pointers)
    if (param.isUnboundedString) {
      return `${constPrefix}char* ${param.name}${dims}`;
    }
 
    // Bounded string arrays use char (dimensions include capacity)
    if (param.isString) {
      return `${constPrefix}char ${param.name}${dims}`;
    }
 
    return `${constPrefix}${param.mappedType} ${param.name}${dims}`;
  }
 
  /**
   * Build pass-by-value parameter signature.
   * Used for: ISR, f32, f64, enums, small unmodified primitives.
   * Example: float value, ISR handler, Status s
   */
  private static _buildPassByValueParam(param: IParameterInput): string {
    const constMod = param.isConst ? "const " : "";
    return `${constMod}${param.mappedType} ${param.name}`;
  }
 
  /**
   * Build non-array string parameter signature.
   * string<N> -> const char* name (with auto-const if unmodified)
   */
  private static _buildStringParam(param: IParameterInput): string {
    const constPrefix = this._getConstPrefix(param);
    return `${constPrefix}char* ${param.name}`;
  }
 
  /**
   * Build pass-by-reference parameter signature.
   * C mode: const Point* p
   * C++ mode: const Point& p (unless forcePointerSyntax)
   *
   * Issue #895: When forcePointerSyntax is set, always use pointer syntax
   * because C callback typedefs expect pointers, not C++ references.
   */
  private static _buildRefParam(
    param: IParameterInput,
    refSuffix: string,
  ): string {
    const constPrefix = this._getConstPrefix(param);
    // Issue #895: Override refSuffix for callback-compatible functions
    const actualSuffix = param.forcePointerSyntax ? "*" : refSuffix;
    return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
  }
 
  /**
   * Build unknown type parameter (pass by value, standard C semantics).
   */
  private static _buildUnknownParam(param: IParameterInput): string {
    const constMod = param.isConst ? "const " : "";
    return `${constMod}${param.mappedType} ${param.name}`;
  }
 
  /**
   * Get const prefix combining explicit const, auto-const, and forced const.
   * Priority: forceConst > isConst > isAutoConst
   * Issue #895: forceConst preserves const from callback typedef signature.
   */
  private static _getConstPrefix(param: IParameterInput): string {
    // Any source of const results in "const " prefix
    if (param.forceConst || param.isConst || param.isAutoConst) {
      return "const ";
    }
    return "";
  }
}
 
export default ParameterSignatureBuilder;