c-next 0.2.16 → 0.2.18

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 (118) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +8897 -6260
  3. package/dist/index.js.map +4 -4
  4. package/grammar/CNext.g4 +12 -0
  5. package/package.json +4 -2
  6. package/src/transpiler/Transpiler.ts +376 -48
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
  9. package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
  10. package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
  11. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
  12. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  13. package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
  14. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
  15. package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
  16. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  17. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  18. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  19. package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
  20. package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
  21. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  22. package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
  23. package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
  24. package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
  25. package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
  26. package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
  27. package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
  28. package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
  29. package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
  30. package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
  31. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  32. package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
  33. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  34. package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
  35. package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
  36. package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
  37. package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
  38. package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
  39. package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
  40. package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
  41. package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
  42. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
  43. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
  44. package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
  45. package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
  46. package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
  47. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
  48. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  49. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  50. package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
  51. package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
  52. package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
  53. package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
  54. package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
  55. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  56. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
  57. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  58. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
  59. package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
  60. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
  61. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
  62. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
  63. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
  64. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
  65. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
  66. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
  67. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
  68. package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
  69. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
  70. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
  71. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
  72. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
  73. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
  74. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  75. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  76. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  77. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
  78. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  79. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
  80. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  81. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  82. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
  83. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
  84. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
  85. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
  86. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  87. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  88. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
  89. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  90. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  91. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  92. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  93. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
  94. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  95. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  96. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
  97. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  98. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  99. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  100. package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
  101. package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
  102. package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
  103. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  104. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  105. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  106. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
  107. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  108. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  109. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  110. package/src/transpiler/state/CodeGenState.ts +91 -22
  111. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  112. package/src/transpiler/types/ICachedFileEntry.ts +7 -0
  113. package/src/utils/LiteralUtils.ts +23 -0
  114. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  115. package/src/utils/cache/CacheManager.ts +13 -2
  116. package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
  117. package/src/utils/constants/TypeConstants.ts +13 -0
  118. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -310,9 +310,12 @@ describe("StringDeclHelper", () => {
310
310
  });
311
311
 
