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

94.11% Statements 32/34
90% Branches 27/30
100% Functions 4/4
93.93% Lines 31/33

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                                                                                                        850x                   306x 306x     306x 206x       100x 100x           100x 59x         41x             206x 206x 33x     173x                                   59x       59x 59x   59x 19x     40x     40x 47x 47x   47x 2x     45x   45x   38x 7x   7x             2x          
/**
 * AssignmentExpectedTypeResolver - Resolves expected type context for assignment targets
 *
 * Issue #644: Extracted from CodeGenerator.generateAssignment() to reduce cognitive complexity.
 *
 * Sets up expectedType and assignmentContext for expression generation,
 * enabling type-aware resolution of unqualified enum members and overflow behavior.
 */
 
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
import TTypeInfo from "../types/TTypeInfo.js";
import TOverflowBehavior from "../types/TOverflowBehavior.js";
import analyzePostfixOps from "../../../../utils/PostfixAnalysisUtils.js";
 
/**
 * Result of resolving expected type for an assignment target.
 */
interface IExpectedTypeResult {
  /** The resolved expected type (e.g., "u32", "Status"), or null if not resolved */
  expectedType: string | null;
  /** Assignment context for overflow behavior tracking */
  assignmentContext: IAssignmentContext | null;
}
 
/**
 * Assignment context for overflow behavior tracking (ADR-044).
 */
interface IAssignmentContext {
  targetName: string;
  targetType: string;
  overflowBehavior: TOverflowBehavior;
}
 
/**
 * Dependencies required for expected type resolution.
 */
interface IExpectedTypeResolverDeps {
  /** Type registry for looking up variable types */
  readonly typeRegistry: ReadonlyMap<string, TTypeInfo>;
  /** Struct field types: structName -> (fieldName -> fieldType) */
  readonly structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
  /** Check if a type is a known struct */
  isKnownStruct: (typeName: string) => boolean;
}
 
/**
 * Resolves expected type for assignment targets.
 */
class AssignmentExpectedTypeResolver {
  private readonly deps: IExpectedTypeResolverDeps;
 
  constructor(deps: IExpectedTypeResolverDeps) {
    this.deps = deps;
  }
 
  /**
   * Resolve expected type for an assignment target.
   *
   * @param targetCtx - The assignment target context
   * @returns The resolved expected type and assignment context
   */
  resolve(targetCtx: Parser.AssignmentTargetContext): IExpectedTypeResult {
    const postfixOps = targetCtx.postfixTargetOp();
    const baseId = targetCtx.IDENTIFIER()?.getText();
 
    // Case 1: Simple identifier (x <- value) - no postfix ops
    if (baseId && postfixOps.length === 0) {
      return this.resolveForSimpleIdentifier(baseId);
    }
 
    // Case 2: Has member access - extract identifiers from postfix chain
    Eif (baseId && postfixOps.length > 0) {
      const { identifiers, hasSubscript } = analyzePostfixOps(
        baseId,
        postfixOps,
      );
 
      // If we have member access (multiple identifiers), resolve for member chain
      if (identifiers.length >= 2 && !hasSubscript) {
        return this.resolveForMemberChain(identifiers);
      }
    }
 
    // Case 3: Array access or complex patterns - no expected type resolution
    return { expectedType: null, assignmentContext: null };
  }
 
  /**
   * Resolve expected type for a simple identifier target.
   */
  private resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
    const typeInfo = this.deps.typeRegistry.get(id);
    if (!typeInfo) {
      return { expectedType: null, assignmentContext: null };
    }
 
    return {
      expectedType: typeInfo.baseType,
      assignmentContext: {
        targetName: id,
        targetType: typeInfo.baseType,
        overflowBehavior: typeInfo.overflowBehavior || "clamp",
      },
    };
  }
 
  /**
   * Resolve expected type for a member access chain.
   * Walks the chain of struct types to find the final field's type.
   *
   * Issue #452: Enables type-aware resolution of unqualified enum members
   * for nested access (e.g., config.nested.field).
   */
  private resolveForMemberChain(identifiers: string[]): IExpectedTypeResult {
    Iif (identifiers.length < 2) {
      return { expectedType: null, assignmentContext: null };
    }
 
    const rootName = identifiers[0];
    const rootTypeInfo = this.deps.typeRegistry.get(rootName);
 
    if (!rootTypeInfo || !this.deps.isKnownStruct(rootTypeInfo.baseType)) {
      return { expectedType: null, assignmentContext: null };
    }
 
    let currentStructType: string | undefined = rootTypeInfo.baseType;
 
    // Walk through each member in the chain to find the final field's type
    for (let i = 1; i < identifiers.length && currentStructType; i++) {
      const memberName = identifiers[i];
      const structFieldTypes = this.deps.structFields.get(currentStructType);
 
      if (!structFieldTypes?.has(memberName)) {
        break;
      }
 
      const memberType = structFieldTypes.get(memberName)!;
 
      if (i === identifiers.length - 1) {
        // Last field in chain - this is the assignment target's type
        return { expectedType: memberType, assignmentContext: null };
      } else if (this.deps.isKnownStruct(memberType)) {
        // Intermediate field - continue walking if it's a struct
        currentStructType = memberType;
      } else E{
        // Intermediate field is not a struct - can't walk further
        break;
      }
    }
 
    return { expectedType: null, assignmentContext: null };
  }
}
 
export default AssignmentExpectedTypeResolver;