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;
|