312
312
  it("allows assignment when source capacity fits", () => {
313
+ // Issue #1044: String variable initialization uses a bounded strncpy
314
+ // (shared with the reassignment path), not an unsafe strcpy (CWE-120).
313
315
  const callbacks = {
314
316
  ...defaultCallbacks,
315
317
  getStringExprCapacity: vi.fn(() => 20),
318
+ generateExpression: vi.fn(() => "smallString"),
316
319
  };
317
320
 
318
321
  const typeCtx = {
@@ -336,7 +339,82 @@ describe("StringDeclHelper", () => {
336
339
  );
337
340
 
338
341
  expect(result.handled).toBe(true);
339
- expect(result.code).toContain("char dest[33] = smallString;");
342
+ // Issue #1044: bounded copy, not strcpy
343
+ expect(result.code).toContain('char dest[33] = "";');
344
+ expect(result.code).toContain("strncpy(dest, smallString, 32);");
345
+ expect(result.code).toContain("dest[32] = '\\0';");
346
+ expect(result.code).not.toContain("strcpy(dest, smallString)");
347
+ });
348
+
349
+ it("does not indent continuation lines (the block emitter owns indentation) — Issue #1037", () => {
350
+ // indentLevel = 1 (beforeEach). generateBlock prefixes every line of a
351
+ // statement, so the helper must NOT add its own indent or continuation
352
+ // lines double-indent.
353
+ const callbacks = {
354
+ ...defaultCallbacks,
355
+ getStringExprCapacity: vi.fn(() => 20),
356
+ generateExpression: vi.fn(() => "smallString"),
357
+ };
358
+
359
+ const typeCtx = {
360
+ stringType: () => ({
361
+ INTEGER_LITERAL: () => ({ getText: () => "32" }),
362
+ }),
363
+ } as never;
364
+
365
+ const expression = {
366
+ getText: () => "smallString",
367
+ } as never;
368
+
369
+ const result = StringDeclHelper.generateStringDecl(
370
+ typeCtx,
371
+ "dest",
372
+ expression,
373
+ [],
374
+ { extern: "", const: "", atomic: "", volatile: "" },
375
+ false,
376
+ callbacks,
377
+ );
378
+
379
+ const lines = result.code.split("\n");
380
+ expect(lines[0]).toBe('char dest[33] = "";');
381
+ // Continuation line must have no leading whitespace of its own.
382
+ expect(lines[1]).toBe(
383
+ "strncpy(dest, smallString, 32); dest[32] = '\\0';",
384
+ );
385
+ });
386
+
387
+ it("throws error for string variable initialization at global scope", () => {
388
+ // Issue #1030: String variable initialization requires function body
389
+ CodeGenState.inFunctionBody = false;
390
+ const callbacks = {
391
+ ...defaultCallbacks,
392
+ getStringExprCapacity: vi.fn(() => 20),
393
+ };
394
+
395
+ const typeCtx = {
396
+ stringType: () => ({
397
+ INTEGER_LITERAL: () => ({ getText: () => "32" }),
398
+ }),
399
+ } as never;
400
+
401
+ const expression = {
402
+ getText: () => "sourceVar",
403
+ } as never;
404
+
405
+ expect(() =>
406
+ StringDeclHelper.generateStringDecl(
407
+ typeCtx,
408
+ "dest",
409
+ expression,
410
+ [],
411
+ { extern: "", const: "", atomic: "", volatile: "" },
412
+ false,
413
+ callbacks,
414
+ ),
415
+ ).toThrow(
416
+ "String initialization from variable cannot be used at global scope",
417
+ );
340
418
  });
341
419
  });
342
420
 
@@ -374,12 +452,15 @@ describe("StringDeclHelper", () => {
374
452
  );
375
453
 
376
454
  expect(result.handled).toBe(true);
377
- expect(result.code).toContain('char combined[33] = "";');
378
- expect(result.code).toContain("strncpy(combined, str1, 32)");
379
- expect(result.code).toContain(
380
- "strncat(combined, str2, 32 - strlen(combined))",
455
+ // Issue #1037: every line carries no indent of its own (the block emitter
456
+ // prefixes each line); continuation lines must not double-indent.
457
+ const concatLines = result.code.split("\n");
458
+ expect(concatLines[0]).toBe('char combined[33] = "";');
459
+ expect(concatLines[1]).toBe("strncpy(combined, str1, 32);");
460
+ expect(concatLines[2]).toBe(
461
+ "strncat(combined, str2, 32 - strlen(combined));",
381
462
  );
382
- expect(result.code).toContain("combined[32] = '\\0';");
463
+ expect(concatLines[3]).toBe("combined[32] = '\\0';");
383
464
  });
384
465
 
