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
@@ -1,17 +1,12 @@
1
1
  /**
2
2
  * TypeValidator - Handles compile-time validation of types, assignments, and control flow
3
- * Extracted from CodeGenerator for better separation of concerns
3
+ * Static class using CodeGenState for all state access.
4
4
  * Issue #63: Validation logic separated for independent testing
5
5
  */
6
6
  import { existsSync } from "node:fs";
7
7
  import { dirname, resolve, join } from "node:path";
8
8
  import * as Parser from "../../logic/parser/grammar/CNextParser";
9
- import ICodeGenSymbols from "../../types/ICodeGenSymbols";
10
- import SymbolTable from "../../logic/symbols/SymbolTable";
11
- import TTypeInfo from "./types/TTypeInfo";
12
- import TParameterInfo from "./types/TParameterInfo";
13
- import ICallbackTypeInfo from "./types/ICallbackTypeInfo";
14
- import ITypeValidatorDeps from "./types/ITypeValidatorDeps";
9
+ import CodeGenState from "./CodeGenState";
15
10
  import TypeResolver from "./TypeResolver";
16
11
  import ExpressionUtils from "../../../utils/ExpressionUtils";
17
12
  // SonarCloud S3776: Extracted literal parsing to reduce complexity
@@ -29,39 +24,10 @@ const IMPLEMENTATION_EXTENSIONS = new Set([
29
24
  ]);
30
25
 
31
26
  /**
32
- * TypeValidator class - validates types, assignments, and control flow at compile time
27
+ * TypeValidator class - validates types, assignments, and control flow at compile time.
28
+ * All methods are static - uses CodeGenState for state access.
33
29
  */
