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

88.46% Statements 23/26
65% Branches 13/20
100% Functions 7/7
88.46% Lines 23/26

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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175                                      56x                         56x         56x                                                                           44x 6x       38x 10x       28x 44x                           67x 16x   51x                   1841x                   1839x                     917x 917x 917x 917x     917x             3x 3x                                       3x 3x          
/**
 * NarrowingCastHelper - MISRA C:2012 Rule 10.3 compliance
 *
 * Issue #845: Wraps expressions with explicit casts when assigning
 * to narrower types or different essential type categories.
 *
 * C's integer promotion rules mean bit operations on u8/u16 produce int,
 * which MISRA flags when assigned back to narrower types without explicit cast.
 */
 
import type TranspileState from "../../../TranspileState";
import CppModeHelper from "./CppModeHelper";
import TYPE_MAP from "../types/TYPE_MAP";
import CastRequirement from "../../../2-Plan/CastRequirement";
 
/**
 * Types that get promoted to int in C's integer promotion rules.
 * In C, operations on types smaller than int get promoted to int.
 */
const PROMOTED_TO_INT = new Set(["u8", "i8", "u16", "i16", "bool"]);
 
/**
 * Float types for cross-category detection -- C-Next's `f32`/`f64` AND the C
 * spellings a header brings in, because MISRA 10.3's essential type categories
 * do not stop at the interop boundary.
 *
 * #1450: deliberately WIDER than `types/FLOAT_TYPES`, which is C-Next only and
 * is what `TypeResolver.isFloatType` answers from. Two different questions; the
 * predicates below used to share `TypeResolver`'s names, so picking the wrong
 * one silently answered the other question. `parseArrayTypeDimension` is what
 * that costs when it happens -- see `issue-1450-hex-array-dimension-fill`.
 */
const FLOAT_TYPES = new Set(["f32", "f64", "float", "double"]);
 
/**
 * Integer types for cross-category detection.
 */
const INTEGER_TYPES = new Set([
  "u8",
  "u16",
  "u32",
  "u64",
  "i8",
  "i16",
  "i32",
  "i64",
  "uint8_t",
  "uint16_t",
  "uint32_t",
  "uint64_t",
  "int8_t",
  "int16_t",
  "int32_t",
  "int64_t",
  "int",
]);
 
/**
 * Helper for adding MISRA 10.3 compliant casts to generated C code.
 */
class NarrowingCastHelper {
  /**
   * Wrap expression with cast if needed for MISRA 10.3 compliance.
   *
   * @param expr - The generated C expression
   * @param sourceType - Type of the expression (C-Next type or "int")
   * @param targetType - Type of the assignment target (C-Next type)
   * @returns Expression with cast wrapper if needed, or original expression
   */
  static wrap(
    expr: string,
    sourceType: string,
    targetType: string,
    state: TranspileState,
  ): string {
    if (!CastRequirement.forConversion(sourceType, targetType)) {
      return expr;
    }
 
    // Bool target: use comparison instead of cast (MISRA 10.5)
    if (targetType === "bool") {
      return `((${expr}) != 0U)`;
    }
 
    // Get C type name for the target
    const cType = TYPE_MAP[targetType] ?? targetType;
    return CppModeHelper.cast(cType, expr, state);
  }
 
  /**
   * Determine the result type of C integer promotion for a given type.
   *
   * In C, operations on types smaller than int are promoted:
   * - u8, i8, u16, i16, bool -> int (32-bit)
   * - u32, i32, u64, i64 -> no promotion (already >= int width)
   *
   * @param baseType - The C-Next type of the operand
   * @returns "int" for promoted types, or the original type
   */
  static getPromotedType(baseType: string): string {
    if (PROMOTED_TO_INT.has(baseType)) {
      return "int";
    }
    return baseType;
  }
 
  /**
   * Is this type in the FLOAT category, C spellings included?
   *
   * Not `TypeResolver.isFloatType`, which answers about C-Next types alone:
   * `double` is true here and false there, on purpose.
   */
  static isFloatCategory(typeName: string): boolean {
    return FLOAT_TYPES.has(typeName);
  }
 
  /**
   * Is this type in the INTEGER category, C spellings included?
   *
   * Not `TypeResolver.isIntegerType` -- `uint8_t` and `int` are true here and
   * false there, on purpose.
   */
  static isIntegerCategory(typeName: string): boolean {
    return INTEGER_TYPES.has(typeName);
  }
 
  /**
   * Check if conversion between source and target is a cross-type-category conversion.
   * MISRA 10.3 requires explicit casts for different essential type categories.
   */
  static isCrossTypeCategoryConversion(
    sourceType: string,
    targetType: string,
  ): boolean {
    const sourceIsFloat = NarrowingCastHelper.isFloatCategory(sourceType);
    const targetIsFloat = NarrowingCastHelper.isFloatCategory(targetType);
    const sourceIsInt = NarrowingCastHelper.isIntegerCategory(sourceType);
    const targetIsInt = NarrowingCastHelper.isIntegerCategory(targetType);
 
    // Float to integer or integer to float
    return (sourceIsFloat && targetIsInt) || (sourceIsInt && targetIsFloat);
  }
 
  /**
   * Get the appropriate C float type for a C-Next type.
   */
  static getCFloatType(typeName: string): string {
    Eif (typeName === "f32" || typeName === "float") {
      return "float";
    }
    if (typeName === "f64" || typeName === "double") {
      return "double";
    }
    return "double"; // Default to double
  }
 
  /**
   * Wrap int-to-float conversion with explicit cast for MISRA 10.3.
   *
   * @param expr - The integer expression
   * @param targetType - The float target type (f32/f64)
   * @returns Expression with cast
   */
  static wrapIntToFloat(
    expr: string,
    targetType: string,
    state: TranspileState,
  ): string {
    const floatType = NarrowingCastHelper.getCFloatType(targetType);
    return CppModeHelper.cast(floatType, expr, state);
  }
}
 
export default NarrowingCastHelper;