385
466
  it("throws error for concatenation at global scope", () => {
@@ -523,9 +604,11 @@ describe("StringDeclHelper", () => {
523
604
  );
524
605
 
525
606
  expect(result.handled).toBe(true);
526
- expect(result.code).toContain('char sub[11] = "";');
527
- expect(result.code).toContain("strncpy(sub, srcStr + 0, 5)");
528
- expect(result.code).toContain("sub[5] = '\\0';");
607
+ // Issue #1037: continuation lines carry no indent of their own.
608
+ const subLines = result.code.split("\n");
609
+ expect(subLines[0]).toBe('char sub[11] = "";');
610
+ expect(subLines[1]).toBe("strncpy(sub, srcStr + 0, 5);");
611
+ expect(subLines[2]).toBe("sub[5] = '\\0';");
529
612
  });
530
613
 
531
614
  it("throws error for substring at global scope", () => {
@@ -1154,4 +1237,234 @@ describe("StringDeclHelper", () => {
1154
1237
  expect(CodeGenState.localArrays.has("tracked")).toBe(true);
1155
1238
  });
1156
1239
  });
1240
+
1241
+ describe("string arrays from arrayType syntax (Issue #1029)", () => {
1242
+ it("generates string array from arrayType without initializer", () => {
1243
+ // Simulates: string<32>[4] items;
1244
+ const typeCtx = {
1245
+ stringType: () => null, // Not directly on typeCtx
1246
+ arrayType: () => ({
1247
+ stringType: () => ({
1248
+ INTEGER_LITERAL: () => ({ getText: () => "32" }),
1249
+ }),
1250
+ arrayTypeDimension: () => [
1251
+ { expression: () => ({ getText: () => "4" }) },
1252
+ ],
1253
+ }),
1254
+ } as never;
1255
+
1256
+ const result = StringDeclHelper.generateStringDecl(
1257
+ typeCtx,
1258
+ "items",
1259
+ null,
1260
+ [],
1261
+ { extern: "", const: "", atomic: "", volatile: "" },
1262
+ false,
1263
+ defaultCallbacks,
1264
+ );
1265
+
1266
+ expect(result.handled).toBe(true);
1267
+ expect(result.code).toBe("char items[4][33] = {0};");
1268
+ });
1269
+
1270
+ it("generates string array from arrayType with initializer", () => {
1271
+ const callbacks = {
1272
+ ...defaultCallbacks,
1273
+ generateExpression: vi.fn(() => {
1274
+ CodeGenState.lastArrayInitCount = 2;
1275
+ CodeGenState.lastArrayFillValue = undefined;
1276
+ return '{"One", "Two"}';
1277
+ }),
1278
+ };
1279
+
1280
+ // Simulates: string<10>[2] labels <- ["One", "Two"];
1281
+ const typeCtx = {
1282
+ stringType: () => null,
1283
+ arrayType: () => ({
1284
+ stringType: () => ({
1285
+ INTEGER_LITERAL: () => ({ getText: () => "10" }),
1286
+ }),
1287
+ arrayTypeDimension: () => [
1288
+ { expression: () => ({ getText: () => "2" }) },
1289
+ ],
1290
+ }),
1291
+ } as never;
1292
+
1293
+ const expression = {
1294
+ getText: () => '["One", "Two"]',
1295
+ } as never;
1296
+
1297
+ const result = StringDeclHelper.generateStringDecl(
1298
+ typeCtx,
1299
+ "labels",
1300
+ expression,
1301
+ [],
1302
+ { extern: "", const: "", atomic: "", volatile: "" },
1303
+ false,
1304
+ callbacks,
1305
+ );
1306
+
1307
+ expect(result.handled).toBe(true);
1308
+ expect(result.code).toContain("[2]");
1309
+ expect(result.code).toContain("[11]"); // capacity + 1
1310
+ expect(result.code).toContain('{"One", "Two"}');
1311
+ });
1312
+
1313
+ it("generates string array from arrayType with trailing dimensions", () => {
1314
+ // Simulates: string<10>[2] matrix[3]; (2D string array)
1315
+ const typeCtx = {
1316
+ stringType: () => null,
1317
+ arrayType: () => ({
1318
+ stringType: () => ({
1319
+ INTEGER_LITERAL: () => ({ getText: () => "10" }),
1320
+ }),
1321
+ arrayTypeDimension: () => [
1322
+ { expression: () => ({ getText: () => "2" }) },
1323
+ ],
1324
+ }),
1325
+ } as never;
1326
+
1327
+ const trailingDims = [
1328
+ { expression: () => ({ getText: () => "3" }) },
1329
+ ] as never[];
1330
+
1331
+ const result = StringDeclHelper.generateStringDecl(
1332
+ typeCtx,
1333
+ "matrix",
1334
+ null,
1335
+ trailingDims,
1336
+ { extern: "", const: "", atomic: "", volatile: "" },
1337
+ false,
1338
+ defaultCallbacks,
1339
+ );
1340
+
1341
+ expect(result.handled).toBe(true);
1342
+ expect(result.code).toBe("char matrix[2][3][11] = {0};");
1343
+ });
1344
+
1345
+ it("throws error for unsized string array from arrayType", () => {
1346
+ // Simulates: string[4] items; (missing capacity)
1347
+ const typeCtx = {
1348
+ stringType: () => null,
1349
+ arrayType: () => ({
1350
+ stringType: () => ({
1351
+ INTEGER_LITERAL: () => null, // No capacity
1352
+ }),
1353
+ arrayTypeDimension: () => [
1354
+ { expression: () => ({ getText: () => "4" }) },
1355
+ ],
1356
+ }),
1357
+ } as never;
1358
+
1359
+ expect(() =>
1360
+ StringDeclHelper.generateStringDecl(
1361
+ typeCtx,
1362
+ "items",
1363
+ null,
1364
+ [],
1365
+ { extern: "", const: "", atomic: "", volatile: "" },
1366
+ false,
1367
+ defaultCallbacks,
1368
+ ),
1369
+ ).toThrow("String arrays require explicit capacity");
1370
+ });
1371
+
1372
+ it("validates element count matches declared size from arrayType", () => {
1373
+ const callbacks = {
1374
+ ...defaultCallbacks,
1375
+ generateExpression: vi.fn(() => {
1376
+ CodeGenState.lastArrayInitCount = 2; // Only 2 elements
1377
+ CodeGenState.lastArrayFillValue = undefined;
1378
+ return '{"One", "Two"}';
1379
+ }),
1380
+ };
1381
+
1382
+ // Simulates: string<10>[4] items <- ["One", "Two"]; (size mismatch)
1383
+ const typeCtx = {
1384
+ stringType: () => null,
1385
+ arrayType: () => ({
1386
+ stringType: () => ({
1387
+ INTEGER_LITERAL: () => ({ getText: () => "10" }),
1388
+ }),
1389
+ arrayTypeDimension: () => [
1390
+ { expression: () => ({ getText: () => "4" }) }, // Declared size = 4
1391
+ ],
1392
+ }),
1393
+ } as never;
1394
+
1395
+ const expression = {
1396
+ getText: () => '["One", "Two"]',
1397
+ } as never;
1398
+
1399
+ expect(() =>
1400
+ StringDeclHelper.generateStringDecl(
1401
+ typeCtx,
1402
+ "items",
1403
+ expression,
1404
+ [],
1405
+ { extern: "", const: "", atomic: "", volatile: "" },
1406
+ false,
1407
+ callbacks,
1408
+ ),
1409
+ ).toThrow("Array size mismatch - declared [4] but got 2 elements");
1410
+ });
1411
+
1412
+ it("tracks local arrays from arrayType in localArrays set", () => {
1413
+ const typeCtx = {
1414
+ stringType: () => null,
1415
+ arrayType: () => ({
1416
+ stringType: () => ({
1417
+ INTEGER_LITERAL: () => ({ getText: () => "20" }),
1418
+ }),
1419
+ arrayTypeDimension: () => [
1420
+ { expression: () => ({ getText: () => "3" }) },
1421
+ ],
1422
+ }),
1423
+ } as never;
1424
+
1425
+ StringDeclHelper.generateStringDecl(
1426
+ typeCtx,
1427
+ "tracked",
1428
+ null,
1429
+ [],
1430
+ { extern: "", const: "", atomic: "", volatile: "" },
1431
+ false,
1432
+ defaultCallbacks,
1433
+ );
1434
+
1435
+ expect(CodeGenState.localArrays.has("tracked")).toBe(true);
1436
+ });
1437
+
1438
+ it("generates string array from arrayType with modifiers", () => {
1439
+ const typeCtx = {
1440
+ stringType: () => null,
1441
+ arrayType: () => ({
1442
+ stringType: () => ({
1443
+ INTEGER_LITERAL: () => ({ getText: () => "8" }),
1444
+ }),
1445
+ arrayTypeDimension: () => [
1446
+ { expression: () => ({ getText: () => "2" }) },
1447
+ ],
1448
+ }),
1449
+ } as never;
1450
+
1451
+ const result = StringDeclHelper.generateStringDecl(
1452
+ typeCtx,
1453
+ "data",
1454
+ null,
1455
+ [],
1456
+ {
1457
+ extern: "extern ",
1458
+ const: "const ",
1459
+ atomic: "",
1460
+ volatile: "volatile ",
1461
+ },
1462
+ true,
1463
+ defaultCallbacks,
1464
+ );
1465
+
1466
+ expect(result.handled).toBe(true);
1467
+ expect(result.code).toContain("extern const volatile char data[2][9]");
1468
+ });
1469
+ });
1157
1470
  });
