c-next 0.1.62 → 0.1.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +86 -63
  2. package/package.json +1 -1
  3. package/src/transpiler/Transpiler.ts +3 -2
  4. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
  6. package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
  7. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  8. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  9. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  10. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
  12. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  13. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  14. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
  16. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  20. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
  21. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
  22. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  24. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
  25. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
  26. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  27. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  28. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  29. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
  30. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  31. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  33. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  34. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
  36. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
  37. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  38. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  40. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  41. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  42. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  43. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  44. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  45. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  46. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  47. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  50. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  51. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  52. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  53. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  54. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  55. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
@@ -3,123 +3,150 @@
3
3
  * Issue #644: C/C++ mode pattern consolidation
4
4
  */
5
5
 
6
- import { describe, it, expect } from "vitest";
6
+ import { describe, it, expect, beforeEach } from "vitest";
7
7
  import CppModeHelper from "../CppModeHelper";
8
+ import CodeGenState from "../../CodeGenState";
8
9
 
9
10
  describe("CppModeHelper", () => {
11
+ beforeEach(() => {
12
+ CodeGenState.reset();
13
+ });
14
+
10
15
  describe("C mode (cppMode: false)", () => {
11
- const helper = new CppModeHelper({ cppMode: false });
16
+ beforeEach(() => {
17
+ CodeGenState.cppMode = false;
18
+ });
12
19
 
13
20
  it("maybeAddressOf adds & prefix", () => {
14
- expect(helper.maybeAddressOf("expr")).toBe("&expr");
15
- expect(helper.maybeAddressOf("foo.bar")).toBe("&foo.bar");
21
+ expect(CppModeHelper.maybeAddressOf("expr")).toBe("&expr");
22
+ expect(CppModeHelper.maybeAddressOf("foo.bar")).toBe("&foo.bar");
16
23
  });
17
24
 
18
25
  it("maybeDereference wraps in (*...)", () => {
19
- expect(helper.maybeDereference("ptr")).toBe("(*ptr)");
20
- expect(helper.maybeDereference("param")).toBe("(*param)");
26
+ expect(CppModeHelper.maybeDereference("ptr")).toBe("(*ptr)");
27
+ expect(CppModeHelper.maybeDereference("param")).toBe("(*param)");
21
28
  });
22
29
 
23
30
  it("refOrPtr returns *", () => {
24
- expect(helper.refOrPtr()).toBe("*");
31
+ expect(CppModeHelper.refOrPtr()).toBe("*");
25
32
  });
26
33
 
27
34
  it("memberSeparator returns ->", () => {
28
- expect(helper.memberSeparator()).toBe("->");
35
+ expect(CppModeHelper.memberSeparator()).toBe("->");
29
36
  });
30
37
 
31
38
  it("nullLiteral returns NULL", () => {
32
- expect(helper.nullLiteral()).toBe("NULL");
39
+ expect(CppModeHelper.nullLiteral()).toBe("NULL");
33
40
  });
34
41
 
35
42
  it("cast returns C-style cast", () => {
36
- expect(helper.cast("int", "x")).toBe("(int)x");
37
- expect(helper.cast("uint8_t", "value")).toBe("(uint8_t)value");
43
+ expect(CppModeHelper.cast("int", "x")).toBe("(int)x");
44
+ expect(CppModeHelper.cast("uint8_t", "value")).toBe("(uint8_t)value");
38
45
  });
39
46
 
40
47
  it("reinterpretCast returns C-style cast", () => {
41
- expect(helper.reinterpretCast("char*", "ptr")).toBe("(char*)ptr");
42
- expect(helper.reinterpretCast("uint8_t*", "buf")).toBe("(uint8_t*)buf");
48
+ expect(CppModeHelper.reinterpretCast("char*", "ptr")).toBe("(char*)ptr");
49
+ expect(CppModeHelper.reinterpretCast("uint8_t*", "buf")).toBe(
50
+ "(uint8_t*)buf",
51
+ );
43
52
  });
44
53
 
45
54
  it("isCppMode returns false", () => {
46
- expect(helper.isCppMode()).toBe(false);
55
+ expect(CppModeHelper.isCppMode()).toBe(false);
47
56
  });
48
57
  });
49
58
 
50
59
  describe("C++ mode (cppMode: true)", () => {
51
- const helper = new CppModeHelper({ cppMode: true });
60
+ beforeEach(() => {
61
+ CodeGenState.cppMode = true;
62
+ });
52
63
 
53
64
  it("maybeAddressOf returns expr unchanged", () => {
54
- expect(helper.maybeAddressOf("expr")).toBe("expr");
55
- expect(helper.maybeAddressOf("foo.bar")).toBe("foo.bar");
65
+ expect(CppModeHelper.maybeAddressOf("expr")).toBe("expr");
66
+ expect(CppModeHelper.maybeAddressOf("foo.bar")).toBe("foo.bar");
56
67
  });
57
68
 
58
69
  it("maybeDereference returns expr unchanged", () => {
59
- expect(helper.maybeDereference("ptr")).toBe("ptr");
60
- expect(helper.maybeDereference("param")).toBe("param");
70
+ expect(CppModeHelper.maybeDereference("ptr")).toBe("ptr");
71
+ expect(CppModeHelper.maybeDereference("param")).toBe("param");
61
72
  });
62
73
 
63
74
  it("refOrPtr returns &", () => {
64
- expect(helper.refOrPtr()).toBe("&");
75
+ expect(CppModeHelper.refOrPtr()).toBe("&");
65
76
  });
66
77
 
67
78
  it("memberSeparator returns .", () => {
68
- expect(helper.memberSeparator()).toBe(".");
79
+ expect(CppModeHelper.memberSeparator()).toBe(".");
69
80
  });
70
81
 
71
82
  it("nullLiteral returns nullptr", () => {
72
- expect(helper.nullLiteral()).toBe("nullptr");
83
+ expect(CppModeHelper.nullLiteral()).toBe("nullptr");
73
84
  });
74
85
 
75
86
  it("cast returns static_cast", () => {
76
- expect(helper.cast("int", "x")).toBe("static_cast<int>(x)");
77
- expect(helper.cast("uint8_t", "value")).toBe(
87
+ expect(CppModeHelper.cast("int", "x")).toBe("static_cast<int>(x)");
88
+ expect(CppModeHelper.cast("uint8_t", "value")).toBe(
78
89
  "static_cast<uint8_t>(value)",
79
90
  );
80
91
  });
81
92
 
82
93
  it("reinterpretCast returns reinterpret_cast", () => {
83
- expect(helper.reinterpretCast("char*", "ptr")).toBe(
94
+ expect(CppModeHelper.reinterpretCast("char*", "ptr")).toBe(
84
95
  "reinterpret_cast<char*>(ptr)",
85
96
  );
86
- expect(helper.reinterpretCast("uint8_t*", "buf")).toBe(
97
+ expect(CppModeHelper.reinterpretCast("uint8_t*", "buf")).toBe(
87
98
  "reinterpret_cast<uint8_t*>(buf)",
88
99
  );
89
100
  });
90
101
 
91
102
  it("isCppMode returns true", () => {
92
- expect(helper.isCppMode()).toBe(true);
103
+ expect(CppModeHelper.isCppMode()).toBe(true);
93
104
  });
94
105
  });
95
106
 
96
107
  describe("edge cases", () => {
97
- it("handles expressions with special characters", () => {
98
- const cHelper = new CppModeHelper({ cppMode: false });
99
- const cppHelper = new CppModeHelper({ cppMode: true });
108
+ it("handles expressions with special characters in C mode", () => {
109
+ CodeGenState.cppMode = false;
110
+
111
+ // Parenthesized expressions
112
+ expect(CppModeHelper.maybeAddressOf("(a + b)")).toBe("&(a + b)");
113
+
114
+ // Array access
115
+ expect(CppModeHelper.maybeDereference("arr[0]")).toBe("(*arr[0])");
116
+ });
117
+
118
+ it("handles expressions with special characters in C++ mode", () => {
119
+ CodeGenState.cppMode = true;
100
120
 
101
121
  // Parenthesized expressions
102
- expect(cHelper.maybeAddressOf("(a + b)")).toBe("&(a + b)");
103
- expect(cppHelper.maybeAddressOf("(a + b)")).toBe("(a + b)");
122
+ expect(CppModeHelper.maybeAddressOf("(a + b)")).toBe("(a + b)");
104
123
 
105
124
  // Array access
106
- expect(cHelper.maybeDereference("arr[0]")).toBe("(*arr[0])");
107
- expect(cppHelper.maybeDereference("arr[0]")).toBe("arr[0]");
125
+ expect(CppModeHelper.maybeDereference("arr[0]")).toBe("arr[0]");
108
126
  });
109
127
 
110
- it("handles complex type casts", () => {
111
- const cHelper = new CppModeHelper({ cppMode: false });
112
- const cppHelper = new CppModeHelper({ cppMode: true });
128
+ it("handles complex type casts in C mode", () => {
129
+ CodeGenState.cppMode = false;
113
130
 
114
131
  // Pointer to pointer
115
- expect(cHelper.cast("int**", "ptr")).toBe("(int**)ptr");
116
- expect(cppHelper.cast("int**", "ptr")).toBe("static_cast<int**>(ptr)");
132
+ expect(CppModeHelper.cast("int**", "ptr")).toBe("(int**)ptr");
117
133
 
118
134
  // Const types
119
- expect(cHelper.reinterpretCast("const char*", "str")).toBe(
135
+ expect(CppModeHelper.reinterpretCast("const char*", "str")).toBe(
120
136
  "(const char*)str",
121
137
  );
122
- expect(cppHelper.reinterpretCast("const char*", "str")).toBe(
138
+ });
139
+
140
+ it("handles complex type casts in C++ mode", () => {
141
+ CodeGenState.cppMode = true;
142
+
143
+ // Pointer to pointer
144
+ expect(CppModeHelper.cast("int**", "ptr")).toBe(
145
+ "static_cast<int**>(ptr)",
146
+ );
147
+
148
+ // Const types
149
+ expect(CppModeHelper.reinterpretCast("const char*", "str")).toBe(
123
150
  "reinterpret_cast<const char*>(str)",
124
151
  );
125
152
  });
@@ -2,165 +2,316 @@
2
2
  * Unit tests for EnumAssignmentValidator
3
3
  *
4
4
  * Issue #644: Tests for the extracted enum assignment validator.
5
+ * Rewritten for static class pattern using CodeGenState.
5
6
  */
6
7
 
7
- import { describe, it, expect, beforeEach, vi } from "vitest";
8
+ import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
8
9
  import EnumAssignmentValidator from "../EnumAssignmentValidator.js";
10
+ import CodeGenState from "../../CodeGenState.js";
11
+ import EnumTypeResolver from "../../resolution/EnumTypeResolver.js";
12
+ import ICodeGenSymbols from "../../../../types/ICodeGenSymbols.js";
9
13
 
10
14
  describe("EnumAssignmentValidator", () => {
11
- let knownEnums: Set<string>;
12
- let validator: EnumAssignmentValidator;
15
+ const createMockSymbols = (
16
+ overrides: Partial<ICodeGenSymbols> = {},
17
+ ): ICodeGenSymbols =>
18
+ ({
19
+ knownScopes: new Set(),
20
+ knownEnums: new Set(),
21
+ knownBitmaps: new Set(),
22
+ knownStructs: new Set(),
23
+ knownRegisters: new Set(),
24
+ bitmapBitWidth: new Map(),
25
+ bitmapFields: new Map(),
26
+ bitmapBackingType: new Map(),
27
+ enumMembers: new Map(),
28
+ structFields: new Map(),
29
+ structFieldArrays: new Map(),
30
+ structFieldDimensions: new Map(),
31
+ functionReturnTypes: new Map(),
32
+ scopeMembers: new Map(),
33
+ scopeMemberVisibility: new Map(),
34
+ scopedRegisters: new Map(),
35
+ registerMemberAccess: new Map(),
36
+ registerMemberTypes: new Map(),
37
+ registerBaseAddresses: new Map(),
38
+ registerMemberOffsets: new Map(),
39
+ registerMemberCTypes: new Map(),
40
+ scopeVariableUsage: new Map(),
41
+ scopePrivateConstValues: new Map(),
42
+ getSingleFunctionForVariable: () => null,
43
+ hasPublicSymbols: () => false,
44
+ ...overrides,
45
+ }) as ICodeGenSymbols;
13
46
 
14
47
  beforeEach(() => {
15
- knownEnums = new Set(["Color", "Status", "Scope_State"]);
48
+ CodeGenState.reset();
49
+ });
16
50
 
17
- validator = new EnumAssignmentValidator({
18
- knownEnums,
19
- getCurrentScope: vi.fn(() => null),
20
- getExpressionEnumType: vi.fn(() => null),
21
- isIntegerExpression: vi.fn(() => false),
22
- });
51
+ afterEach(() => {
52
+ vi.restoreAllMocks();
23
53
  });
24
54
 
25
55
  describe("validateEnumAssignment", () => {
26
56
  it("does nothing for non-enum types", () => {
57
+ CodeGenState.symbols = createMockSymbols();
27
58
  const expression = { getText: () => "42" } as never;
28
59
 
29
- // Should not throw for non-enum type
30
60
  expect(() =>
31
- validator.validateEnumAssignment("u32", expression),
61
+ EnumAssignmentValidator.validateEnumAssignment("u32", expression),
32
62
  ).not.toThrow();
33
63
  });
34
64
 
35
65
  it("throws for assigning different enum type", () => {
36
- const validator = new EnumAssignmentValidator({
37
- knownEnums,
38
- getCurrentScope: () => null,
39
- getExpressionEnumType: () => "Status", // Value is Status type
40
- isIntegerExpression: () => false,
66
+ CodeGenState.symbols = createMockSymbols({
67
+ knownEnums: new Set(["Color", "Status"]),
41
68
  });
69
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue("Status");
42
70
 
43
71
  const expression = { getText: () => "Status.OK" } as never;
44
72
 
45
73
  expect(() =>
46
- validator.validateEnumAssignment("Color", expression),
74
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
47
75
  ).toThrow("Cannot assign Status enum to Color enum");
48
76
  });
49
77
 
50
78
  it("throws for assigning integer to enum", () => {
51
- const validator = new EnumAssignmentValidator({
52
- knownEnums,
53
- getCurrentScope: () => null,
54
- getExpressionEnumType: () => null,
55
- isIntegerExpression: () => true, // Expression is integer
79
+ CodeGenState.symbols = createMockSymbols({
80
+ knownEnums: new Set(["Color"]),
56
81
  });
82
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
57
83
 
58
84
  const expression = { getText: () => "42" } as never;
59
85
 
60
86
  expect(() =>
61
- validator.validateEnumAssignment("Color", expression),
87
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
62
88
  ).toThrow("Cannot assign integer to Color enum");
63
89
  });
64
90
 
65
91
  it("allows same enum type assignment", () => {
66
- const validator = new EnumAssignmentValidator({
67
- knownEnums,
68
- getCurrentScope: () => null,
69
- getExpressionEnumType: () => "Color", // Value is Color type
70
- isIntegerExpression: () => false,
92
+ CodeGenState.symbols = createMockSymbols({
93
+ knownEnums: new Set(["Color"]),
71
94
  });
95
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue("Color");
72
96
 
73
97
  const expression = { getText: () => "Color.RED" } as never;
74
98
 
75
99
  expect(() =>
76
- validator.validateEnumAssignment("Color", expression),
100
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
77
101
  ).not.toThrow();
78
102
  });
79
103
 
80
104
  it("allows direct enum member access", () => {
105
+ CodeGenState.symbols = createMockSymbols({
106
+ knownEnums: new Set(["Color"]),
107
+ });
108
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
109
+
81
110
  const expression = { getText: () => "Color.RED" } as never;
82
111
 
83
112
  expect(() =>
84
- validator.validateEnumAssignment("Color", expression),
113
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
85
114
  ).not.toThrow();
86
115
  });
87
116
 
88
117
  it("allows this.Enum.MEMBER pattern", () => {
89
- const validator = new EnumAssignmentValidator({
118
+ CodeGenState.currentScope = "MyScope";
119
+ CodeGenState.symbols = createMockSymbols({
90
120
  knownEnums: new Set(["MyScope_State"]),
91
- getCurrentScope: () => "MyScope",
92
- getExpressionEnumType: () => null,
93
- isIntegerExpression: () => false,
94
121
  });
122
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
95
123
 
96
124
  const expression = { getText: () => "this.State.ACTIVE" } as never;
97
125
 
98
126
  expect(() =>
99
- validator.validateEnumAssignment("MyScope_State", expression),
127
+ EnumAssignmentValidator.validateEnumAssignment(
128
+ "MyScope_State",
129
+ expression,
130
+ ),
100
131
  ).not.toThrow();
101
132
  });
102
133
 
103
134
  it("allows global.Enum.MEMBER pattern", () => {
135
+ CodeGenState.symbols = createMockSymbols({
136
+ knownEnums: new Set(["Color"]),
137
+ });
138
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
139
+
104
140
  const expression = { getText: () => "global.Color.RED" } as never;
105
141
 
106
142
  expect(() =>
107
- validator.validateEnumAssignment("Color", expression),
143
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
108
144
  ).not.toThrow();
109
145
  });
110
146
 
111
147
  it("throws for global.WrongEnum.MEMBER pattern", () => {
148
+ CodeGenState.symbols = createMockSymbols({
149
+ knownEnums: new Set(["Color", "Status"]),
150
+ });
151
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
152
+
112
153
  const expression = { getText: () => "global.Status.OK" } as never;
113
154
 
114
155
  expect(() =>
115
- validator.validateEnumAssignment("Color", expression),
156
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
116
157
  ).toThrow("Cannot assign non-enum value to Color enum");
117
158
  });
118
159
 
160
+ it("allows global.structVar.enumField (non-enum parts[1])", () => {
161
+ CodeGenState.symbols = createMockSymbols({
162
+ knownEnums: new Set(["EValueId"]),
163
+ });
164
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
165
+
166
+ const expression = {
167
+ getText: () => "global.input.assignedValue",
168
+ } as never;
169
+
170
+ // Should NOT throw — parts[1] "input" is not a known enum
171
+ expect(() =>
172
+ EnumAssignmentValidator.validateEnumAssignment("EValueId", expression),
173
+ ).not.toThrow();
174
+ });
175
+
119
176
  it("allows scoped enum pattern", () => {
177
+ CodeGenState.symbols = createMockSymbols({
178
+ knownEnums: new Set(["Scope_State"]),
179
+ });
180
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
181
+
120
182
  const expression = { getText: () => "Scope.State.ACTIVE" } as never;
121
183
 
122
184
  expect(() =>
123
- validator.validateEnumAssignment("Scope_State", expression),
185
+ EnumAssignmentValidator.validateEnumAssignment(
186
+ "Scope_State",
187
+ expression,
188
+ ),
124
189
  ).not.toThrow();
125
190
  });
126
191
 
127
192
  it("throws for wrong scoped enum pattern", () => {
193
+ CodeGenState.symbols = createMockSymbols({
194
+ knownEnums: new Set(["Scope_State"]),
195
+ });
196
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
197
+
128
198
  const expression = { getText: () => "Other.State.ACTIVE" } as never;
129
199
 
130
200
  expect(() =>
131
- validator.validateEnumAssignment("Scope_State", expression),
201
+ EnumAssignmentValidator.validateEnumAssignment(
202
+ "Scope_State",
203
+ expression,
204
+ ),
132
205
  ).toThrow("Cannot assign non-enum value to Scope_State enum");
133
206
  });
134
207
 
135
208
  it("throws for this.WrongEnum.MEMBER with wrong scoped name", () => {
136
- const validator = new EnumAssignmentValidator({
209
+ CodeGenState.currentScope = "MyScope";
210
+ CodeGenState.symbols = createMockSymbols({
137
211
  knownEnums: new Set(["MyScope_State"]),
138
- getCurrentScope: () => "MyScope",
139
- getExpressionEnumType: () => null,
140
- isIntegerExpression: () => false,
141
212
  });
213
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
142
214
 
143
215
  const expression = { getText: () => "this.Wrong.ACTIVE" } as never;
144
216
 
145
217
  expect(() =>
146
- validator.validateEnumAssignment("MyScope_State", expression),
218
+ EnumAssignmentValidator.validateEnumAssignment(
219
+ "MyScope_State",
220
+ expression,
221
+ ),
147
222
  ).toThrow("Cannot assign non-enum value to MyScope_State enum");
148
223
  });
149
224
 
150
225
  it("throws for variable.field access on non-enum type", () => {
226
+ CodeGenState.symbols = createMockSymbols({
227
+ knownEnums: new Set(["Color"]),
228
+ });
229
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
230
+
151
231
  const expression = { getText: () => "someVar.field" } as never;
152
232
 
153
233
  expect(() =>
154
- validator.validateEnumAssignment("Color", expression),
234
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
155
235
  ).toThrow("Cannot assign non-enum value to Color enum");
156
236
  });
157
237
 
158
238
  it("allows variable.field when variable is a known enum", () => {
239
+ CodeGenState.symbols = createMockSymbols({
240
+ knownEnums: new Set(["Color", "Status"]),
241
+ });
242
+ vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
243
+
159
244
  const expression = { getText: () => "Status.OK" } as never;
160
245
 
161
246
  expect(() =>
162
- validator.validateEnumAssignment("Color", expression),
247
+ EnumAssignmentValidator.validateEnumAssignment("Color", expression),
163
248
  ).not.toThrow();
164
249
  });
165
250
  });
251
+
252
+ describe("isIntegerExpression", () => {
253
+ it("returns true for decimal literals", () => {
254
+ expect(
255
+ EnumAssignmentValidator.isIntegerExpression({
256
+ getText: () => "42",
257
+ } as never),
258
+ ).toBe(true);
259
+ });
260
+
261
+ it("returns true for hex literals", () => {
262
+ expect(
263
+ EnumAssignmentValidator.isIntegerExpression({
264
+ getText: () => "0xFF",
265
+ } as never),
266
+ ).toBe(true);
267
+ });
268
+
269
+ it("returns true for binary literals", () => {
270
+ expect(
271
+ EnumAssignmentValidator.isIntegerExpression({
272
+ getText: () => "0b1010",
273
+ } as never),
274
+ ).toBe(true);
275
+ });
276
+
277
+ it("returns true for integer-typed variable", () => {
278
+ CodeGenState.typeRegistry.set("count", {
279
+ baseType: "u32",
280
+ bitWidth: 32,
281
+ isArray: false,
282
+ isConst: false,
283
+ });
284
+
285
+ expect(
286
+ EnumAssignmentValidator.isIntegerExpression({
287
+ getText: () => "count",
288
+ } as never),
289
+ ).toBe(true);
290
+ });
291
+
292
+ it("returns false for enum-typed variable", () => {
293
+ CodeGenState.typeRegistry.set("state", {
294
+ baseType: "State",
295
+ bitWidth: 0,
296
+ isArray: false,
297
+ isConst: false,
298
+ isEnum: true,
299
+ enumTypeName: "State",
300
+ });
301
+
302
+ expect(
303
+ EnumAssignmentValidator.isIntegerExpression({
304
+ getText: () => "state",
305
+ } as never),
306
+ ).toBe(false);
307
+ });
308
+
309
+ it("returns false for non-integer expression", () => {
310
+ expect(
311
+ EnumAssignmentValidator.isIntegerExpression({
312
+ getText: () => "foo.bar",
313
+ } as never),
314
+ ).toBe(false);
315
+ });
316
+ });
166
317
  });