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

100% Statements 11/11
100% Branches 22/22
100% Functions 3/3
100% Lines 11/11

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                                                                                                              810x 810x 810x 810x     810x     810x 4x 4x             806x                                 22x                         767x          
/**
 * VariableModifierBuilder - Extracts and validates variable modifiers
 *
 * Issue #696: Extracted from CodeGenerator to reduce cognitive complexity
 * and eliminate duplication across generateVariableDecl, generateParameter,
 * and ControlFlowGenerator.generateForVarDecl.
 *
 * Handles:
 * - const, atomic, volatile, extern modifiers
 * - Validation that atomic and volatile are not both specified
 */
 
/**
 * Result from building variable modifiers.
 */
interface IVariableModifiers {
  /** "const " or "" */
  const: string;
  /** "volatile " for atomic modifier or "" */
  atomic: string;
  /** "volatile " for volatile modifier or "" */
  volatile: string;
  /** "extern " for top-level const in C++ or "" */
  extern: string;
}
 
/**
 * Context interface for variable declarations that have modifiers.
 * This allows the builder to work with different parser contexts.
 * Uses unknown since we only check truthiness of modifier methods.
 * constModifier is optional because ForVarDeclContext doesn't have it.
 */
interface IModifierContext {
  constModifier?: () => unknown;
  atomicModifier(): unknown;
  volatileModifier(): unknown;
  start?: { line?: number } | null;
}
 
/**
 * Builds and validates variable modifiers from parser context.
 */
class VariableModifierBuilder {
  /**
   * Build modifiers for a variable declaration.
   *
   * @param ctx - Parser context with modifier methods
   * @param inFunctionBody - Whether we're inside a function body (affects extern)
   * @returns Modifier strings ready for use in generated code
   * @throws Error if both atomic and volatile are specified
   */
  static build(
    ctx: IModifierContext,
    inFunctionBody: boolean,
  ): IVariableModifiers {
    const hasConst = ctx.constModifier?.() ?? false;
    const constMod = hasConst ? "const " : "";
    const atomicMod = ctx.atomicModifier() ? "volatile " : "";
    const volatileMod = ctx.volatileModifier() ? "volatile " : "";
 
    // Issue #525: Add extern for top-level const in C++ for external linkage
    const externMod = hasConst && !inFunctionBody ? "extern " : "";
 
    // Validate: cannot use both atomic and volatile
    if (ctx.atomicModifier() && ctx.volatileModifier()) {
      const line = ctx.start?.line ?? 0;
      throw new Error(
        `Error at line ${line}: Cannot use both 'atomic' and 'volatile' modifiers. ` +
          `Use 'atomic' for ISR-shared variables (includes volatile + atomicity), ` +
          `or 'volatile' for hardware registers and delay loops.`,
      );
    }
 
    return {
      const: constMod,
      atomic: atomicMod,
      volatile: volatileMod,
      extern: externMod,
    };
  }
 
  /**
   * Build simple modifiers (atomic and volatile only) for contexts like for-loop vars.
   *
   * @param ctx - Parser context with modifier methods
   * @returns Modifier strings (just atomic and volatile)
   */
  static buildSimple(
    ctx: IModifierContext,
  ): Pick<IVariableModifiers, "atomic" | "volatile"> {
    return {
      atomic: ctx.atomicModifier() ? "volatile " : "",
      volatile: ctx.volatileModifier() ? "volatile " : "",
    };
  }
 
  /**
   * Build the combined modifier prefix string.
   *
   * @param modifiers - The modifier object
   * @returns Combined string like "extern const volatile "
   */
  static toPrefix(modifiers: IVariableModifiers): string {
    return `${modifiers.extern}${modifiers.const}${modifiers.atomic}${modifiers.volatile}`;
  }
}
 
export default VariableModifierBuilder;