@@ -203,70 +203,6 @@ describe("SymbolLookupHelper", () => {
203
203
  });
204
204
  });
205
205
 
206
- describe("isCppType", () => {
207
- it("returns false when symbolTable is null", () => {
208
- expect(SymbolLookupHelper.isCppType(null, "MyType")).toBe(false);
209
- });
210
-
211
- it("returns false when symbolTable is undefined", () => {
212
- expect(SymbolLookupHelper.isCppType(undefined, "MyType")).toBe(false);
213
- });
214
-
215
- it("returns true for C++ struct", () => {
216
- const mockTable = {
217
- getOverloads: () => [
218
- { kind: "struct" as const, sourceLanguage: ESourceLanguage.Cpp },
219
- ],
220
- };
221
- expect(SymbolLookupHelper.isCppType(mockTable, "MyStruct")).toBe(true);
222
- });
223
-
224
- it("returns true for C++ class", () => {
225
- const mockTable = {
226
- getOverloads: () => [
227
- { kind: "class" as const, sourceLanguage: ESourceLanguage.Cpp },
228
- ],
229
- };
230
- expect(SymbolLookupHelper.isCppType(mockTable, "MyClass")).toBe(true);
231
- });
232
-
233
- it("returns true for C++ type alias", () => {
234
- const mockTable = {
235
- getOverloads: () => [
236
- { kind: "type" as const, sourceLanguage: ESourceLanguage.Cpp },
237
- ],
238
- };
239
- expect(SymbolLookupHelper.isCppType(mockTable, "MyType")).toBe(true);
240
- });
241
-
242
- it("returns false for C struct", () => {
243
- const mockTable = {
244
- getOverloads: () => [
245
- { kind: "struct" as const, sourceLanguage: ESourceLanguage.C },
246
- ],
247
- };
248
- expect(SymbolLookupHelper.isCppType(mockTable, "MyStruct")).toBe(false);
249
- });
250
-
251
- it("returns false for C++ function", () => {
252
- const mockTable = {
253
- getOverloads: () => [
254
- { kind: "function" as const, sourceLanguage: ESourceLanguage.Cpp },
255
- ],
256
- };
257
- expect(SymbolLookupHelper.isCppType(mockTable, "myFunc")).toBe(false);
258
- });
259
-
260
- it("returns false for C++ enum", () => {
261
- const mockTable = {
262
- getOverloads: () => [
263
- { kind: "enum" as const, sourceLanguage: ESourceLanguage.Cpp },
264
- ],
265
- };
266
- expect(SymbolLookupHelper.isCppType(mockTable, "MyEnum")).toBe(false);
267
- });
268
- });
269
-
270
206
  describe("isCNextFunction", () => {
271
207
  it("returns false when symbolTable is null", () => {
272
208
  expect(SymbolLookupHelper.isCNextFunction(null, "myFunc")).toBe(false);
@@ -183,5 +183,106 @@ describe("TypeRegistrationEngine", () => {
183
183
 
184
184
  expect(mockCallbacks.requireInclude).toHaveBeenCalledWith("string");
185
185
  });
186
+
187
+ it("registers string array types with correct dimensions (Issue #1029)", () => {
188
+ const source = `string<32>[4] items;`;
189
+ const tree = CNextSourceParser.parse(source).tree;
190
+
191
+ TypeRegistrationEngine.register(tree, mockCallbacks);
192
+
193
+ const info = CodeGenState.getVariableTypeInfo("items");
194
+ expect(info).not.toBeNull();
195
+ expect(info?.baseType).toBe("char");
196
+ expect(info?.isArray).toBe(true);
197
+ expect(info?.isString).toBe(true);
198
+ expect(info?.stringCapacity).toBe(32);
199
+ // Dimensions: [4] for array, [33] for string capacity + null terminator
200
+ expect(info?.arrayDimensions).toEqual([4, 33]);
201
+ });
202
+
203
+ it("registers multi-dimensional string arrays (Issue #1029)", () => {
204
+ const source = `string<16>[2][3] matrix;`;
205
+ const tree = CNextSourceParser.parse(source).tree;
206
+
207
+ TypeRegistrationEngine.register(tree, mockCallbacks);
208
+
209
+ const info = CodeGenState.getVariableTypeInfo("matrix");
210
+ expect(info).not.toBeNull();
211
+ expect(info?.baseType).toBe("char");
212
+ expect(info?.isArray).toBe(true);
213
+ expect(info?.isString).toBe(true);
214
+ expect(info?.stringCapacity).toBe(16);
215
+ // Dimensions: [2][3] for array, [17] for string capacity + null terminator
216
+ expect(info?.arrayDimensions).toEqual([2, 3, 17]);
217
+ });
218
+
219
+ it("requires string include for string array types (Issue #1029)", () => {
220
+ const source = `string<32>[4] items;`;
221
+ const tree = CNextSourceParser.parse(source).tree;
222
+
223
+ TypeRegistrationEngine.register(tree, mockCallbacks);
224
+
225
+ expect(mockCallbacks.requireInclude).toHaveBeenCalledWith("string");
226
+ });
227
+
228
+ it("registers global type arrays", () => {
229
+ // Set up a scope context to test global.Type[N] pattern
230
+ CodeGenState.currentScope = "Motor";
231
+
232
+ const source = `
233
+ scope Motor {
234
+ enum State { OFF, ON }
235
+ global.State[3] states;
236
+ }
237
+ `;
238
+ const tree = CNextSourceParser.parse(source).tree;
239
+
240
+ TypeRegistrationEngine.register(tree, mockCallbacks);
241
+
242
+ const info = CodeGenState.getVariableTypeInfo("Motor_states");
243
+ expect(info).not.toBeNull();
244
+ expect(info?.baseType).toBe("State");
245
+ expect(info?.isArray).toBe(true);
246
+
247
+ CodeGenState.currentScope = null;
248
+ });
249
+
250
+ it("registers qualified type arrays (Scope.Type[N])", () => {
251
+ const source = `
252
+ scope Motor {
253
+ public enum State { OFF, ON }
254
+ }
255
+ Motor.State[4] allStates;
256
+ `;
257
+ const tree = CNextSourceParser.parse(source).tree;
258
+
259
+ TypeRegistrationEngine.register(tree, mockCallbacks);
260
+
261
+ const info = CodeGenState.getVariableTypeInfo("allStates");
262
+ expect(info).not.toBeNull();
263
+ expect(info?.baseType).toBe("Motor_State");
264
+ expect(info?.isArray).toBe(true);
265
+ });
266
+
267
+ it("registers scoped type arrays (this.Type[N])", () => {
268
+ CodeGenState.currentScope = "Motor";
269
+
270
+ const source = `
271
+ scope Motor {
272
+ enum State { OFF, ON }
273
+ this.State[2] localStates;
274
+ }
275
+ `;
276
+ const tree = CNextSourceParser.parse(source).tree;
277
+
278
+ TypeRegistrationEngine.register(tree, mockCallbacks);
279
+
280
+ const info = CodeGenState.getVariableTypeInfo("Motor_localStates");
281
+ expect(info).not.toBeNull();
282
+ expect(info?.baseType).toBe("Motor_State");
283
+ expect(info?.isArray).toBe(true);
284
+
285
+ CodeGenState.currentScope = null;
286
+ });
186
287
  });
187
288
  });
