All files / transpiler/logic/symbols AutoConstUpdater.ts

100% Statements 14/14
100% Branches 20/20
100% Functions 2/2
100% Lines 13/13

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                                                                      146x 352x 198x     154x 154x     153x 51x     28x                                               59x 13x         46x               59x   59x          
/**
 * AutoConstUpdater
 * Issue #588: Extracted from Transpiler to logic layer
 *
 * Updates symbol parameters with auto-const information based on code generation
 * analysis. Parameters that are not modified in function bodies can be marked
 * as auto-const, enabling correct const qualifier generation in headers.
 */
 
import ISymbol from "../../../utils/types/ISymbol";
import ESymbolKind from "../../../utils/types/ESymbolKind";
 
/**
 * Utility class for updating symbol parameters with auto-const information.
 *
 * In C-Next, struct parameters are passed by pointer by default. If a parameter
 * is never modified, it can be marked as `const` in the generated C code.
 * This class encapsulates the logic to determine which parameters qualify.
 */
class AutoConstUpdater {
  /**
   * Update symbols with auto-const information.
   *
   * For each function symbol, checks if its parameters were unmodified during
   * code generation and marks them as auto-const if they meet the criteria.
   *
   * @param symbols - Array of symbols to update (typically from a single file)
   * @param unmodifiedParams - Map of function name to set of unmodified param names
   * @param knownEnums - Set of known enum type names (these don't get pointer semantics)
   */
  static update(
    symbols: ISymbol[],
    unmodifiedParams: ReadonlyMap<string, ReadonlySet<string>>,
    knownEnums: ReadonlySet<string>,
  ): void {
    for (const symbol of symbols) {
      if (symbol.kind !== ESymbolKind.Function || !symbol.parameters) {
        continue;
      }
 
      const unmodified = unmodifiedParams.get(symbol.name);
      if (!unmodified) continue;
 
      // Update each parameter's isAutoConst
      for (const param of symbol.parameters) {
        if (
          AutoConstUpdater.shouldMarkAutoConst(param, unmodified, knownEnums)
        ) {
          param.isAutoConst = true;
        }
      }
    }
  }
 
  /**
   * Determine if a parameter should be marked as auto-const.
   *
   * A parameter qualifies for auto-const if:
   * 1. It was not modified in the function body
   * 2. It would get pointer semantics in generated C (not a primitive/enum)
   *
   * @param param - The parameter to check
   * @param unmodified - Set of unmodified parameter names for this function
   * @param knownEnums - Set of known enum type names
   * @returns true if the parameter should be marked as auto-const
   */
  static shouldMarkAutoConst(
    param: NonNullable<ISymbol["parameters"]>[number],
    unmodified: ReadonlySet<string>,
    knownEnums: ReadonlySet<string>,
  ): boolean {
    // Parameter must be unmodified
    if (!unmodified.has(param.name)) {
      return false;
    }
 
    // Check if parameter would get pointer semantics
    const isPointerParam =
      !param.isConst &&
      !param.isArray &&
      param.type !== "f32" &&
      param.type !== "f64" &&
      param.type !== "ISR" &&
      !knownEnums.has(param.type);
 
    // Array params also become pointers in C
    const isArrayParam = param.isArray && !param.isConst;
 
    return isPointerParam || isArrayParam;
  }
}
 
export default AutoConstUpdater;