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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | 665x 606x 59x 59x 1x 58x 1x 57x 10x 10x 138x 138x 24x 114x 45x 45x 24x 90x 10x 10x 2x 2x 8x 3x 3x 5x 1x 4x 2x 2x 1x 1x 2x 1x 2x 2x 2x 1x 3x 3x 1x 2x 1x 2x | /**
* EnumAssignmentValidator - Validates enum type assignments
*
* Issue #644: Extracted from CodeGenerator to reduce file size.
* Static class using CodeGenState for all state access.
*
* Validates that enum assignments are type-safe:
* - Cannot assign different enum types to each other
* - Cannot assign integers to enums
* - Handles this.Enum.MEMBER and global.Enum.MEMBER patterns
* - Handles scoped enum patterns (Scope.Enum.MEMBER)
*/
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
import CodeGenState from "../../../state/CodeGenState.js";
import EnumTypeResolver from "../resolution/EnumTypeResolver.js";
import TypeCheckUtils from "../../../../utils/TypeCheckUtils.js";
/**
* Validates enum type assignments.
* All methods are static - uses CodeGenState for state access.
*/
class EnumAssignmentValidator {
/**
* Validate that an expression can be assigned to an enum-typed variable.
* Throws an error if the assignment is invalid.
*
* @param typeName - The target enum type name
* @param expression - The expression being assigned
*/
static validateEnumAssignment(
typeName: string,
expression: Parser.ExpressionContext,
): void {
// Only validate if the target type is a known enum
if (!CodeGenState.isKnownEnum(typeName)) {
return;
}
const valueEnumType = EnumTypeResolver.resolve(expression);
// Check if assigning from a different enum type
if (valueEnumType && valueEnumType !== typeName) {
throw new Error(
`Error: Cannot assign ${valueEnumType} enum to ${typeName} enum`,
);
}
// Check if assigning integer to enum
if (EnumAssignmentValidator.isIntegerExpression(expression)) {
throw new Error(`Error: Cannot assign integer to ${typeName} enum`);
}
// Check if assigning a non-enum, non-integer expression
if (!valueEnumType) {
const exprText = expression.getText();
EnumAssignmentValidator.validateNonEnumExpression(exprText, typeName);
}
}
/**
* Check if an expression is an integer literal or integer-typed variable.
* Used to detect comparisons between enums and integers.
*/
static isIntegerExpression(
ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
): boolean {
const text = ctx.getText();
// Check for integer literals
if (
/^-?\d+$/.exec(text) ||
/^0[xX][0-9a-fA-F]+$/.exec(text) ||
/^0[bB][01]+$/.exec(text)
) {
return true;
}
// Check if it's a variable of primitive integer type
if (/^[a-zA-Z_]\w*$/.exec(text)) {
const typeInfo = CodeGenState.getVariableTypeInfo(text);
if (
typeInfo &&
!typeInfo.isEnum &&
TypeCheckUtils.isInteger(typeInfo.baseType)
) {
return true;
}
}
return false;
}
/**
* Validate a non-enum expression being assigned to an enum type.
* Handles various access patterns like this.Enum.MEMBER, global.Enum.MEMBER, etc.
*/
private static validateNonEnumExpression(
exprText: string,
typeName: string,
): void {
const parts = exprText.split(".");
// ADR-016: Handle this.State.MEMBER pattern
if (parts[0] === "this" && parts.length >= 3) {
EnumAssignmentValidator.validateThisEnumPattern(parts, typeName);
return;
}
// Issue #478: Handle global.Enum.MEMBER or global.struct.field pattern
if (parts[0] === "global" && parts.length >= 3) {
EnumAssignmentValidator.validateGlobalEnumPattern(parts, typeName);
return;
}
// Allow if it's an enum member access of the correct type
if (exprText.startsWith(typeName + ".")) {
return;
}
// Check for scoped enum
if (parts.length >= 3) {
const scopedEnumName = `${parts[0]}_${parts[1]}`;
if (scopedEnumName !== typeName) {
throw new Error(
`Error: Cannot assign non-enum value to ${typeName} enum`,
);
}
return;
}
// parts.length === 2: Could be Enum.Member or variable.field
// Allow if it's a known enum type (even if different - resolve would catch mismatches)
if (
parts.length === 2 &&
!EnumAssignmentValidator.isMatchingEnum(parts[0], typeName)
) {
throw new Error(
`Error: Cannot assign non-enum value to ${typeName} enum`,
);
}
}
/**
* ADR-016: Validate this.Enum.MEMBER pattern inside a scope.
*/
private static validateThisEnumPattern(
parts: string[],
typeName: string,
): void {
Iif (!CodeGenState.currentScope) {
throw new Error(
`Error: Cannot assign non-enum value to ${typeName} enum`,
);
}
const scopedEnumName = `${CodeGenState.currentScope}_${parts[1]}`;
if (scopedEnumName !== typeName) {
throw new Error(
`Error: Cannot assign non-enum value to ${typeName} enum`,
);
}
}
/**
* Issue #478: Validate global.X.Y pattern.
* If X is a known enum, validates it matches the target type.
* If X is not an enum (e.g. struct variable), allows through since
* EnumTypeResolver.resolve() with TypeResolver fallback handles the chain.
*/
private static validateGlobalEnumPattern(
parts: string[],
typeName: string,
): void {
const name = parts[1];
// Not an enum (e.g. struct variable like global.input.field) — allow through
// since EnumTypeResolver.resolve() with TypeResolver fallback handles the chain
if (!CodeGenState.isKnownEnum(name)) {
return;
}
// Known enum that doesn't match target type
if (name !== typeName) {
throw new Error(
`Error: Cannot assign non-enum value to ${typeName} enum`,
);
}
}
/**
* Check if an identifier is the target enum type or a known enum type.
*/
private static isMatchingEnum(identifier: string, typeName: string): boolean {
return identifier === typeName || CodeGenState.isKnownEnum(identifier);
}
}
export default EnumAssignmentValidator;
|