@@ -127,8 +127,8 @@ describe("VariableDeclHelper", () => {
127
127
  }).not.toThrow();
128
128
  });
129
129
 
130
- it("allows empty dimension for size inference", () => {
131
- const varDecl = parseVarDecl("u8 arr[] <- [1, 2, 3];");
130
+ it("allows empty dimension for size inference in arrayType", () => {
131
+ const varDecl = parseVarDecl("u8[] arr <- [1, 2, 3];");
132
132
  const typeCtx = varDecl.type();
133
133
  expect(() => {
134
134
  VariableDeclHelper.validateArrayDeclarationSyntax(
@@ -139,7 +139,19 @@ describe("VariableDeclHelper", () => {
139
139
  }).not.toThrow();
140
140
  });
141
141
 
142
- it("allows multi-dimensional C-style", () => {
142
+ it("rejects empty dimension with C-style trailing brackets (Issue #1017)", () => {
143
+ const varDecl = parseVarDecl("u8 arr[] <- [1, 2, 3];");
144
+ const typeCtx = varDecl.type();
145
+ expect(() => {
146
+ VariableDeclHelper.validateArrayDeclarationSyntax(
147
+ varDecl,
148
+ typeCtx,
149
+ "arr",
150
+ );
151
+ }).toThrow("C-style array declaration is not allowed");
152
+ });
153
+
154
+ it("rejects multi-dimensional C-style (Issue #1014)", () => {
143
155
  const varDecl = parseVarDecl("u8 matrix[4][4];");
144
156
  const typeCtx = varDecl.type();
145
157
  expect(() => {
@@ -148,10 +160,22 @@ describe("VariableDeclHelper", () => {
148
160
  typeCtx,
149
161
  "matrix",
150
162
  );
163
+ }).toThrow("C-style array declaration is not allowed");
164
+ });
165
+
166
+ it("allows string type with arrayType syntax", () => {
167
+ const varDecl = parseVarDecl("string<32>[4] names;");
168
+ const typeCtx = varDecl.type();
169
+ expect(() => {
170
+ VariableDeclHelper.validateArrayDeclarationSyntax(
171
+ varDecl,
172
+ typeCtx,
173
+ "names",
174
+ );
151
175
  }).not.toThrow();
152
176
  });
153
177
 
154
- it("allows string type with C-style", () => {
178
+ it("rejects string type with C-style trailing brackets (Issue #1016)", () => {
155
179
  const varDecl = parseVarDecl("string<32> names[4];");
156
180
  const typeCtx = varDecl.type();
157
181
  expect(() => {
@@ -160,7 +184,7 @@ describe("VariableDeclHelper", () => {
160
184
  typeCtx,
161
185
  "names",
162
186
  );
163
- }).not.toThrow();
187
+ }).toThrow("C-style array declaration is not allowed");
164
188
  });
165
189
 
166
190
  it("rejects C-style single dimension for primitives", () => {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Issue #579: Shared subscript classifier for array vs bit access
2
+ * Issue #579 / Issue #1100: Shared subscript classifier for array vs bit access
3
3
  *
4
4
  * This utility unifies the classification logic used by:
5
5
  * - AssignmentClassifier (assignment path)
@@ -7,8 +7,28 @@
7
7
  *
8
8
  * The classification rule is:
9
9
  * 1. If isArray or isString -> array access
10
- * 2. If isParameter && !isArray -> array access (parameter becomes pointer in C, ADR-006)
11
- * 3. Otherwise -> bit manipulation
10
+ * 2. Otherwise -> bit manipulation
11
+ *
12
+ * This rule is identical for parameters and local variables. ADR-006 requires
13
+ * array parameters to use explicit array syntax (`u8[8] buf`), which sets
14
+ * isArray on the parameter's type info exactly like a local array declaration
15
+ * does. A scalar-typed parameter (no array brackets) is therefore classified
16
+ * the same way a scalar local variable is: bit manipulation (ADR-007 — "Any
17
+ * integer type can be indexed to access individual bits", with no carve-out
18
+ * for parameters).
19
+ *
20
+ * Issue #1100: A prior version of this classifier treated ANY function
21
+ * parameter without explicit array syntax as array access (`isParameter &&
22
+ * !isArray -> array`), added in #579 to support buffer-style parameters
23
+ * declared without brackets (e.g. `void fillBuffer(u8 buf)`). That blanket
24
+ * rule was a divergent decision path from local-variable classification: it
25
+ * silently broke ADR-007 bit-indexing for scalar parameters (`u32 v; ...
26
+ * v[4]` inside a function became `v[4]` array-subscript C code on a pointer
27
+ * instead of a shift-and-mask bit read), producing invalid/incorrect C
28
+ * for the extremely common embedded pattern of reading a bit out of a
29
+ * hardware-register-shaped scalar parameter. Buffer-style parameters must now
30
+ * use explicit array syntax (`u8[N] buf`), which was already the ADR-006
31
+ * documented and supported spelling — see tests/params/param-array-indexing.test.cnx.
12
32
  */
13
33
  import TSubscriptKind from "./TSubscriptKind";
14
34
  import TTypeInfo from "../types/TTypeInfo";
@@ -63,9 +83,14 @@ class SubscriptClassifier {
63
83
  * Determine if a type should use array access semantics.
64
84
  *
65
85
  * Array access is used when:
66
- * - Type is explicitly an array (isArray: true)
86
+ * - Type is explicitly an array (isArray: true) — including array
87
+ * parameters declared with explicit syntax, e.g. `u8[8] buf` (ADR-006)
67
88
  * - Type is a string (strings are char arrays)
68
- * - Type is a non-array parameter (becomes pointer in C, ADR-006)
89
+ *
90
+ * Parameters are NOT special-cased here (Issue #1100): whether a subscript
91
+ * is array access or bit access depends solely on the declared type, the
92
+ * same rule used for local variables. A scalar parameter without array
93
+ * syntax is bit-indexable exactly like a scalar local variable (ADR-007).
69
94
  *
70
95
  * @param typeInfo - Type information, or null if unknown
71
96
  * @returns true if subscript should be treated as array access
@@ -82,12 +107,6 @@ class SubscriptClassifier {
82
107
  return true;
83
108
  }
84
109
 
85
- // Issue #579: Non-array parameter becomes pointer in C (ADR-006)
86
- // So buf[i] is array access, not bit access
87
- if (typeInfo.isParameter) {
88
- return true;
89
- }
90
-
91
110
  // Otherwise it's a scalar - use bit access
92
111
  return false;
93
112
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  type TSubscriptKind =
8
8
  | "array_element" // Single array element access: arr[i]
9
- | "array_slice" // Array slice/memcpy: arr[offset, length]
9
+ | "array_slice" // Array slice: arr[offset, length] (per-element little-endian writes, ADR-007/#1081)
10
10
  | "bit_single" // Single bit access: flags[3]
11
11
  | "bit_range"; // Bit range extraction: value[start, width]
12
12