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

100% Statements 13/13
100% Branches 6/6
100% Functions 4/4
100% Lines 12/12

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                                              10x 3x   7x               8x 8x                     7x                           7x   5x 5x 5x         5x 5x          
/**
 * CppConstructorHelper
 *
 * Helper class for C++ constructor detection logic.
 * Issue #517: C++ classes with user-defined constructors are NOT aggregate types,
 * so designated initializers { .field = value } don't work with them.
 */
 
import ESymbolKind from "../../../../utils/types/ESymbolKind.js";
 
/**
 * Symbol lookup interface for constructor detection
 */
interface ISymbolLookup {
  getSymbol(name: string): { kind: ESymbolKind } | undefined;
}
 
class CppConstructorHelper {
  /**
   * Convert underscore format to :: for namespaced types.
   * e.g., TestNS_MyClass -> TestNS::MyClass
   */
  static toQualifiedName(typeName: string): string {
    if (typeName.includes("_") && !typeName.includes("::")) {
      return typeName.replaceAll("_", "::");
    }
    return typeName;
  }
 
  /**
   * Extract just the class name (part after last ::).
   * e.g., TestNS::MyClass -> MyClass, CppTestClass -> CppTestClass
   */
  static extractClassName(qualifiedName: string): string {
    const parts = qualifiedName.split("::");
    return parts.at(-1)!;
  }
 
  /**
   * Build the constructor name pattern.
   * e.g., TestNS::MyClass -> TestNS::MyClass::MyClass
   */
  static buildConstructorName(
    qualifiedName: string,
    className: string,
  ): string {
    return `${qualifiedName}::${className}`;
  }
 
  /**
   * Check if a type has a C++ constructor in the symbol table.
   *
   * @param typeName - The type name (may use underscore or :: notation)
   * @param symbolTable - Symbol table for lookup (may be null)
   * @returns true if a constructor function symbol exists
   */
  static hasConstructor(
    typeName: string,
    symbolTable: ISymbolLookup | null | undefined,
  ): boolean {
    if (!symbolTable) return false;
 
    const qualifiedName = CppConstructorHelper.toQualifiedName(typeName);
    const className = CppConstructorHelper.extractClassName(qualifiedName);
    const constructorName = CppConstructorHelper.buildConstructorName(
      qualifiedName,
      className,
    );
 
    const constructorSymbol = symbolTable.getSymbol(constructorName);
    return constructorSymbol?.kind === ESymbolKind.Function;
  }
}
 
export default CppConstructorHelper;