34
30
  class TypeValidator {
35
- private readonly symbols: ICodeGenSymbols | null;
36
- private readonly symbolTable: SymbolTable | null;
37
- private readonly typeRegistry: Map<string, TTypeInfo>;
38
- private readonly typeResolver: TypeResolver;
39
- private readonly callbackTypes: Map<string, ICallbackTypeInfo>;
40
- private readonly knownFunctions: Set<string>;
41
- private readonly knownGlobals: Set<string>;
42
- private readonly getCurrentScopeFn: () => string | null;
43
- private readonly getScopeMembersFn: () => Map<string, Set<string>>;
44
- private readonly getCurrentParametersFn: () => Map<string, TParameterInfo>;
45
- private readonly getLocalVariablesFn: () => Set<string>;
46
- private readonly resolveIdentifierFn: (name: string) => string;
47
- private readonly getExpressionTypeFn: (ctx: unknown) => string | null;
48
-
49
- constructor(deps: ITypeValidatorDeps) {
50
- this.symbols = deps.symbols;
51
- this.symbolTable = deps.symbolTable;
52
- this.typeRegistry = deps.typeRegistry;
53
- this.typeResolver = deps.typeResolver;
54
- this.callbackTypes = deps.callbackTypes;
55
- this.knownFunctions = deps.knownFunctions;
56
- this.knownGlobals = deps.knownGlobals;
57
- this.getCurrentScopeFn = deps.getCurrentScope;
58
- this.getScopeMembersFn = deps.getScopeMembers;
59
- this.getCurrentParametersFn = deps.getCurrentParameters;
60
- this.getLocalVariablesFn = deps.getLocalVariables;
61
- this.resolveIdentifierFn = deps.resolveIdentifier;
62
- this.getExpressionTypeFn = deps.getExpressionType;
63
- }
64
-
65
31
  // ========================================================================
66
32
  // Include Validation (ADR-010)
67
33
  // ========================================================================
@@ -69,21 +35,18 @@ class TypeValidator {
69
35
  /**
70
36
  * ADR-010: Validate that #include doesn't include implementation files
71
37
  */
72
- validateIncludeNotImplementationFile(
38
+ static validateIncludeNotImplementationFile(
73
39
  includeText: string,
74
40
  lineNumber: number,
75
41
  ): void {
76
- // Extract the file path from #include directive
77
- // Match both <file> and "file" forms
78
42
  const angleMatch = /#\s*include\s*<([^>]+)>/.exec(includeText);
79
43
  const quoteMatch = /#\s*include\s*"([^"]+)"/.exec(includeText);
80
44
 
81
45
  const includePath = angleMatch?.[1] || quoteMatch?.[1];
82
46
  if (!includePath) {
83
- return; // Malformed include, let other validation handle it
47
+ return;
84
48
  }
85
49
 
86
- // Get the file extension (lowercase for case-insensitive comparison)
87
50
  const ext = includePath
88
51
  .substring(includePath.lastIndexOf("."))
89
52
  .toLowerCase();
@@ -98,31 +61,23 @@ class TypeValidator {
98
61
 
99
62
  /**
100
63
  * E0504: Validate that a .cnx alternative doesn't exist for a .h/.hpp include
101
- * This helps during codebase migration by alerting developers when they should
102
- * be using the C-Next version of a file instead of the C header.
103
- *
104
- * @param includeText - The full #include directive text
105
- * @param lineNumber - Line number for error reporting
106
- * @param sourcePath - Path to the source file (for resolving relative includes)
107
- * @param includePaths - Array of directories to search for includes
108
- * @param fileExists - Function to check if a file exists (injectable for testing)
109
64
  */
110
- validateIncludeNoCnxAlternative(
65
+ static validateIncludeNoCnxAlternative(
111
66
  includeText: string,
112
67
  lineNumber: number,
113
68
  sourcePath: string | null,
114
69
  includePaths: string[],
115
70
  fileExists: (path: string) => boolean = existsSync,
116
71
  ): void {
117
- const parsed = this._parseIncludeDirective(includeText);
72
+ const parsed = TypeValidator._parseIncludeDirective(includeText);
118
73
  if (!parsed) return;
119
74
  if (parsed.path.endsWith(".cnx")) return;
120
- if (!this._isHeaderFile(parsed.path)) return;
75
+ if (!TypeValidator._isHeaderFile(parsed.path)) return;
121
76
 
122
77
  const cnxPath = parsed.path.replace(/\.(h|hpp)$/i, ".cnx");
123
78
 
124
79
  if (parsed.isQuoted) {
125
- this._checkQuotedIncludeForCnx(
80
+ TypeValidator._checkQuotedIncludeForCnx(
126
81
  parsed.path,
127
82
  cnxPath,
128
83
  sourcePath,
@@ -130,7 +85,7 @@ class TypeValidator {
130
85
  fileExists,
131
86
  );
132
87
  } else {
133
- this._checkAngleIncludeForCnx(
88
+ TypeValidator._checkAngleIncludeForCnx(
134
89
  parsed.path,
135
90
  cnxPath,
136
91
  includePaths,
@@ -140,10 +95,7 @@ class TypeValidator {
140
95
  }
141
96
  }
142
97
 
143
- /**
144
- * Parse an include directive to extract path and type
145
- */
146
- private _parseIncludeDirective(
98
+ private static _parseIncludeDirective(
147
99
  includeText: string,
148
100
  ): { path: string; isQuoted: boolean } | null {
149
101
  const angleMatch = /#\s*include\s*<([^>]+)>/.exec(includeText);
@@ -154,18 +106,12 @@ class TypeValidator {
154
106
  return null;
155
107
  }
156
108
 
157
- /**
158
- * Check if a path is a header file (.h or .hpp)
159
- */
160
- private _isHeaderFile(path: string): boolean {
109
+ private static _isHeaderFile(path: string): boolean {
161
110
  const ext = path.substring(path.lastIndexOf(".")).toLowerCase();
162
111
  return ext === ".h" || ext === ".hpp";
163
112
  }
164
113
 
165
- /**
166
- * Check quoted include for .cnx alternative
167
- */
168
- private _checkQuotedIncludeForCnx(
114
+ private static _checkQuotedIncludeForCnx(
169
115
  includePath: string,
170
116
  cnxPath: string,
171
117
  sourcePath: string | null,
@@ -184,10 +130,7 @@ class TypeValidator {
184
130
  }
185
131
  }
186
132
 
187
- /**
188
- * Check angle bracket include for .cnx alternative in search paths
189
- */
190
- private _checkAngleIncludeForCnx(
133
+ private static _checkAngleIncludeForCnx(
191
134
  includePath: string,
192
135
  cnxPath: string,
193
136
  includePaths: string[],
@@ -209,10 +152,7 @@ class TypeValidator {
209
152
  // Bitmap Field Validation (ADR-034)
210
153
  // ========================================================================
211
154
 
212
- /**
213
- * ADR-034: Validate that a literal value fits in a bitmap field
214
- */
215
- validateBitmapFieldLiteral(
155
+ static validateBitmapFieldLiteral(
216
156
  expr: Parser.ExpressionContext,
217
157
  width: number,
218
158
  fieldName: string,
@@ -220,7 +160,6 @@ class TypeValidator {
220
160
  const text = expr.getText().trim();
221
161
  const maxValue = (1 << width) - 1;
222
162
 
223
- // Check for integer literals
224
163
  let value: number | null = null;
225
164
 
226
165
  if (/^\d+$/.exec(text)) {
@@ -242,11 +181,7 @@ class TypeValidator {
242
181
  // Array Bounds Validation (ADR-036)
243
182
  // ========================================================================
244
183
 
245
- /**
246
- * ADR-036: Check array bounds at compile time for constant indices.
247
- * Throws an error if the constant index is out of bounds.
248
- */
249
- checkArrayBounds(
184
+ static checkArrayBounds(
250
185
  arrayName: string,
251
186
  dimensions: number[],
252
187
  indexExprs: Parser.ExpressionContext[],
@@ -261,7 +196,6 @@ class TypeValidator {
261
196
  `Array index out of bounds: ${constValue} is negative for '${arrayName}' dimension ${i + 1} (line ${line})`,
262
197
  );
263
198
  } else if (dimensions[i] > 0 && constValue >= dimensions[i]) {
264
- // Issue #547: Skip upper bound check for unsized dimensions (size 0)
265
199
  throw new Error(
266
200
  `Array index out of bounds: ${constValue} >= ${dimensions[i]} for '${arrayName}' dimension ${i + 1} (line ${line})`,
267
201
  );
@@ -274,12 +208,7 @@ class TypeValidator {
274
208
  // Callback Assignment Validation (ADR-029)
275
209
  // ========================================================================
276
210
 
277
- /**
278
- * ADR-029: Validate callback assignment with nominal typing
279
- * - If value IS a callback type used as a field type: must match exactly (nominal typing)
280
- * - If value is just a function (not used as a type): signature must match
281
- */
282
- validateCallbackAssignment(
211
+ static validateCallbackAssignment(
283
212
  expectedType: string,
284
213
  valueExpr: Parser.ExpressionContext,
285
214
  fieldName: string,
@@ -287,30 +216,23 @@ class TypeValidator {
287
216
  ): void {
288
217
  const valueText = valueExpr.getText();
289
218
 
290
- // Check if the value is a known function
291
- if (!this.knownFunctions.has(valueText)) {
292
- // Not a function name - could be a variable holding a callback
293
- // Skip validation for now (C compiler will catch type mismatches)
219
+ if (!CodeGenState.knownFunctions.has(valueText)) {
294
220
  return;
295
221
  }
296
222
 
297
- const expectedInfo = this.callbackTypes.get(expectedType);
298
- const valueInfo = this.callbackTypes.get(valueText);
223
+ const expectedInfo = CodeGenState.callbackTypes.get(expectedType);
224
+ const valueInfo = CodeGenState.callbackTypes.get(valueText);
299
225
 
300
226
  if (!expectedInfo || !valueInfo) {
301
- // Shouldn't happen, but guard against it
302
227
  return;
303
228
  }
304
229
 
305
- // First check if signatures match
306
- if (!this.callbackSignaturesMatch(expectedInfo, valueInfo)) {
230
+ if (!TypeValidator.callbackSignaturesMatch(expectedInfo, valueInfo)) {
307
231
  throw new Error(
308
232
  `Error: Function '${valueText}' signature does not match callback type '${expectedType}'`,
309
233
  );
310
234
  }
311
235
 
312
- // Nominal typing: if the value function is used as a field type somewhere,
313
- // it can only be assigned to fields of that same type
314
236
  if (
315
237
  isCallbackTypeUsedAsFieldType(valueText) &&
316
238
  valueText !== expectedType
@@ -322,10 +244,26 @@ class TypeValidator {
322
244
  }
323
245
  }
324
246
 
325
- /**
326
- * ADR-029: Check if two callback signatures match
327
- */
328
- callbackSignaturesMatch(a: ICallbackTypeInfo, b: ICallbackTypeInfo): boolean {
247
+ static callbackSignaturesMatch(
248
+ a: {
249
+ returnType: string;
250
+ parameters: {
251
+ type: string;
252
+ isConst: boolean;
253
+ isPointer: boolean;
254
+ isArray: boolean;
255
+ }[];
256
+ },
257
+ b: {
258
+ returnType: string;
259
+ parameters: {
260
+ type: string;
261
+ isConst: boolean;
262
+ isPointer: boolean;
263
+ isArray: boolean;
264
+ }[];
265
+ },
266
+ ): boolean {
329
267
  if (a.returnType !== b.returnType) return false;
330
268
  if (a.parameters.length !== b.parameters.length) return false;
331
269
 
@@ -345,41 +283,29 @@ class TypeValidator {
345
283
  // Const Assignment Validation (ADR-013)
346
284
  // ========================================================================
347
285
 
348
- /**
349
- * ADR-013: Check if assigning to an identifier would violate const rules.
350
- * Returns error message if const, null if mutable.
351
- */
352
- checkConstAssignment(identifier: string): string | null {
353
- // Check if it's a const parameter
354
- const paramInfo = this.getCurrentParametersFn().get(identifier);
286
+ static checkConstAssignment(identifier: string): string | null {
287
+ const paramInfo = CodeGenState.currentParameters.get(identifier);
355
288
  if (paramInfo?.isConst) {
356
289
  return `cannot assign to const parameter '${identifier}'`;
357
290
  }
358
291
 
359
- // Resolve identifier to scoped name for proper lookup
360
- const scopedName = this.resolveIdentifierFn(identifier);
292
+ const scopedName = CodeGenState.resolveIdentifier(identifier);
361
293
 
362
- // Check if it's a const variable
363
- const typeInfo = this.typeRegistry.get(scopedName);
294
+ const typeInfo = CodeGenState.typeRegistry.get(scopedName);
364
295
  if (typeInfo?.isConst) {
365
296
  return `cannot assign to const variable '${identifier}'`;
366
297
  }
367
298
 
368
- return null; // Mutable, assignment OK
299
+ return null;
369
300
  }
370
301
 
371
- /**
372
- * ADR-013: Check if an argument is const (variable or parameter)
373
- */
374
- isConstValue(identifier: string): boolean {
375
- // Check if it's a const parameter
376
- const paramInfo = this.getCurrentParametersFn().get(identifier);
302
+ static isConstValue(identifier: string): boolean {
303
+ const paramInfo = CodeGenState.currentParameters.get(identifier);
377
304
  if (paramInfo?.isConst) {
378
305
  return true;
379
306
  }
380
307
 
381
- // Check if it's a const variable
382
- const typeInfo = this.typeRegistry.get(identifier);
308
+ const typeInfo = CodeGenState.typeRegistry.get(identifier);
383
309
  if (typeInfo?.isConst) {
384
310
  return true;
385
311
  }
@@ -391,44 +317,36 @@ class TypeValidator {
391
317
  // Scope Identifier Validation (ADR-016)
392
318
  // ========================================================================
393
319
 
394
- /**
395
- * ADR-016: Validate that bare identifiers inside scopes are only used for local variables.
396
- * Throws an error if a bare identifier references a scope member or global.
397
- */
398
- validateBareIdentifierInScope(
320
+ static validateBareIdentifierInScope(
399
321
  identifier: string,
400
322
  isLocalVariable: boolean,
401
323
  isKnownStruct: (name: string) => boolean,
402
324
  ): void {
403
- const currentScope = this.getCurrentScopeFn();
325
+ const currentScope = CodeGenState.currentScope;
404
326
 
405
- // Only enforce inside scopes
406
327
  if (!currentScope) {
407
328
  return;
408
329
  }
409
330
 
410
- // Local variables and parameters are allowed as bare identifiers
411
331
  if (isLocalVariable) {
412
332
  return;
413
333
  }
414
334
 
415
- // Check if this identifier is a scope member
416
- const scopeMembers = this.getScopeMembersFn().get(currentScope);
335
+ const scopeMembers = CodeGenState.scopeMembers.get(currentScope);
417
336
  if (scopeMembers?.has(identifier)) {
418
337
  throw new Error(
419
338
  `Error: Use 'this.${identifier}' to access scope member '${identifier}' inside scope '${currentScope}'`,
420
339
  );
421
340
  }
422
341
 
423
- // Check if this is a known global (register, function, enum, struct)
424
- if (this.symbols!.knownRegisters.has(identifier)) {
342
+ if (CodeGenState.symbols!.knownRegisters.has(identifier)) {
425
343
  throw new Error(
426
344
  `Error: Use 'global.${identifier}' to access register '${identifier}' inside scope '${currentScope}'`,
427
345
  );
428
346
  }
429
347
 
430
348
  if (
431
- this.knownFunctions.has(identifier) &&
349
+ CodeGenState.knownFunctions.has(identifier) &&
432
350
  !identifier.startsWith(currentScope + "_")
433
351
  ) {
434
352
  throw new Error(
@@ -436,7 +354,7 @@ class TypeValidator {
436
354
  );
437
355
  }
438
356
 
439
- if (this.symbols!.knownEnums.has(identifier)) {
357
+ if (CodeGenState.symbols!.knownEnums.has(identifier)) {
440
358
  throw new Error(
441
359
  `Error: Use 'global.${identifier}' to access global enum '${identifier}' inside scope '${currentScope}'`,
442
360
  );
@@ -448,9 +366,7 @@ class TypeValidator {
448
366
  );
449
367
  }
450
368
 
451
- // Check if this identifier exists as a global variable in the type registry
452
- // (but not a scoped variable - those would have Scope_ prefix)
453
- const typeInfo = this.typeRegistry.get(identifier);
369
+ const typeInfo = CodeGenState.typeRegistry.get(identifier);
454
370
  if (typeInfo && !identifier.includes("_")) {
455
371
  throw new Error(
456
372
  `Error: Use 'global.${identifier}' to access global variable '${identifier}' inside scope '${currentScope}'`,
@@ -458,103 +374,81 @@ class TypeValidator {
458
374
  }
459
375
  }
460
376
 
461
- /**
462
- * Resolve a bare identifier to its qualified name using priority:
463
- * 1. Local variables/parameters (return null - no transformation needed)
464
- * 2. Scope members (return Scope_identifier)
465
- * 3. Global variables/functions (return identifier)
466
- *
467
- * Returns null if no transformation needed, the resolved name otherwise.
468
- * Used by implicit scope resolution feature.
469
- */
470
- resolveBareIdentifier(
377
+ static resolveBareIdentifier(
471
378
  identifier: string,
472
379
  isLocalVariable: boolean,
473
380
  isKnownStruct: (name: string) => boolean,
474
381
  ): string | null {
475
- // Priority 1: Local variables and parameters - no transformation
476
382
  if (isLocalVariable) {
477
383
  return null;
478
384
  }
479
385
 
480
- const currentScope = this.getCurrentScopeFn();
386
+ const currentScope = CodeGenState.currentScope;
481
387
 
482
- // Priority 2: If inside a scope, check scope members
483
388
  if (currentScope) {
484
- const scopeResolved = this._resolveScopeMember(identifier, currentScope);
389
+ const scopeResolved = TypeValidator._resolveScopeMember(
390
+ identifier,
391
+ currentScope,
392
+ );
485
393
  if (scopeResolved) return scopeResolved;
486
394
  }
487
395
 
488
- // Priority 3: Global resolution
489
396
  if (
490
- this._isKnownGlobalIdentifier(identifier, currentScope, isKnownStruct)
397
+ TypeValidator._isKnownGlobalIdentifier(
398
+ identifier,
399
+ currentScope,
400
+ isKnownStruct,
401
+ )
491
402
  ) {
492
403
  return currentScope ? identifier : null;
493
404
  }
494
405
 
495
- // Not found anywhere - let it pass through (may be enum member or error later)
496
406
  return null;
497
407
  }
498
408
 
499
- /**
500
- * Try to resolve identifier as a scope member or scope function
501
- */
502
- private _resolveScopeMember(
409
+ private static _resolveScopeMember(
503
410
  identifier: string,
504
411
  currentScope: string,
505
412
  ): string | null {
506
- const scopeMembers = this.getScopeMembersFn().get(currentScope);
413
+ const scopeMembers = CodeGenState.scopeMembers.get(currentScope);
507
414
  if (scopeMembers?.has(identifier)) {
508
415
  return `${currentScope}_${identifier}`;
509
416
  }
510
417
 
511
418
  const scopedFuncName = `${currentScope}_${identifier}`;
512
- if (this.knownFunctions.has(scopedFuncName)) {
419
+ if (CodeGenState.knownFunctions.has(scopedFuncName)) {
513
420
  return scopedFuncName;
514
421
  }
515
422
 
516
423
  return null;
517
424
  }
518
425
 
519
- /**
520
- * Check if identifier is a known global (variable, function, enum, struct, register)
521
- */
522
- private _isKnownGlobalIdentifier(
426
+ private static _isKnownGlobalIdentifier(
523
427
  identifier: string,
524
428
  currentScope: string | null,
525
429
  isKnownStruct: (name: string) => boolean,
526
430
  ): boolean {
527
- // Global variable in type registry
528
- const typeInfo = this.typeRegistry.get(identifier);
431
+ const typeInfo = CodeGenState.typeRegistry.get(identifier);
529
432
  if (typeInfo && !identifier.includes("_")) {
530
433
  return true;
531
434
  }
532
435
 
533
- // Global function (not prefixed with current scope)
534
436
  if (
535
- this.knownFunctions.has(identifier) &&
437
+ CodeGenState.knownFunctions.has(identifier) &&
536
438
  !identifier.startsWith(currentScope + "_")
537
439
  ) {
538
440
  return true;
539
441
  }
540
442
 
541
- // Known types: enums, structs, registers
542
443
  return (
543
- this.symbols!.knownEnums.has(identifier) ||
444
+ CodeGenState.symbols!.knownEnums.has(identifier) ||
544
445
  isKnownStruct(identifier) ||
545
- this.symbols!.knownRegisters.has(identifier)
446
+ CodeGenState.symbols!.knownRegisters.has(identifier)
546
447
  );
547
448
  }
548
449
 
549
- /**
550
- * Resolve an identifier that appears before a '.' (member access).
551
- * Prioritizes scope names for Scope.member() calls.
552
- *
553
- * Returns the resolved name or null if not a scope.
554
- */
555
- resolveForMemberAccess(identifier: string): string | null {
556
- // For member access, check if it's a scope name first
557
- if (this.symbols!.knownScopes.has(identifier)) {
450
+ static resolveForMemberAccess(identifier: string): string | null {
451
+ if (CodeGenState.symbols!.knownScopes.has(identifier)) {
558
452
  return identifier;
559
453
  }
560
454
  return null;
@@ -564,44 +458,33 @@ class TypeValidator {
564
458
  // Critical Section Validation (ADR-050)
565
459
  // ========================================================================
566
460
 
567
- /**
568
- * ADR-050: Validate no early exits inside critical block
569
- * return, break, continue would leave interrupts disabled
570
- */
571
- validateNoEarlyExits(ctx: Parser.BlockContext): void {
461
+ static validateNoEarlyExits(ctx: Parser.BlockContext): void {
572
462
  for (const stmt of ctx.statement()) {
573
- this._validateStatementForEarlyExit(stmt);
463
+ TypeValidator._validateStatementForEarlyExit(stmt);
574
464
  }
575
465
  }
576
466
 
577
- /**
578
- * Validate a single statement for early exits in critical sections
579
- */
580
- private _validateStatementForEarlyExit(stmt: Parser.StatementContext): void {
467
+ private static _validateStatementForEarlyExit(
468
+ stmt: Parser.StatementContext,
469
+ ): void {
581
470
  if (stmt.returnStatement()) {
582
471
  throw new Error(
583
472
  `E0853: Cannot use 'return' inside critical section - would leave interrupts disabled`,
584
473
  );
585
474
  }
586
475
 
587
- // Recursively check nested blocks
588
476
  if (stmt.block()) {
589
- this.validateNoEarlyExits(stmt.block()!);
477
+ TypeValidator.validateNoEarlyExits(stmt.block()!);
590
478
  }
591
479
 
592
- // Check inside if statements
593
480
  if (stmt.ifStatement()) {
594
- this._validateIfStatementForEarlyExit(stmt.ifStatement()!);
481
+ TypeValidator._validateIfStatementForEarlyExit(stmt.ifStatement()!);
595
482
  }
596
483
 
597
- // Check inside loops
598
- this._validateLoopForEarlyExit(stmt);
484
+ TypeValidator._validateLoopForEarlyExit(stmt);
599
485
  }
600
486
 
601
- /**
602
- * Validate if statement branches for early exits
603
- */
604
- private _validateIfStatementForEarlyExit(
487
+ private static _validateIfStatementForEarlyExit(
605
488
  ifStmt: Parser.IfStatementContext,
606
489
  ): void {
607
490
  for (const innerStmt of ifStmt.statement()) {
@@ -611,38 +494,35 @@ class TypeValidator {
611
494
  );
612
495
  }
613
496
  if (innerStmt.block()) {
614
- this.validateNoEarlyExits(innerStmt.block()!);
497
+ TypeValidator.validateNoEarlyExits(innerStmt.block()!);
615
498
  }
616
499
  }
617
500
  }
618
501
 
619
- /**
620
- * Validate loop statements for early exits
621
- */
622
- private _validateLoopForEarlyExit(stmt: Parser.StatementContext): void {
502
+ private static _validateLoopForEarlyExit(
503
+ stmt: Parser.StatementContext,
504
+ ): void {
623
505
  if (stmt.whileStatement()) {
624
- this._checkLoopBodyForReturn(stmt.whileStatement()!.statement());
506
+ TypeValidator._checkLoopBodyForReturn(stmt.whileStatement()!.statement());
625
507
  }
626
508
  if (stmt.forStatement()) {
627
- this._checkLoopBodyForReturn(stmt.forStatement()!.statement());
509
+ TypeValidator._checkLoopBodyForReturn(stmt.forStatement()!.statement());
628
510
  }
629
511
  if (stmt.doWhileStatement()) {
630
- this.validateNoEarlyExits(stmt.doWhileStatement()!.block());
512
+ TypeValidator.validateNoEarlyExits(stmt.doWhileStatement()!.block());
631
513
  }
632
514
  }
633
515
 
634
- /**
635
- * Issue #707: Check a loop body statement for early return.
636
- * Shared between while and for loop validation in critical sections.
637
- */
638
- private _checkLoopBodyForReturn(loopStmt: Parser.StatementContext): void {
516
+ private static _checkLoopBodyForReturn(
517
+ loopStmt: Parser.StatementContext,
518
+ ): void {
639
519
  if (loopStmt.returnStatement()) {
640
520
  throw new Error(
641
521
  `E0853: Cannot use 'return' inside critical section - would leave interrupts disabled`,
642
522
  );
643
523
  }
644
524
  if (loopStmt.block()) {
645
- this.validateNoEarlyExits(loopStmt.block()!);
525
+ TypeValidator.validateNoEarlyExits(loopStmt.block()!);
646
526
  }
647
527
  }
648
528
 
@@ -650,10 +530,7 @@ class TypeValidator {
650
530
  // Switch Statement Validation (ADR-025)
651
531
  // ========================================================================
652
532
 
653
- /**
654
- * ADR-025: Validate switch statement for MISRA compliance
655
- */
656
- validateSwitchStatement(
533
+ static validateSwitchStatement(
657
534
  ctx: Parser.SwitchStatementContext,
658
535
  switchExpr: Parser.ExpressionContext,
659
536
  ): void {
@@ -661,26 +538,23 @@ class TypeValidator {
661
538
  const defaultCase = ctx.defaultCase();
662
539
  const totalClauses = cases.length + (defaultCase ? 1 : 0);
663
540
 
664
- // MISRA 16.7: No boolean switches (use if/else instead)
665
- const exprType = this.getExpressionTypeFn(switchExpr);
541
+ const exprType = TypeResolver.getExpressionType(switchExpr);
666
542
  if (exprType === "bool") {
667
543
  throw new Error(
668
544
  "Error: Cannot switch on boolean type (MISRA 16.7). Use if/else instead.",
669
545
  );
670
546
  }
671
547
 
672
- // MISRA 16.6: Minimum 2 clauses required
673
548
  if (totalClauses < 2) {
674
549
  throw new Error(
675
550
  "Error: Switch requires at least 2 clauses (MISRA 16.6). Use if statement for single case.",
676
551
  );
677
552
  }
678
553
 
679
- // Check for duplicate case values
680
554
  const seenValues = new Set<string>();
681
555
  for (const caseCtx of cases) {
682
556
  for (const labelCtx of caseCtx.caseLabel()) {
683
- const labelValue = this.getCaseLabelValue(labelCtx);
557
+ const labelValue = TypeValidator.getCaseLabelValue(labelCtx);
684
558
  if (seenValues.has(labelValue)) {
685
559
  throw new Error(
686
560
  `Error: Duplicate case value '${labelValue}' in switch statement.`,
@@ -690,38 +564,36 @@ class TypeValidator {
690
564
  }
691
565
  }
692
566
 
693
- // ADR-025: Enum exhaustiveness checking
694
- if (exprType && this.symbols!.knownEnums.has(exprType)) {
695
- this.validateEnumExhaustiveness(ctx, exprType, cases, defaultCase);
567
+ if (exprType && CodeGenState.symbols!.knownEnums.has(exprType)) {
568
+ TypeValidator.validateEnumExhaustiveness(
569
+ ctx,
570
+ exprType,
571
+ cases,
572
+ defaultCase,
573
+ );
696
574
  }
697
575
  }
698
576
 
699
- /**
700
- * ADR-025: Validate enum switch exhaustiveness with default(n) counting
701
- */
702
- validateEnumExhaustiveness(
577
+ static validateEnumExhaustiveness(
703
578
  ctx: Parser.SwitchStatementContext,
704
579
  enumTypeName: string,
705
580
  cases: Parser.SwitchCaseContext[],
706
581
  defaultCase: Parser.DefaultCaseContext | null,
707
582
  ): void {
708
- const enumVariants = this.symbols!.enumMembers.get(enumTypeName);
709
- if (!enumVariants) return; // Shouldn't happen if knownEnums has it
583
+ const enumVariants = CodeGenState.symbols!.enumMembers.get(enumTypeName);
584
+ if (!enumVariants) return;
710
585
 
711
586
  const totalVariants = enumVariants.size;
712
587
 
713
- // Count explicit cases (each || alternative counts as 1)
714
588
  let explicitCaseCount = 0;
715
589
  for (const caseCtx of cases) {
716
590
  explicitCaseCount += caseCtx.caseLabel().length;
717
591
  }
718
592
 
719
593
  if (defaultCase) {
720
- // Check for default(n) syntax
721
- const defaultCount = this.getDefaultCount(defaultCase);
594
+ const defaultCount = TypeValidator.getDefaultCount(defaultCase);
722
595
 
723
596
  if (defaultCount !== null) {
724
- // default(n) mode: explicit + n must equal total variants
725
597
  const covered = explicitCaseCount + defaultCount;
726
598
  if (covered !== totalVariants) {
727
599
  throw new Error(
@@ -731,9 +603,7 @@ class TypeValidator {
731
603
  );
732
604
  }
733
605
  }
734
- // Plain default: no exhaustiveness check needed
735
606
  } else if (explicitCaseCount !== totalVariants) {
736
- // No default: must cover all variants explicitly
737
607
  const missing = totalVariants - explicitCaseCount;
738
608
  throw new Error(
739
609
  `Error: Non-exhaustive switch on ${enumTypeName}: covers ${explicitCaseCount} of ${totalVariants} variants, missing ${missing}.`,
@@ -741,10 +611,7 @@ class TypeValidator {
741
611
  }
742
612
  }
743
613
 
744
- /**
745
- * Get the count from default(n) syntax, or null for plain default
746
- */
747
- getDefaultCount(ctx: Parser.DefaultCaseContext): number | null {
614
+ static getDefaultCount(ctx: Parser.DefaultCaseContext): number | null {
748
615
  const intLiteral = ctx.INTEGER_LITERAL();
749
616
  if (intLiteral) {
750
617
  return Number.parseInt(intLiteral.getText(), 10);
@@ -752,10 +619,7 @@ class TypeValidator {
752
619
  return null;
753
620
  }
754
621
 
755
- /**
756
- * Get the string representation of a case label for duplicate checking
757
- */
758
- getCaseLabelValue(ctx: Parser.CaseLabelContext): string {
622
+ static getCaseLabelValue(ctx: Parser.CaseLabelContext): string {
759
623
  if (ctx.qualifiedType()) {
760
624
  const qt = ctx.qualifiedType()!;
761
625
  return qt
@@ -768,23 +632,19 @@ class TypeValidator {
768
632
  }
769
633
  if (ctx.INTEGER_LITERAL()) {
770
634
  const num = ctx.INTEGER_LITERAL()!.getText();
771
- // Check if minus token exists (first child would be '-')
772
635
  const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
773
636
  const value = BigInt(num);
774
637
  return String(hasNeg ? -value : value);
775
638
  }
776
639
  if (ctx.HEX_LITERAL()) {
777
- // Normalize hex to decimal for comparison
778
640
  const hex = ctx.HEX_LITERAL()!.getText();
779
- // Check if minus token exists (first child would be '-')
780
641
  const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
781
- const value = BigInt(hex); // BigInt handles 0x prefix natively
642
+ const value = BigInt(hex);
782
643
  return String(hasNeg ? -value : value);
783
644
  }
784
645
  if (ctx.BINARY_LITERAL()) {
785
- // Normalize binary to decimal for comparison
786
646
  const bin = ctx.BINARY_LITERAL()!.getText();
787
- return String(BigInt(bin)); // BigInt handles 0b prefix natively
647
+ return String(BigInt(bin));
788
648
  }
789
649
  if (ctx.CHAR_LITERAL()) {
790
650
  return ctx.CHAR_LITERAL()!.getText();
@@ -796,17 +656,11 @@ class TypeValidator {
796
656
  // Ternary Validation (ADR-022)
797
657
  // ========================================================================
798
658
 
799
- /**
800
- * ADR-022: Validate that ternary condition is a boolean expression
801
- * Must be a comparison or logical operation, not just a value
802
- */
803
- validateTernaryCondition(ctx: Parser.OrExpressionContext): void {
804
- // Check if the condition contains a comparison or logical operator
659
+ static validateTernaryCondition(ctx: Parser.OrExpressionContext): void {
805
660
  const text = ctx.getText();
806
661
 
807
- // If it has && or ||, it's a logical expression (valid)
808
662
  if (ctx.andExpression().length > 1) {
809
- return; // Has || operator - valid
663
+ return;
810
664
  }
811
665
 
812
666
  const andExpr = ctx.andExpression(0);
@@ -817,7 +671,7 @@ class TypeValidator {
817
671
  }
818
672
 
819
673
  if (andExpr.equalityExpression().length > 1) {
820
- return; // Has && operator - valid
674
+ return;
821
675
  }
822
676
 
823
677
  const equalityExpr = andExpr.equalityExpression(0);
@@ -828,7 +682,7 @@ class TypeValidator {
828
682
  }
829
683
 
830
684
  if (equalityExpr.relationalExpression().length > 1) {
831
- return; // Has = or != operator - valid
685
+ return;
832
686
  }
833
687
 
834
688
  const relationalExpr = equalityExpr.relationalExpression(0);
@@ -839,24 +693,19 @@ class TypeValidator {
839
693
  }
840
694
 
841
695
  if (relationalExpr.bitwiseOrExpression().length > 1) {
842
- return; // Has <, >, <=, >= operator - valid
696
+ return;
843
697
  }
844
698
 
845
- // No comparison or logical operators found - just a value
846
699
  throw new Error(
847
700
  `Error: Ternary condition must be a boolean expression (comparison or logical operation), not '${text}'`,
848
701
  );
849
702
  }
850
703
 
851
- /**
852
- * ADR-022: Validate that expression does not contain a nested ternary
853
- */
854
- validateNoNestedTernary(
704
+ static validateNoNestedTernary(
855
705
  ctx: Parser.OrExpressionContext,
856
706
  branchName: string,
857
707
  ): void {
858
708
  const text = ctx.getText();
859
- // Check for ternary pattern: something ? something : something
860
709
  if (text.includes("?") && text.includes(":")) {
861
710
  throw new Error(
862
711
  `Error: Nested ternary not allowed in ${branchName}. Use if/else instead.`,
@@ -868,17 +717,10 @@ class TypeValidator {
868
717
  // Do-While Validation (ADR-027)
869
718
  // ========================================================================
870
719
 
871
- /**
872
- * ADR-027: Validate that do-while condition is a boolean expression (E0701)
873
- * Must be a comparison, logical operation, or boolean variable - not just a value.
874
- * This enforces MISRA C:2012 Rule 14.4.
875
- */
876
- validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
877
- // Unwrap: ExpressionContext -> TernaryExpressionContext -> OrExpressionContext
720
+ static validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
878
721
  const ternaryExpr = ctx.ternaryExpression();
879
722
  const orExprs = ternaryExpr.orExpression();
880
723
 
881
- // For do-while, we expect a non-ternary expression (single orExpression)
882
724
  if (orExprs.length !== 1) {
883
725
  throw new Error(
884
726
  `Error E0701: do-while condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
@@ -888,7 +730,6 @@ class TypeValidator {
888
730
  const orExpr = orExprs[0];
889
731
  const text = orExpr.getText();
890
732
 
891
- // If it has || operator, it's valid (logical expression)
892
733
  if (orExpr.andExpression().length > 1) {
893
734
  return;
894
735
  }
@@ -900,7 +741,6 @@ class TypeValidator {
900
741
  );
901
742
  }
902
743
 
903
- // If it has && operator, it's valid
904
744
  if (andExpr.equalityExpression().length > 1) {
905
745
  return;
906
746
  }
@@ -912,7 +752,6 @@ class TypeValidator {
912
752
  );
913
753
  }
914
754
 
915
- // If it has = or != operator, it's valid
916
755
  if (equalityExpr.relationalExpression().length > 1) {
917
756
  return;
918
757
  }
@@ -924,42 +763,34 @@ class TypeValidator {
924
763
  );
925
764
  }
926
765
 
927
- // If it has <, >, <=, >= operator, it's valid
928
766
  if (relationalExpr.bitwiseOrExpression().length > 1) {
929
767
  return;
930
768
  }
931
769
 
932
- // Check if it's a unary ! (negation) expression - that's valid on booleans
933
770
  const bitwiseOrExpr = relationalExpr.bitwiseOrExpression(0);
934
- if (bitwiseOrExpr && this.isBooleanExpression(bitwiseOrExpr)) {
771
+ if (bitwiseOrExpr && TypeValidator._isBooleanExpression(bitwiseOrExpr)) {
935
772
  return;
936
773
  }
937
774
 
938
- // No comparison or logical operators found - just a value
939
775
  throw new Error(
940
776
  `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
941
777
  );
942
778
  }
943
779
 
944
- /**
945
- * Check if an expression resolves to a boolean type.
946
- * This includes: boolean literals, boolean variables, negation of booleans, function calls returning bool.
947
- */
948
- private isBooleanExpression(ctx: Parser.BitwiseOrExpressionContext): boolean {
780
+ private static _isBooleanExpression(
781
+ ctx: Parser.BitwiseOrExpressionContext,
782
+ ): boolean {
949
783
  const text = ctx.getText();
950
784
 
951
- // Check for boolean literals
952
785
  if (text === "true" || text === "false") {
953
786
  return true;
954
787
  }
955
788
 
956
- // Check for negation (! operator) - valid for boolean expressions
957
789
  if (text.startsWith("!")) {
958
790
  return true;
959
791
  }
960
792
 
961
- // Check if it's a known boolean variable
962
- const typeInfo = this.typeRegistry.get(text);
793
+ const typeInfo = CodeGenState.typeRegistry.get(text);
963
794
  if (typeInfo?.baseType === "bool") {
964
795
  return true;
965
796
  }
@@ -971,13 +802,7 @@ class TypeValidator {
971
802
  // Function Call in Condition Validation (Issue #254)
972
803
  // ========================================================================
973
804
 
974
- /**
975
- * Issue #254: Validate that condition does not contain function calls (E0702)
976
- * MISRA C:2012 Rule 13.5 forbids function calls in conditions because:
977
- * - Short-circuit evaluation may skip the function call
978
- * - Side effects become unpredictable
979
- */
980
- validateConditionNoFunctionCall(
805
+ static validateConditionNoFunctionCall(
981
806
  ctx: Parser.ExpressionContext,
982
807
  conditionType: string,
983
808
  ): void {
@@ -991,11 +816,7 @@ class TypeValidator {
991
816
  }
992
817
  }
993
818
 
994
- /**
995
- * Issue #254: Validate that ternary condition does not contain function calls (E0702)
996
- * Used for ternary expressions where condition is OrExpressionContext
997
- */
998
- validateTernaryConditionNoFunctionCall(
819
+ static validateTernaryConditionNoFunctionCall(
999
820
  ctx: Parser.OrExpressionContext,
1000
821
  ): void {
1001
822
  if (ExpressionUtils.hasFunctionCallInOr(ctx)) {
@@ -1008,31 +829,22 @@ class TypeValidator {
1008
829
  }
1009
830
  }
1010
831
 
1011
- // Function call detection delegated to ExpressionUtils (Issue #254, #366)
1012
-
1013
832
  // ========================================================================
1014
833
  // Shift Amount Validation (MISRA C:2012 Rule 12.2)
1015
834
  // ========================================================================
1016
835
 
1017
- /**
1018
- * Validate shift amount doesn't exceed type width (MISRA C:2012 Rule 12.2).
1019
- * Shifting by an amount >= type width is undefined behavior.
1020
- */
1021
- validateShiftAmount(
836
+ static validateShiftAmount(
1022
837
  leftType: string,
1023
838
  rightExpr: Parser.AdditiveExpressionContext,
1024
839
  op: string,
1025
840
  ctx: Parser.ShiftExpressionContext,
1026
841
  ): void {
1027
- // Get type width in bits
1028
- const typeWidth = this.getTypeWidth(leftType);
1029
- if (!typeWidth) return; // Unknown type, skip validation
842
+ const typeWidth = TypeValidator._getTypeWidth(leftType);
843
+ if (!typeWidth) return;
1030
844
 
1031
- // Try to evaluate shift amount if it's a constant
1032
- const shiftAmount = this.evaluateShiftAmount(rightExpr);
1033
- if (shiftAmount === null) return; // Not a constant, skip validation
845
+ const shiftAmount = TypeValidator._evaluateShiftAmount(rightExpr);
846
+ if (shiftAmount === null) return;
1034
847
 
1035
- // Check for negative shift (undefined behavior)
1036
848
  if (shiftAmount < 0) {
1037
849
  throw new Error(
1038
850
  `Error: Negative shift amount (${shiftAmount}) is undefined behavior\n` +
@@ -1042,7 +854,6 @@ class TypeValidator {
1042
854
  );
1043
855
  }
1044
856
 
1045
- // Check if shift amount >= type width (undefined behavior)
1046
857
  if (shiftAmount >= typeWidth) {
1047
858
  throw new Error(
1048
859
  `Error: Shift amount (${shiftAmount}) exceeds type width (${typeWidth} bits) for type '${leftType}'\n` +
@@ -1053,10 +864,7 @@ class TypeValidator {
1053
864
  }
1054
865
  }
1055
866
 
1056
- /**
1057
- * Get the bit width of a primitive type.
1058
- */
1059
- private getTypeWidth(type: string): number | null {
867
+ private static _getTypeWidth(type: string): number | null {
1060
868
  switch (type) {
1061
869
  case "u8":
1062
870
  case "i8":
@@ -1071,34 +879,24 @@ class TypeValidator {
1071
879
  case "i64":
1072
880
  return 64;
1073
881
  default:
1074
- return null; // Unknown type
882
+ return null;
1075
883
  }
1076
884
  }
1077
885
 
1078
- /**
1079
- * Try to evaluate a shift amount expression to get its numeric value.
1080
- * Returns null if not a constant or cannot be evaluated.
1081
- */
1082
- private evaluateShiftAmount(
886
+ private static _evaluateShiftAmount(
1083
887
  ctx: Parser.AdditiveExpressionContext,
1084
888
  ): number | null {
1085
- // For now, handle simple literals only
1086
889
  const multExprs = ctx.multiplicativeExpression();
1087
- if (multExprs.length !== 1) return null; // Not a simple literal
890
+ if (multExprs.length !== 1) return null;
1088
891
 
1089
892
  const multExpr = multExprs[0];
1090
893
  const unaryExprs = multExpr.unaryExpression();
1091
894
  if (unaryExprs.length !== 1) return null;
1092
895
 
1093
- const unaryExpr = unaryExprs[0];
1094
- return this.evaluateUnaryExpression(unaryExpr);
896
+ return TypeValidator._evaluateUnaryExpression(unaryExprs[0]);
1095
897
  }
1096
898
 
1097
- /**
1098
- * Helper to evaluate a unary expression recursively.
1099
- * SonarCloud S3776: Uses LiteralEvaluator for literal parsing.
1100
- */
1101
- private evaluateUnaryExpression(
899
+ private static _evaluateUnaryExpression(
1102
900
  ctx: Parser.UnaryExpressionContext,
1103
901
  ): number | null {
1104
902
  const unaryText = ctx.getText();
@@ -1106,23 +904,19 @@ class TypeValidator {
1106
904
 
1107
905
  const postfixExpr = ctx.postfixExpression();
1108
906
  if (postfixExpr) {
1109
- return this.evaluateLiteralFromPostfix(postfixExpr, isNegative);
907
+ return TypeValidator._evaluateLiteralFromPostfix(postfixExpr, isNegative);
1110
908
  }
1111
909
 
1112
- // Recursive unary
1113
910
  const nestedUnary = ctx.unaryExpression();
1114
911
  if (nestedUnary) {
1115
- const nestedValue = this.evaluateUnaryExpression(nestedUnary);
912
+ const nestedValue = TypeValidator._evaluateUnaryExpression(nestedUnary);
1116
913
  return LiteralEvaluator.applySign(nestedValue, isNegative);
1117
914
  }
1118
915
 
1119
916
  return null;
1120
917
  }
1121
918
 
1122
- /**
1123
- * Extract and evaluate a literal from a postfix expression.
1124
- */
1125
- private evaluateLiteralFromPostfix(
919
+ private static _evaluateLiteralFromPostfix(
1126
920
  postfixExpr: Parser.PostfixExpressionContext,
1127
921
  isNegative: boolean,
1128
922
  ): number | null {
@@ -1141,46 +935,30 @@ class TypeValidator {
1141
935
  // Integer Assignment Validation (ADR-024)
1142
936
  // ========================================================================
1143
937
 
1144
- /**
1145
- * ADR-024: Validate integer type conversions for assignments.
1146
- * - For literals: validates the value fits in the target type
1147
- * - For expressions: validates no narrowing or sign-changing conversions
1148
- *
1149
- * @param targetType - The target type (e.g., "u8", "i32")
1150
- * @param expressionText - The expression text (for literal detection)
1151
- * @param sourceType - The source type (for non-literal expressions), or null
1152
- * @param isCompound - Whether this is a compound assignment (skip validation)
1153
- */
1154
- validateIntegerAssignment(
938
+ static validateIntegerAssignment(
1155
939
  targetType: string,
1156
940
  expressionText: string,
1157
941
  sourceType: string | null,
1158
942
  isCompound: boolean,
1159
943
  ): void {
1160
- // Skip validation for compound assignments (+<-, -<-, etc.)
1161
- // since the operand doesn't need to fit directly in the target type
1162
944
  if (isCompound) {
1163
945
  return;
1164
946
  }
1165
947
 
1166
- // Only validate integer types
1167
- if (!this.typeResolver.isIntegerType(targetType)) {
948
+ if (!TypeResolver.isIntegerType(targetType)) {
1168
949
  return;
1169
950
  }
1170
951
 
1171
952
  const trimmed = expressionText.trim();
1172
953
 
1173
- // Check if it's a direct literal (decimal, hex, or binary)
1174
954
  const isDecimalLiteral = /^-?\d+$/.exec(trimmed);
1175
955
  const isHexLiteral = /^0[xX][0-9a-fA-F]+$/.exec(trimmed);
1176
956
  const isBinaryLiteral = /^0[bB][01]+$/.exec(trimmed);
1177
957
 
1178
958
  if (isDecimalLiteral || isHexLiteral || isBinaryLiteral) {
1179
- // Validate literal fits in target type
1180
- this.typeResolver.validateLiteralFitsType(trimmed, targetType);
959
+ TypeResolver.validateLiteralFitsType(trimmed, targetType);
1181
960
  } else {
1182
- // Not a literal - check for narrowing/sign conversions
1183
- this.typeResolver.validateTypeConversion(targetType, sourceType);
961
+ TypeResolver.validateTypeConversion(targetType, sourceType);
1184
962
  }
1185
963
  }
1186
964
  }