c-next 0.2.16 → 0.2.17

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 (56) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1347 -414
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
  17. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -4
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  20. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  21. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  22. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  23. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  24. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  25. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  26. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  27. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  28. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  29. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  30. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  31. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  32. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  33. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  34. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  35. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  36. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  37. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  38. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  39. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  40. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  41. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  42. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  43. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  44. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  45. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  46. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  47. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  48. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  49. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  50. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  51. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  52. package/src/transpiler/state/CodeGenState.ts +71 -6
  53. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  54. package/src/utils/LiteralUtils.ts +23 -0
  55. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  56. package/src/utils/types/IParameterSymbol.ts +1 -0
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { hideBin } from "yargs/helpers";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "c-next",
14
- version: "0.2.16",
14
+ version: "0.2.17",
15
15
  description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
16
16
  packageManager: "npm@11.9.0",
17
17
  type: "module",
@@ -35,6 +35,10 @@ var package_default = {
35
35
  "test:update": "tsx scripts/test.ts --update",
36
36
  "integration:transpile": "tsx scripts/test.ts --transpile-only",
37
37
  "integration:execute": "tsx scripts/test.ts --execute-only",
38
+ "test:bugs": "tsx scripts/test.ts -- bugs",
39
+ "test:bugs:q": "tsx scripts/test.ts -- bugs -q",
40
+ "integration:bugs:transpile": "tsx scripts/test.ts -- bugs --transpile-only",
41
+ "integration:bugs:execute": "tsx scripts/test.ts -- bugs --execute-only",
38
42
  analyze: "./scripts/static-analysis.sh",
39
43
  clean: "rm -rf dist src/transpiler/logic/parser/grammar src/transpiler/logic/parser/c/grammar src/transpiler/logic/parser/cpp/grammar",
40
44
  "prettier:check": "prettier --check .",
@@ -16039,21 +16043,10 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16039
16043
  this.enterRule(localContext, 170, _CNextParser.RULE_arrayType);
16040
16044
  let _la;
16041
16045
  try {
16042
- this.state = 921;
16046
+ this.state = 939;
16043
16047
  this.errorHandler.sync(this);
16044
- switch (this.tokenStream.LA(1)) {
16045
- case _CNextParser.U8:
16046
- case _CNextParser.U16:
16047
- case _CNextParser.U32:
16048
- case _CNextParser.U64:
16049
- case _CNextParser.I8:
16050
- case _CNextParser.I16:
16051
- case _CNextParser.I32:
16052
- case _CNextParser.I64:
16053
- case _CNextParser.F32:
16054
- case _CNextParser.F64:
16055
- case _CNextParser.BOOL:
16056
- case _CNextParser.ISR_TYPE:
16048
+ switch (this.interpreter.adaptivePredict(this.tokenStream, 98, this.context)) {
16049
+ case 1:
16057
16050
  this.enterOuterAlt(localContext, 1);
16058
16051
  {
16059
16052
  this.state = 903;
@@ -16074,7 +16067,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16074
16067
  } while (_la === 100);
16075
16068
  }
16076
16069
  break;
16077
- case _CNextParser.IDENTIFIER:
16070
+ case 2:
16078
16071
  this.enterOuterAlt(localContext, 2);
16079
16072
  {
16080
16073
  this.state = 909;
@@ -16095,7 +16088,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16095
16088
  } while (_la === 100);
16096
16089
  }
16097
16090
  break;
16098
- case _CNextParser.STRING:
16091
+ case 3:
16099
16092
  this.enterOuterAlt(localContext, 3);
16100
16093
  {
16101
16094
  this.state = 915;
@@ -16116,8 +16109,69 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16116
16109
  } while (_la === 100);
16117
16110
  }
16118
16111
  break;
16119
- default:
16120
- throw new antlr2.NoViableAltException(this);
16112
+ case 4:
16113
+ this.enterOuterAlt(localContext, 4);
16114
+ {
16115
+ this.state = 921;
16116
+ this.scopedType();
16117
+ this.state = 923;
16118
+ this.errorHandler.sync(this);
16119
+ _la = this.tokenStream.LA(1);
16120
+ do {
16121
+ {
16122
+ {
16123
+ this.state = 922;
16124
+ this.arrayTypeDimension();
16125
+ }
16126
+ }
16127
+ this.state = 925;
16128
+ this.errorHandler.sync(this);
16129
+ _la = this.tokenStream.LA(1);
16130
+ } while (_la === 100);
16131
+ }
16132
+ break;
16133
+ case 5:
16134
+ this.enterOuterAlt(localContext, 5);
16135
+ {
16136
+ this.state = 927;
16137
+ this.qualifiedType();
16138
+ this.state = 929;
16139
+ this.errorHandler.sync(this);
16140
+ _la = this.tokenStream.LA(1);
16141
+ do {
16142
+ {
16143
+ {
16144
+ this.state = 928;
16145
+ this.arrayTypeDimension();
16146
+ }
16147
+ }
16148
+ this.state = 931;
16149
+ this.errorHandler.sync(this);
16150
+ _la = this.tokenStream.LA(1);
16151
+ } while (_la === 100);
16152
+ }
16153
+ break;
16154
+ case 6:
16155
+ this.enterOuterAlt(localContext, 6);
16156
+ {
16157
+ this.state = 933;
16158
+ this.globalType();
16159
+ this.state = 935;
16160
+ this.errorHandler.sync(this);
16161
+ _la = this.tokenStream.LA(1);
16162
+ do {
16163
+ {
16164
+ {
16165
+ this.state = 934;
16166
+ this.arrayTypeDimension();
16167
+ }
16168
+ }
16169
+ this.state = 937;
16170
+ this.errorHandler.sync(this);
16171
+ _la = this.tokenStream.LA(1);
16172
+ } while (_la === 100);
16173
+ }
16174
+ break;
16121
16175
  }
16122
16176
  } catch (re) {
16123
16177
  if (re instanceof antlr2.RecognitionException) {
@@ -16138,18 +16192,18 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16138
16192
  try {
16139
16193
  this.enterOuterAlt(localContext, 1);
16140
16194
  {
16141
- this.state = 923;
16195
+ this.state = 941;
16142
16196
  this.match(_CNextParser.LBRACKET);
16143
- this.state = 925;
16197
+ this.state = 943;
16144
16198
  this.errorHandler.sync(this);
16145
16199
  _la = this.tokenStream.LA(1);
16146
16200
  if ((_la & ~31) === 0 && (1 << _la & 2147532800) !== 0 || (_la - 32 & ~31) === 0 && (1 << _la - 32 & 1966091) !== 0 || (_la - 83 & ~31) === 0 && (1 << _la - 83 & 2130879681) !== 0) {
16147
16201
  {
16148
- this.state = 924;
16202
+ this.state = 942;
16149
16203
  this.expression();
16150
16204
  }
16151
16205
  }
16152
- this.state = 927;
16206
+ this.state = 945;
16153
16207
  this.match(_CNextParser.RBRACKET);
16154
16208
  }
16155
16209
  } catch (re) {
@@ -16171,7 +16225,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16171
16225
  try {
16172
16226
  this.enterOuterAlt(localContext, 1);
16173
16227
  {
16174
- this.state = 929;
16228
+ this.state = 947;
16175
16229
  _la = this.tokenStream.LA(1);
16176
16230
  if (!((_la - 31 & ~31) === 0 && (1 << _la - 31 & 3932167) !== 0 || (_la - 107 & ~31) === 0 && (1 << _la - 107 & 63) !== 0)) {
16177
16231
  this.errorHandler.recoverInline(this);
@@ -16196,7 +16250,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
16196
16250
  4,
16197
16251
  1,
16198
16252
  117,
16199
- 932,
16253
+ 950,
16200
16254
  2,
16201
16255
  0,
16202
16256
  7,
@@ -18171,9 +18225,51 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
18171
18225
  12,
18172
18226
  85,
18173
18227
  919,
18228
+ 1,
18229
+ 85,
18230
+ 1,
18231
+ 85,
18232
+ 4,
18233
+ 85,
18234
+ 924,
18235
+ 8,
18236
+ 85,
18237
+ 11,
18238
+ 85,
18239
+ 12,
18240
+ 85,
18241
+ 925,
18242
+ 1,
18243
+ 85,
18244
+ 1,
18245
+ 85,
18246
+ 4,
18247
+ 85,
18248
+ 930,
18249
+ 8,
18250
+ 85,
18251
+ 11,
18252
+ 85,
18253
+ 12,
18254
+ 85,
18255
+ 931,
18256
+ 1,
18257
+ 85,
18258
+ 1,
18259
+ 85,
18260
+ 4,
18261
+ 85,
18262
+ 936,
18263
+ 8,
18264
+ 85,
18265
+ 11,
18266
+ 85,
18267
+ 12,
18268
+ 85,
18269
+ 937,
18174
18270
  3,
18175
18271
  85,
18176
- 922,
18272
+ 940,
18177
18273
  8,
18178
18274
  85,
18179
18275
  1,
@@ -18182,7 +18278,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
18182
18278
  86,
18183
18279
  3,
18184
18280
  86,
18185
- 926,
18281
+ 944,
18186
18282
  8,
18187
18283
  86,
18188
18284
  1,
@@ -18348,7 +18444,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
18348
18444
  52,
18349
18445
  107,
18350
18446
  112,
18351
- 988,
18447
+ 1012,
18352
18448
  0,
18353
18449
  180,
18354
18450
  1,
@@ -18860,19 +18956,19 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
18860
18956
  0,
18861
18957
  0,
18862
18958
  170,
18863
- 921,
18959
+ 939,
18864
18960
  1,
18865
18961
  0,
18866
18962
  0,
18867
18963
  0,
18868
18964
  172,
18869
- 923,
18965
+ 941,
18870
18966
  1,
18871
18967
  0,
18872
18968
  0,
18873
18969
  0,
18874
18970
  174,
18875
- 929,
18971
+ 947,
18876
18972
  1,
18877
18973
  0,
18878
18974
  0,
@@ -24110,7 +24206,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
24110
24206
  0,
24111
24207
  0,
24112
24208
  908,
24113
- 922,
24209
+ 940,
24114
24210
  1,
24115
24211
  0,
24116
24212
  0,
@@ -24152,7 +24248,7 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
24152
24248
  0,
24153
24249
  0,
24154
24250
  914,
24155
- 922,
24251
+ 940,
24156
24252
  1,
24157
24253
  0,
24158
24254
  0,
@@ -24194,90 +24290,234 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
24194
24290
  0,
24195
24291
  0,
24196
24292
  920,
24197
- 922,
24293
+ 940,
24198
24294
  1,
24199
24295
  0,
24200
24296
  0,
24201
24297
  0,
24202
24298
  921,
24299
+ 923,
24300
+ 3,
24301
+ 152,
24302
+ 76,
24303
+ 0,
24304
+ 922,
24305
+ 924,
24306
+ 3,
24307
+ 172,
24308
+ 86,
24309
+ 0,
24310
+ 923,
24311
+ 922,
24312
+ 1,
24313
+ 0,
24314
+ 0,
24315
+ 0,
24316
+ 924,
24317
+ 925,
24318
+ 1,
24319
+ 0,
24320
+ 0,
24321
+ 0,
24322
+ 925,
24323
+ 923,
24324
+ 1,
24325
+ 0,
24326
+ 0,
24327
+ 0,
24328
+ 925,
24329
+ 926,
24330
+ 1,
24331
+ 0,
24332
+ 0,
24333
+ 0,
24334
+ 926,
24335
+ 940,
24336
+ 1,
24337
+ 0,
24338
+ 0,
24339
+ 0,
24340
+ 927,
24341
+ 929,
24342
+ 3,
24343
+ 156,
24344
+ 78,
24345
+ 0,
24346
+ 928,
24347
+ 930,
24348
+ 3,
24349
+ 172,
24350
+ 86,
24351
+ 0,
24352
+ 929,
24353
+ 928,
24354
+ 1,
24355
+ 0,
24356
+ 0,
24357
+ 0,
24358
+ 930,
24359
+ 931,
24360
+ 1,
24361
+ 0,
24362
+ 0,
24363
+ 0,
24364
+ 931,
24365
+ 929,
24366
+ 1,
24367
+ 0,
24368
+ 0,
24369
+ 0,
24370
+ 931,
24371
+ 932,
24372
+ 1,
24373
+ 0,
24374
+ 0,
24375
+ 0,
24376
+ 932,
24377
+ 940,
24378
+ 1,
24379
+ 0,
24380
+ 0,
24381
+ 0,
24382
+ 933,
24383
+ 935,
24384
+ 3,
24385
+ 154,
24386
+ 77,
24387
+ 0,
24388
+ 934,
24389
+ 936,
24390
+ 3,
24391
+ 172,
24392
+ 86,
24393
+ 0,
24394
+ 935,
24395
+ 934,
24396
+ 1,
24397
+ 0,
24398
+ 0,
24399
+ 0,
24400
+ 936,
24401
+ 937,
24402
+ 1,
24403
+ 0,
24404
+ 0,
24405
+ 0,
24406
+ 937,
24407
+ 935,
24408
+ 1,
24409
+ 0,
24410
+ 0,
24411
+ 0,
24412
+ 937,
24413
+ 938,
24414
+ 1,
24415
+ 0,
24416
+ 0,
24417
+ 0,
24418
+ 938,
24419
+ 940,
24420
+ 1,
24421
+ 0,
24422
+ 0,
24423
+ 0,
24424
+ 939,
24203
24425
  903,
24204
24426
  1,
24205
24427
  0,
24206
24428
  0,
24207
24429
  0,
24208
- 921,
24430
+ 939,
24209
24431
  909,
24210
24432
  1,
24211
24433
  0,
24212
24434
  0,
24213
24435
  0,
24214
- 921,
24436
+ 939,
24215
24437
  915,
24216
24438
  1,
24217
24439
  0,
24218
24440
  0,
24219
24441
  0,
24220
- 922,
24442
+ 939,
24443
+ 921,
24444
+ 1,
24445
+ 0,
24446
+ 0,
24447
+ 0,
24448
+ 939,
24449
+ 927,
24450
+ 1,
24451
+ 0,
24452
+ 0,
24453
+ 0,
24454
+ 939,
24455
+ 933,
24456
+ 1,
24457
+ 0,
24458
+ 0,
24459
+ 0,
24460
+ 940,
24221
24461
  171,
24222
24462
  1,
24223
24463
  0,
24224
24464
  0,
24225
24465
  0,
24226
- 923,
24227
- 925,
24466
+ 941,
24467
+ 943,
24228
24468
  5,
24229
24469
  100,
24230
24470
  0,
24231
24471
  0,
24232
- 924,
24233
- 926,
24472
+ 942,
24473
+ 944,
24234
24474
  3,
24235
24475
  102,
24236
24476
  51,
24237
24477
  0,
24238
- 925,
24239
- 924,
24478
+ 943,
24479
+ 942,
24240
24480
  1,
24241
24481
  0,
24242
24482
  0,
24243
24483
  0,
24244
- 925,
24245
- 926,
24484
+ 943,
24485
+ 944,
24246
24486
  1,
24247
24487
  0,
24248
24488
  0,
24249
24489
  0,
24250
- 926,
24251
- 927,
24490
+ 944,
24491
+ 945,
24252
24492
  1,
24253
24493
  0,
24254
24494
  0,
24255
24495
  0,
24256
- 927,
24257
- 928,
24496
+ 945,
24497
+ 946,
24258
24498
  5,
24259
24499
  101,
24260
24500
  0,
24261
24501
  0,
24262
- 928,
24502
+ 946,
24263
24503
  173,
24264
24504
  1,
24265
24505
  0,
24266
24506
  0,
24267
24507
  0,
24268
- 929,
24269
- 930,
24508
+ 947,
24509
+ 948,
24270
24510
  7,
24271
24511
  13,
24272
24512
  0,
24273
24513
  0,
24274
- 930,
24514
+ 948,
24275
24515
  175,
24276
24516
  1,
24277
24517
  0,
24278
24518
  0,
24279
24519
  0,
24280
- 97,
24520
+ 100,
24281
24521
  178,
24282
24522
  180,
24283
24523
  186,
@@ -24373,8 +24613,11 @@ var CNextParser = class _CNextParser extends antlr2.Parser {
24373
24613
  907,
24374
24614
  913,
24375
24615
  919,
24376
- 921,
24377
- 925
24616
+ 925,
24617
+ 931,
24618
+ 937,
24619
+ 939,
24620
+ 943
24378
24621
  ];
24379
24622
  static __ATN;
24380
24623
  static get _ATN() {
@@ -27875,6 +28118,15 @@ var ArrayTypeContext = class extends antlr2.ParserRuleContext {
27875
28118
  stringType() {
27876
28119
  return this.getRuleContext(0, StringTypeContext);
27877
28120
  }
28121
+ scopedType() {
28122
+ return this.getRuleContext(0, ScopedTypeContext);
28123
+ }
28124
+ qualifiedType() {
28125
+ return this.getRuleContext(0, QualifiedTypeContext);
28126
+ }
28127
+ globalType() {
28128
+ return this.getRuleContext(0, GlobalTypeContext);
28129
+ }
27878
28130
  get ruleIndex() {
27879
28131
  return CNextParser.RULE_arrayType;
27880
28132
  }
@@ -111383,7 +111635,7 @@ var ESourceLanguage = /* @__PURE__ */ ((ESourceLanguage2) => {
111383
111635
  var ESourceLanguage_default = ESourceLanguage;
111384
111636
 
111385
111637
  // src/utils/LiteralUtils.ts
111386
- var LiteralUtils = class {
111638
+ var LiteralUtils = class _LiteralUtils {
111387
111639
  /**
111388
111640
  * Check if a literal represents zero.
111389
111641
  *
@@ -111418,8 +111670,25 @@ var LiteralUtils = class {
111418
111670
  if (ctx.SUFFIXED_BINARY()) {
111419
111671
  return text.startsWith("0b0u") || text.startsWith("0b0i") || text.startsWith("0B0u") || text.startsWith("0B0i");
111420
111672
  }
111673
+ if (ctx.FLOAT_LITERAL()) {
111674
+ return _LiteralUtils.isFloatZero(text);
111675
+ }
111421
111676
  return false;
111422
111677
  }
111678
+ /**
111679
+ * Check if a float literal string represents zero.
111680
+ * Issue #1010: Detect float zero for division-by-zero checking.
111681
+ *
111682
+ * Handles: 0.0, .0, 0., 0.0f, 0.0F, 0.0e0, 0.0E0, etc.
111683
+ *
111684
+ * @param text - The float literal text
111685
+ * @returns true if the float is zero
111686
+ */
111687
+ static isFloatZero(text) {
111688
+ const withoutSuffix = text.replace(/[fF]$/, "");
111689
+ const value = Number.parseFloat(withoutSuffix);
111690
+ return value === 0;
111691
+ }
111423
111692
  /**
111424
111693
  * Check if a literal is a floating-point number.
111425
111694
  *
@@ -112539,12 +112808,23 @@ Rename the C-Next symbol to resolve.`
112539
112808
  * Issue #958: Check if a typedef aliases a struct type.
112540
112809
  * Used for scope variables, function parameters, and local variables
112541
112810
  * which should be pointers for C-header struct types.
112811
+ *
112812
+ * Issue #948: Performs query-time resolution - if the underlying struct
112813
+ * tag has a full body definition, this is NOT an external typedef struct
112814
+ * (it's a complete type that can use value semantics).
112815
+ *
112542
112816
  * @param typeName The type name to check
112543
112817
  * @returns true if this is a typedef'd struct type from C headers
112544
112818
  */
112545
112819
  isTypedefStructType(typeName) {
112546
- const result = this.structState.typedefStructTypes.has(typeName);
112547
- return result;
112820
+ if (!this.structState.typedefStructTypes.has(typeName)) {
112821
+ return false;
112822
+ }
112823
+ const tag = this.structState.typedefToTag.get(typeName);
112824
+ if (tag && this.structState.structTagsWithBodies.has(tag)) {
112825
+ return false;
112826
+ }
112827
+ return true;
112548
112828
  }
112549
112829
  /**
112550
112830
  * Issue #958: Get all typedef struct types for cache serialization.
@@ -112798,6 +113078,11 @@ var CodeGenState = class _CodeGenState {
112798
113078
  static indentLevel = 0;
112799
113079
  /** Whether we're inside a function body */
112800
113080
  static inFunctionBody = false;
113081
+ /** Whether we're generating the RHS of a variable declaration initializer.
113082
+ * When true, struct literals use { .field = value } instead of (Type){ .field = value }
113083
+ * because plain designated initializers are valid C99 at any scope, while compound
113084
+ * literals are not constant expressions and fail at file scope on GCC < 13. */
113085
+ static inDeclarationInit = false;
112801
113086
  /** Expected type for struct initializers and enum inference */
112802
113087
  static expectedType = null;
112803
113088
  /**
@@ -112910,6 +113195,7 @@ var CodeGenState = class _CodeGenState {
112910
113195
  this.floatShadowCurrent = /* @__PURE__ */ new Set();
112911
113196
  this.indentLevel = 0;
112912
113197
  this.inFunctionBody = false;
113198
+ this.inDeclarationInit = false;
112913
113199
  this.expectedType = null;
112914
113200
  this.suppressBareEnumResolution = false;
112915
113201
  this.mainArgsName = null;
@@ -112989,6 +113275,46 @@ var CodeGenState = class _CodeGenState {
112989
113275
  this.suppressBareEnumResolution = savedSuppress;
112990
113276
  }
112991
113277
  }
113278
+ /** Execute fn with inDeclarationInit=true, restoring prior value on exit. */
113279
+ static withDeclarationInit(fn) {
113280
+ const saved = this.inDeclarationInit;
113281
+ this.inDeclarationInit = true;
113282
+ try {
113283
+ return fn();
113284
+ } finally {
113285
+ this.inDeclarationInit = saved;
113286
+ }
113287
+ }
113288
+ /** Execute fn with inDeclarationInit=false, restoring prior value on exit.
113289
+ * Used in sub-expression contexts (function args, ternary arms) where
113290
+ * plain designated initializers are not valid C. */
113291
+ static withoutDeclarationInit(fn) {
113292
+ const saved = this.inDeclarationInit;
113293
+ this.inDeclarationInit = false;
113294
+ try {
113295
+ return fn();
113296
+ } finally {
113297
+ this.inDeclarationInit = saved;
113298
+ }
113299
+ }
113300
+ /**
113301
+ * Execute fn with expectedType=null, restoring prior value on exit.
113302
+ * Issue #1032: Used in comparison contexts (relational/equality expressions)
113303
+ * where MISRA 7.2 U suffix should NOT be applied - comparing `i32 < 0`
113304
+ * should not generate `signedIdx < 0U` which changes comparison semantics.
113305
+ */
113306
+ static withoutExpectedType(fn) {
113307
+ const savedType = this.expectedType;
113308
+ const savedSuppress = this.suppressBareEnumResolution;
113309
+ this.expectedType = null;
113310
+ this.suppressBareEnumResolution = false;
113311
+ try {
113312
+ return fn();
113313
+ } finally {
113314
+ this.expectedType = savedType;
113315
+ this.suppressBareEnumResolution = savedSuppress;
113316
+ }
113317
+ }
112992
113318
  // ===========================================================================
112993
113319
  // CONVENIENCE LOOKUP METHODS
112994
113320
  // ===========================================================================
@@ -113525,14 +113851,27 @@ var CodeGenState = class _CodeGenState {
113525
113851
  this.opaqueScopeVariables.add(qualifiedName);
113526
113852
  }
113527
113853
  /**
113528
- * Check if a scope variable has an opaque type (and is thus a pointer).
113529
- * Used during access generation to determine if & prefix is needed.
113854
+ * Check if generated code accesses an opaque scope variable (and is thus
113855
+ * already a pointer). Used during argument generation to decide whether an
113856
+ * address-of (&) prefix is needed.
113530
113857
  *
113531
- * @param qualifiedName - The fully qualified variable name (e.g., "MyScope_widget")
113532
- * @returns true if this is an opaque scope variable (already a pointer)
113858
+ * Handles two forms:
113859
+ * - Direct access: "MyScope_widget" → the handle itself (pointer)
113860
+ * - Array-element access: "MyScope_widgets[i]" → an element of an opaque
113861
+ * handle array, which is itself a pointer (Issue #996)
113862
+ *
113863
+ * @param generatedCode - The generated access expression (e.g. "UI_widgets[i]")
113864
+ * @returns true if this resolves to an opaque scope variable (already a pointer)
113533
113865
  */
113534
- static isOpaqueScopeVariable(qualifiedName) {
113535
- return this.opaqueScopeVariables.has(qualifiedName);
113866
+ static isOpaqueScopeVariableAccess(generatedCode) {
113867
+ if (this.opaqueScopeVariables.has(generatedCode)) {
113868
+ return true;
113869
+ }
113870
+ const bracketIndex = generatedCode.indexOf("[");
113871
+ if (bracketIndex === -1) {
113872
+ return false;
113873
+ }
113874
+ return this.opaqueScopeVariables.has(generatedCode.slice(0, bracketIndex));
113536
113875
  }
113537
113876
  // ===========================================================================
113538
113877
  // C++ MODE HELPERS
@@ -114442,41 +114781,73 @@ var TypeRegistrationEngine = class _TypeRegistrationEngine {
114442
114781
  });
114443
114782
  return true;
114444
114783
  }
114784
+ /**
114785
+ * Issue #1029: Register type info for string arrays (string<N>[M]).
114786
+ *
114787
+ * String arrays are parsed as arrayType with stringType inside:
114788
+ * arrayType -> stringType arrayTypeDimension+
114789
+ *
114790
+ * For `string<32>[4] items`:
114791
+ * - stringType gives capacity 32
114792
+ * - arrayTypeDimension gives [4]
114793
+ * - Result: char items[4][33] with dimensions [4, 33]
114794
+ */
114795
+ static _registerStringArrayType(registryName, arrayTypeCtx, arrayDim, isConst, overflowBehavior, isAtomic, callbacks) {
114796
+ const stringCtx = arrayTypeCtx.stringType();
114797
+ const intLiteral = stringCtx.INTEGER_LITERAL();
114798
+ if (!intLiteral) {
114799
+ return;
114800
+ }
114801
+ const capacity = Number.parseInt(intLiteral.getText(), 10);
114802
+ callbacks.requireInclude("string");
114803
+ const stringDim = capacity + 1;
114804
+ const arrayTypeDims = arrayTypeCtx.arrayTypeDimension().map((dim) => dim.expression()).filter((expr) => expr !== null).map((expr) => Number.parseInt(expr.getText(), 10)).filter((size) => !Number.isNaN(size));
114805
+ const additionalDims = ArrayDimensionParser_default.parseSimpleDimensions(arrayDim);
114806
+ const dimensions = [...arrayTypeDims, ...additionalDims, stringDim];
114807
+ CodeGenState.setVariableTypeInfo(registryName, {
114808
+ baseType: "char",
114809
+ bitWidth: 8,
114810
+ isArray: true,
114811
+ arrayDimensions: dimensions,
114812
+ isConst,
114813
+ isString: true,
114814
+ stringCapacity: capacity,
114815
+ overflowBehavior,
114816
+ isAtomic
114817
+ });
114818
+ }
114445
114819
  // ============================================================================
114446
114820
  // Array and standard type registration
114447
114821
  // ============================================================================
114448
114822
  static _registerArrayTypeVariable(registryName, arrayTypeCtx, arrayDim, isConst, overflowBehavior, isAtomic, callbacks) {
114449
- let baseType = "";
114450
- let bitWidth = 0;
114451
- if (arrayTypeCtx.primitiveType()) {
114452
- baseType = arrayTypeCtx.primitiveType().getText();
114453
- bitWidth = TYPE_WIDTH_default[baseType] || 0;
114454
- } else if (arrayTypeCtx.userType()) {
114455
- baseType = arrayTypeCtx.userType().getText();
114456
- const combinedArrayDim = arrayDim ?? [];
114457
- if (_TypeRegistrationEngine._tryRegisterEnumOrBitmapType(
114823
+ if (arrayTypeCtx.stringType()) {
114824
+ _TypeRegistrationEngine._registerStringArrayType(
114458
114825
  registryName,
114459
- baseType,
114826
+ arrayTypeCtx,
114827
+ arrayDim,
114460
114828
  isConst,
114461
- combinedArrayDim,
114462
114829
  overflowBehavior,
114463
114830
  isAtomic,
114464
114831
  callbacks
114465
- )) {
114466
- const existingInfo = CodeGenState.getVariableTypeInfo(registryName);
114467
- if (existingInfo) {
114468
- const arrayTypeDim = _TypeRegistrationEngine.parseArrayTypeDimension(arrayTypeCtx);
114469
- const allDims = arrayTypeDim ? [arrayTypeDim, ...existingInfo.arrayDimensions ?? []] : existingInfo.arrayDimensions;
114470
- CodeGenState.setVariableTypeInfo(registryName, {
114471
- ...existingInfo,
114472
- isArray: true,
114473
- arrayDimensions: allDims
114474
- });
114475
- }
114832
+ );
114833
+ return;
114834
+ }
114835
+ if (arrayTypeCtx.userType()) {
114836
+ const registered = _TypeRegistrationEngine._tryRegisterUserTypeArray(
114837
+ registryName,
114838
+ arrayTypeCtx,
114839
+ arrayDim,
114840
+ isConst,
114841
+ overflowBehavior,
114842
+ isAtomic,
114843
+ callbacks
114844
+ );
114845
+ if (registered) {
114476
114846
  return;
114477
114847
  }
114478
114848
  }
114479
- if (!baseType) {
114849
+ const typeInfo = _TypeRegistrationEngine._extractArrayBaseTypeInfo(arrayTypeCtx);
114850
+ if (!typeInfo.baseType) {
114480
114851
  return;
114481
114852
  }
114482
114853
  const arrayDimensions = _TypeRegistrationEngine._collectArrayDimensions(
@@ -114485,8 +114856,8 @@ var TypeRegistrationEngine = class _TypeRegistrationEngine {
114485
114856
  callbacks
114486
114857
  );
114487
114858
  CodeGenState.setVariableTypeInfo(registryName, {
114488
- baseType,
114489
- bitWidth,
114859
+ baseType: typeInfo.baseType,
114860
+ bitWidth: typeInfo.bitWidth,
114490
114861
  isArray: true,
114491
114862
  arrayDimensions: arrayDimensions.length > 0 ? arrayDimensions : void 0,
114492
114863
  isConst,
@@ -114494,6 +114865,64 @@ var TypeRegistrationEngine = class _TypeRegistrationEngine {
114494
114865
  isAtomic
114495
114866
  });
114496
114867
  }
114868
+ /**
114869
+ * Extract base type and bit width from an array type context.
114870
+ * Handles primitive, qualified, scoped, and user types.
114871
+ */
114872
+ static _extractArrayBaseTypeInfo(arrayTypeCtx) {
114873
+ if (arrayTypeCtx.primitiveType()) {
114874
+ const baseType = arrayTypeCtx.primitiveType().getText();
114875
+ return { baseType, bitWidth: TYPE_WIDTH_default[baseType] || 0 };
114876
+ }
114877
+ if (arrayTypeCtx.qualifiedType()) {
114878
+ const parts = arrayTypeCtx.qualifiedType().IDENTIFIER();
114879
+ return { baseType: parts.map((p) => p.getText()).join("_"), bitWidth: 0 };
114880
+ }
114881
+ if (arrayTypeCtx.scopedType()) {
114882
+ const typeName = arrayTypeCtx.scopedType().IDENTIFIER().getText();
114883
+ const baseType = CodeGenState.currentScope ? `${CodeGenState.currentScope}_${typeName}` : typeName;
114884
+ return { baseType, bitWidth: 0 };
114885
+ }
114886
+ if (arrayTypeCtx.globalType()) {
114887
+ const typeName = arrayTypeCtx.globalType().IDENTIFIER().getText();
114888
+ return { baseType: typeName, bitWidth: 0 };
114889
+ }
114890
+ if (arrayTypeCtx.userType()) {
114891
+ return { baseType: arrayTypeCtx.userType().getText(), bitWidth: 0 };
114892
+ }
114893
+ return { baseType: "", bitWidth: 0 };
114894
+ }
114895
+ /**
114896
+ * Try to register a user type array as enum or bitmap.
114897
+ * Returns true if registration was handled, false if it should fall through.
114898
+ */
114899
+ static _tryRegisterUserTypeArray(registryName, arrayTypeCtx, arrayDim, isConst, overflowBehavior, isAtomic, callbacks) {
114900
+ const baseType = arrayTypeCtx.userType().getText();
114901
+ const combinedArrayDim = arrayDim ?? [];
114902
+ const registered = _TypeRegistrationEngine._tryRegisterEnumOrBitmapType(
114903
+ registryName,
114904
+ baseType,
114905
+ isConst,
114906
+ combinedArrayDim,
114907
+ overflowBehavior,
114908
+ isAtomic,
114909
+ callbacks
114910
+ );
114911
+ if (!registered) {
114912
+ return false;
114913
+ }
114914
+ const existingInfo = CodeGenState.getVariableTypeInfo(registryName);
114915
+ if (existingInfo) {
114916
+ const arrayTypeDim = _TypeRegistrationEngine.parseArrayTypeDimension(arrayTypeCtx);
114917
+ const allDims = arrayTypeDim ? [arrayTypeDim, ...existingInfo.arrayDimensions ?? []] : existingInfo.arrayDimensions;
114918
+ CodeGenState.setVariableTypeInfo(registryName, {
114919
+ ...existingInfo,
114920
+ isArray: true,
114921
+ arrayDimensions: allDims
114922
+ });
114923
+ }
114924
+ return true;
114925
+ }
114497
114926
  static _collectArrayDimensions(arrayTypeCtx, arrayDim, callbacks) {
114498
114927
  const arrayDimensions = [];
114499
114928
  for (const dim of arrayTypeCtx.arrayTypeDimension()) {
@@ -116989,18 +117418,10 @@ var generateEqualityExpr = (node, input, state, orchestrator) => {
116989
117418
  const rightIsString = orchestrator.isStringExpression(exprs[1]);
116990
117419
  if (leftIsString || rightIsString) {
116991
117420
  effects.push({ type: "include", header: "string" });
116992
- const leftResult = generateRelationalExpr(
116993
- exprs[0],
116994
- input,
116995
- state,
116996
- orchestrator
116997
- );
116998
- const rightResult = generateRelationalExpr(
116999
- exprs[1],
117000
- input,
117001
- state,
117002
- orchestrator
117003
- );
117421
+ const [leftResult, rightResult] = CodeGenState.withoutExpectedType(() => [
117422
+ generateRelationalExpr(exprs[0], input, state, orchestrator),
117423
+ generateRelationalExpr(exprs[1], input, state, orchestrator)
117424
+ ]);
117004
117425
  effects.push(...leftResult.effects, ...rightResult.effects);
117005
117426
  const fullText = node.getText();
117006
117427
  const isNotEqual = fullText.includes("!=");
@@ -117015,13 +117436,15 @@ var generateEqualityExpr = (node, input, state, orchestrator) => {
117015
117436
  }
117016
117437
  }
117017
117438
  const operators = orchestrator.getOperatorsFromChildren(node);
117018
- return accumulateBinaryExprs(
117019
- exprs,
117020
- operators,
117021
- "=",
117022
- generateRelationalExpr,
117023
- { input, state, orchestrator },
117024
- BinaryExprUtils_default.mapEqualityOperator
117439
+ return CodeGenState.withoutExpectedType(
117440
+ () => accumulateBinaryExprs(
117441
+ exprs,
117442
+ operators,
117443
+ "=",
117444
+ generateRelationalExpr,
117445
+ { input, state, orchestrator },
117446
+ BinaryExprUtils_default.mapEqualityOperator
117447
+ )
117025
117448
  );
117026
117449
  };
117027
117450
  var generateRelationalExpr = (node, input, state, orchestrator) => {
@@ -117030,11 +117453,13 @@ var generateRelationalExpr = (node, input, state, orchestrator) => {
117030
117453
  return generateBitwiseOrExpr(exprs[0], input, state, orchestrator);
117031
117454
  }
117032
117455
  const operators = orchestrator.getOperatorsFromChildren(node);
117033
- return accumulateBinaryExprs(exprs, operators, "<", generateBitwiseOrExpr, {
117034
- input,
117035
- state,
117036
- orchestrator
117037
- });
117456
+ return CodeGenState.withoutExpectedType(
117457
+ () => accumulateBinaryExprs(exprs, operators, "<", generateBitwiseOrExpr, {
117458
+ input,
117459
+ state,
117460
+ orchestrator
117461
+ })
117462
+ );
117038
117463
  };
117039
117464
  var generateBitwiseOrExpr = (node, input, state, orchestrator) => {
117040
117465
  const effects = [];
@@ -117224,8 +117649,12 @@ var generateTernaryExpr = (node, _input, _state, orchestrator) => {
117224
117649
  orchestrator.validateNoNestedTernary(falseExpr, "false branch");
117225
117650
  orchestrator.validateTernaryConditionNoFunctionCall(condition);
117226
117651
  const condCode = orchestrator.generateOrExpr(condition);
117227
- const trueCode = orchestrator.generateOrExpr(trueExpr);
117228
- const falseCode = orchestrator.generateOrExpr(falseExpr);
117652
+ const trueCode = CodeGenState.withoutDeclarationInit(
117653
+ () => orchestrator.generateOrExpr(trueExpr)
117654
+ );
117655
+ const falseCode = CodeGenState.withoutDeclarationInit(
117656
+ () => orchestrator.generateOrExpr(falseExpr)
117657
+ );
117229
117658
  return { code: `(${condCode}) ? ${trueCode} : ${falseCode}`, effects };
117230
117659
  };
117231
117660
  var expressionGenerators = {
@@ -117436,7 +117865,7 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
117436
117865
  if (typeInfo?.isPointer) {
117437
117866
  isPointerVariable = true;
117438
117867
  }
117439
- const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariable(argCode);
117868
+ const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariableAccess(argCode);
117440
117869
  const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && !isOpaqueScopeVar && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
117441
117870
  const finalArgCode = needsAddressOf ? `&${argCode}` : argCode;
117442
117871
  return wrapWithCppEnumCast(
@@ -117473,39 +117902,41 @@ var generateFunctionCall = (funcExpr, argCtx, input, _state, orchestrator) => {
117473
117902
  trackPassThroughModifications(funcExpr, argExprs, orchestrator);
117474
117903
  }
117475
117904
  const sig = input.functionSignatures.get(funcExpr);
117476
- const args = argExprs.map((e, idx) => {
117477
- const resolved = CallExprUtils_default.resolveTargetParam(
117478
- sig,
117479
- idx,
117480
- funcExpr,
117481
- input.symbolTable
117482
- );
117483
- const targetParam = resolved.param;
117484
- if (!isCNextFunc) {
117485
- return _generateCFunctionArg(e, targetParam, input, orchestrator);
117486
- }
117487
- if (_shouldPassByValue(
117488
- funcExpr,
117489
- idx,
117490
- targetParam,
117491
- resolved.isCrossFile,
117492
- orchestrator
117493
- )) {
117494
- const argCode = CodeGenState.withExpectedType(
117495
- targetParam?.baseType,
117496
- () => orchestrator.generateExpression(e),
117497
- true
117498
- // suppressEnumResolution
117905
+ const args = CodeGenState.withoutDeclarationInit(
117906
+ () => argExprs.map((e, idx) => {
117907
+ const resolved = CallExprUtils_default.resolveTargetParam(
117908
+ sig,
117909
+ idx,
117910
+ funcExpr,
117911
+ input.symbolTable
117499
117912
  );
117500
- return wrapWithCppEnumCast(
117501
- argCode,
117502
- e,
117503
- targetParam?.baseType,
117913
+ const targetParam = resolved.param;
117914
+ if (!isCNextFunc) {
117915
+ return _generateCFunctionArg(e, targetParam, input, orchestrator);
117916
+ }
117917
+ if (_shouldPassByValue(
117918
+ funcExpr,
117919
+ idx,
117920
+ targetParam,
117921
+ resolved.isCrossFile,
117504
117922
  orchestrator
117505
- );
117506
- }
117507
- return orchestrator.generateFunctionArg(e, targetParam?.baseType);
117508
- }).join(", ");
117923
+ )) {
117924
+ const argCode = CodeGenState.withExpectedType(
117925
+ targetParam?.baseType,
117926
+ () => orchestrator.generateExpression(e),
117927
+ true
117928
+ // suppressEnumResolution
117929
+ );
117930
+ return wrapWithCppEnumCast(
117931
+ argCode,
117932
+ e,
117933
+ targetParam?.baseType,
117934
+ orchestrator
117935
+ );
117936
+ }
117937
+ return orchestrator.generateFunctionArg(e, targetParam?.baseType);
117938
+ }).join(", ")
117939
+ );
117509
117940
  return { code: `${funcExpr}(${args})`, effects };
117510
117941
  };
117511
117942
  var generateSafeDivMod = (funcName, argExprs, input, orchestrator, effects) => {
@@ -119948,7 +120379,9 @@ function generateInitializer(varDecl, isArray, orchestrator) {
119948
120379
  const typeName = orchestrator.generateType(varDecl.type());
119949
120380
  return CodeGenState.withExpectedType(
119950
120381
  typeName,
119951
- () => ` = ${orchestrator.generateExpression(varDecl.expression())}`
120382
+ () => CodeGenState.withDeclarationInit(
120383
+ () => ` = ${orchestrator.generateExpression(varDecl.expression())}`
120384
+ )
119952
120385
  );
119953
120386
  }
119954
120387
  return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
@@ -120026,9 +120459,19 @@ function generateRegularVariable(varDecl, varName, scopeName, isPrivate, orchest
120026
120459
  type = `${type}*`;
120027
120460
  orchestrator.markOpaqueScopeVariable(fullName);
120028
120461
  }
120462
+ const modifiers = VariableModifierBuilder_default.build(
120463
+ varDecl,
120464
+ false,
120465
+ // inFunctionBody - scope vars are file scope
120466
+ false,
120467
+ // hasInitializer - doesn't affect volatile/atomic handling
120468
+ false
120469
+ // cppMode - doesn't affect volatile/atomic handling
120470
+ );
120471
+ const staticPrefix = isPrivate ? "static " : "";
120472
+ const volatilePrefix = modifiers.atomic || modifiers.volatile;
120029
120473
  const constPrefix = isConst ? "const " : "";
120030
- const prefix = isPrivate ? "static " : "";
120031
- let decl = `${prefix}${constPrefix}${type} ${fullName}`;
120474
+ let decl = `${staticPrefix}${volatilePrefix}${constPrefix}${type} ${fullName}`;
120032
120475
  decl += ArrayDimensionUtils_default.generateArrayTypeDimension(
120033
120476
  arrayTypeCtx,
120034
120477
  orchestrator
@@ -120037,7 +120480,7 @@ function generateRegularVariable(varDecl, varName, scopeName, isPrivate, orchest
120037
120480
  decl += orchestrator.generateArrayDimensions(arrayDims);
120038
120481
  }
120039
120482
  decl += ArrayDimensionUtils_default.generateStringCapacityDim(varDecl.type());
120040
- if (isOpaque || isExternalStruct) {
120483
+ if ((isOpaque || isExternalStruct) && !isArray) {
120041
120484
  decl += " = NULL";
120042
120485
  } else {
120043
120486
  decl += generateInitializer(varDecl, isArray, orchestrator);
@@ -125872,19 +126315,6 @@ var SymbolLookupHelper = class _SymbolLookupHelper {
125872
126315
  const symbols = symbolTable.getOverloads(name);
125873
126316
  return symbols.some((sym) => sym.kind === "namespace");
125874
126317
  }
125875
- /**
125876
- * Check if a type name is from a C++ header.
125877
- * Issue #304: Used to determine whether to use {} or {0} for initialization.
125878
- * C++ types with constructors may fail with {0} but work with {}.
125879
- */
125880
- static isCppType(symbolTable, typeName) {
125881
- if (!symbolTable) return false;
125882
- const symbols = symbolTable.getOverloads(typeName);
125883
- const cppTypeKinds = /* @__PURE__ */ new Set(["struct", "class", "type"]);
125884
- return symbols.some(
125885
- (sym) => sym.sourceLanguage === ESourceLanguage_default.Cpp && cppTypeKinds.has(sym.kind)
125886
- );
125887
- }
125888
126318
  /**
125889
126319
  * Check if a function is a C-Next function (uses pass-by-reference semantics).
125890
126320
  * Returns true if the function is found in symbol table as C-Next.
@@ -126224,6 +126654,18 @@ var StringDeclHelper = class _StringDeclHelper {
126224
126654
  * Returns { handled: false } if not a string type.
126225
126655
  */
126226
126656
  static generateStringDecl(typeCtx, name, expression, arrayDims, modifiers, isConst, callbacks) {
126657
+ const arrayTypeCtx = typeCtx.arrayType?.();
126658
+ if (arrayTypeCtx?.stringType?.()) {
126659
+ return _StringDeclHelper._generateStringArrayFromArrayType(
126660
+ name,
126661
+ arrayTypeCtx,
126662
+ expression,
126663
+ arrayDims,
126664
+ modifiers,
126665
+ isConst,
126666
+ callbacks
126667
+ );
126668
+ }
126227
126669
  const stringCtx = typeCtx.stringType();
126228
126670
  if (!stringCtx) {
126229
126671
  return { code: "", handled: false };
@@ -126250,6 +126692,122 @@ var StringDeclHelper = class _StringDeclHelper {
126250
126692
  );
126251
126693
  }
126252
126694
  }
126695
+ /**
126696
+ * Issue #1029: Generate string array declaration from arrayType syntax.
126697
+ * Handles: string<32>[4] items -> char items[4][33] = {0};
126698
+ */
126699
+ static _generateStringArrayFromArrayType(name, arrayTypeCtx, expression, trailingArrayDims, modifiers, isConst, callbacks) {
126700
+ const stringCtx = arrayTypeCtx.stringType();
126701
+ const intLiteral = stringCtx.INTEGER_LITERAL();
126702
+ if (!intLiteral) {
126703
+ throw new Error(
126704
+ "Error: String arrays require explicit capacity, e.g., string<64>[4]"
126705
+ );
126706
+ }
126707
+ const capacity = Number.parseInt(intLiteral.getText(), 10);
126708
+ callbacks.requireStringInclude();
126709
+ const {
126710
+ extern,
126711
+ const: constMod,
126712
+ atomic,
126713
+ volatile: volatileMod
126714
+ } = modifiers;
126715
+ let arrayDimStr = "";
126716
+ for (const dim of arrayTypeCtx.arrayTypeDimension()) {
126717
+ const sizeExpr = dim.expression();
126718
+ if (sizeExpr) {
126719
+ arrayDimStr += `[${sizeExpr.getText()}]`;
126720
+ } else {
126721
+ arrayDimStr += "[]";
126722
+ }
126723
+ }
126724
+ arrayDimStr += callbacks.generateArrayDimensions(trailingArrayDims);
126725
+ arrayDimStr += `[${capacity + 1}]`;
126726
+ let decl = `${extern}${constMod}${atomic}${volatileMod}char ${name}${arrayDimStr}`;
126727
+ CodeGenState.localArrays.add(name);
126728
+ if (!expression) {
126729
+ return { code: `${decl} = {0};`, handled: true };
126730
+ }
126731
+ CodeGenState.lastArrayInitCount = 0;
126732
+ CodeGenState.lastArrayFillValue = void 0;
126733
+ const initValue = callbacks.generateExpression(expression);
126734
+ const isArrayInit = CodeGenState.lastArrayInitCount > 0 || CodeGenState.lastArrayFillValue !== void 0;
126735
+ if (!isArrayInit) {
126736
+ throw new Error(
126737
+ `Error: String array initialization from variables not supported`
126738
+ );
126739
+ }
126740
+ const declaredSize = _StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
126741
+ if (declaredSize !== null) {
126742
+ const isFillAll = CodeGenState.lastArrayFillValue !== void 0;
126743
+ const elementCount = CodeGenState.lastArrayInitCount;
126744
+ if (!isFillAll && elementCount !== declaredSize) {
126745
+ throw new Error(
126746
+ `Error: Array size mismatch - declared [${declaredSize}] but got ${elementCount} elements`
126747
+ );
126748
+ }
126749
+ }
126750
+ const finalInitValue = _StringDeclHelper._expandFillAllForArrayType(
126751
+ initValue,
126752
+ arrayTypeCtx
126753
+ );
126754
+ const suppression = "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
126755
+ return {
126756
+ code: `${suppression}${decl} = ${finalInitValue};`,
126757
+ handled: true
126758
+ };
126759
+ }
126760
+ /**
126761
+ * Get the numeric size from the first arrayTypeDimension, or null if not numeric.
126762
+ * Used by arrayType-based string arrays (string<N>[M]).
126763
+ */
126764
+ static _getArrayTypeDeclaredSize(arrayTypeCtx) {
126765
+ const dims = arrayTypeCtx.arrayTypeDimension();
126766
+ if (dims.length === 0) {
126767
+ return null;
126768
+ }
126769
+ const firstDimExpr = dims[0].expression();
126770
+ return _StringDeclHelper._parseNumericSize(firstDimExpr);
126771
+ }
126772
+ /**
126773
+ * Expand fill-all syntax for arrayType-based string arrays.
126774
+ * Delegates to the common fill-all expansion logic with size from arrayType.
126775
+ */
126776
+ static _expandFillAllForArrayType(initValue, arrayTypeCtx) {
126777
+ const declaredSize = _StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
126778
+ return _StringDeclHelper._expandFillAll(initValue, declaredSize);
126779
+ }
126780
+ /**
126781
+ * Common fill-all expansion logic shared between arrayType and arrayDimension paths.
126782
+ */
126783
+ static _expandFillAll(initValue, declaredSize) {
126784
+ const fillVal = CodeGenState.lastArrayFillValue;
126785
+ if (fillVal === void 0) {
126786
+ return initValue;
126787
+ }
126788
+ if (fillVal === '""') {
126789
+ return initValue;
126790
+ }
126791
+ if (declaredSize === null) {
126792
+ return initValue;
126793
+ }
126794
+ const elements = new Array(declaredSize).fill(fillVal);
126795
+ return `{${elements.join(", ")}}`;
126796
+ }
126797
+ /**
126798
+ * Parse a numeric size from an expression, or return null if not numeric.
126799
+ * Shared helper to eliminate duplicate parsing logic.
126800
+ */
126801
+ static _parseNumericSize(expr) {
126802
+ if (!expr) {
126803
+ return null;
126804
+ }
126805
+ const sizeText = expr.getText();
126806
+ if (!/^\d+$/.exec(sizeText)) {
126807
+ return null;
126808
+ }
126809
+ return Number.parseInt(sizeText, 10);
126810
+ }
126253
126811
  /**
126254
126812
  * Generate bounded string declaration (string<N>).
126255
126813
  */
@@ -126303,16 +126861,33 @@ var StringDeclHelper = class _StringDeclHelper {
126303
126861
  constMod
126304
126862
  );
126305
126863
  }
126306
- _StringDeclHelper._validateStringInit(
126307
- expression.getText(),
126864
+ const exprText = expression.getText();
126865
+ const isLiteral = _StringDeclHelper._validateStringInit(
126866
+ exprText,
126308
126867
  capacity,
126309
126868
  callbacks
126310
126869
  );
126311
- const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
126312
- return { code, handled: true };
126870
+ if (isLiteral) {
126871
+ const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
126872
+ return { code, handled: true };
126873
+ }
126874
+ if (!CodeGenState.inFunctionBody) {
126875
+ throw new Error(
126876
+ `Error: String initialization from variable cannot be used at global scope. Move the declaration inside a function, or use an empty initializer and assign later.`
126877
+ );
126878
+ }
126879
+ const srcExpr = callbacks.generateExpression(expression);
126880
+ const indent = FormatUtils_default.indent(CodeGenState.indentLevel);
126881
+ const lines = [];
126882
+ lines.push(
126883
+ `${constMod}char ${name}[${capacity + 1}] = "";`,
126884
+ `${indent}strcpy(${name}, ${srcExpr});`
126885
+ );
126886
+ return { code: lines.join("\n"), handled: true };
126313
126887
  }
126314
126888
  /**
126315
126889
  * Validate string initialization (literal length and variable capacity)
126890
+ * Returns true if the expression is a string literal, false if it's a variable.
126316
126891
  */
126317
126892
  static _validateStringInit(exprText, capacity, callbacks) {
126318
126893
  if (exprText.startsWith('"') && exprText.endsWith('"')) {
@@ -126322,6 +126897,7 @@ var StringDeclHelper = class _StringDeclHelper {
126322
126897
  `Error: String literal (${content} chars) exceeds string<${capacity}> capacity`
126323
126898
  );
126324
126899
  }
126900
+ return true;
126325
126901
  }
126326
126902
  const srcCapacity = callbacks.getStringExprCapacity(exprText);
126327
126903
  if (srcCapacity !== null && srcCapacity > capacity) {
@@ -126329,6 +126905,7 @@ var StringDeclHelper = class _StringDeclHelper {
126329
126905
  `Error: Cannot assign string<${srcCapacity}> to string<${capacity}> (potential truncation)`
126330
126906
  );
126331
126907
  }
126908
+ return false;
126332
126909
  }
126333
126910
  /**
126334
126911
  * Generate string array declaration.
@@ -126416,35 +126993,19 @@ var StringDeclHelper = class _StringDeclHelper {
126416
126993
  /**
126417
126994
  * Expand fill-all syntax if needed.
126418
126995
  * ["Hello"*] with size 3 -> {"Hello", "Hello", "Hello"}
126996
+ * Delegates to the common fill-all expansion logic with size from arrayDimension.
126419
126997
  */
126420
126998
  static _expandFillAllIfNeeded(initValue, arrayDims) {
126421
- const fillVal = CodeGenState.lastArrayFillValue;
126422
- if (fillVal === void 0) {
126423
- return initValue;
126424
- }
126425
- if (fillVal === '""') {
126426
- return initValue;
126427
- }
126428
126999
  const declaredSize = _StringDeclHelper._getFirstDimNumericSize(arrayDims);
126429
- if (declaredSize === null) {
126430
- return initValue;
126431
- }
126432
- const elements = new Array(declaredSize).fill(fillVal);
126433
- return `{${elements.join(", ")}}`;
127000
+ return _StringDeclHelper._expandFillAll(initValue, declaredSize);
126434
127001
  }
126435
127002
  /**
126436
127003
  * Get the numeric size from the first array dimension, or null if not numeric.
127004
+ * Used by arrayDimension-based string arrays (string<N> arr[M]).
126437
127005
  */
126438
127006
  static _getFirstDimNumericSize(arrayDims) {
126439
127007
  const firstDimExpr = arrayDims[0]?.expression();
126440
- if (!firstDimExpr) {
126441
- return null;
126442
- }
126443
- const sizeText = firstDimExpr.getText();
126444
- if (!/^\d+$/.exec(sizeText)) {
126445
- return null;
126446
- }
126447
- return Number.parseInt(sizeText, 10);
127008
+ return _StringDeclHelper._parseNumericSize(firstDimExpr);
126448
127009
  }
126449
127010
  /**
126450
127011
  * Generate string concatenation declaration.
@@ -126638,24 +127199,14 @@ var VariableDeclHelper = class _VariableDeclHelper {
126638
127199
  if (arrayDims.length === 0) {
126639
127200
  return;
126640
127201
  }
126641
- if (typeCtx.arrayType()) {
126642
- return;
126643
- }
126644
- if (arrayDims.length === 1 && !arrayDims[0].expression()) {
126645
- return;
126646
- }
126647
- if (arrayDims.length > 1) {
126648
- return;
126649
- }
126650
- if (typeCtx.qualifiedType() || typeCtx.scopedType() || typeCtx.globalType() || typeCtx.stringType()) {
126651
- return;
126652
- }
126653
127202
  const baseType = _VariableDeclHelper.extractBaseTypeName(typeCtx);
126654
- const dimensions = arrayDims.map((dim) => `[${dim.expression()?.getText() ?? ""}]`).join("");
127203
+ const existingDims = typeCtx.arrayType() ? typeCtx.arrayType().arrayTypeDimension().map((d) => `[${d.expression()?.getText() ?? ""}]`).join("") : "";
127204
+ const trailingDims = arrayDims.map((dim) => `[${dim.expression()?.getText() ?? ""}]`).join("");
127205
+ const allDims = existingDims + trailingDims;
126655
127206
  const line = ctx.start?.line ?? 0;
126656
127207
  const col = ctx.start?.column ?? 0;
126657
127208
  throw new Error(
126658
- `${line}:${col} C-style array declaration is not allowed. Use '${baseType}${dimensions} ${name}' instead of '${baseType} ${name}${dimensions}'`
127209
+ `${line}:${col} C-style array declaration is not allowed. Use '${baseType}${allDims} ${name}' instead of '${baseType}${existingDims} ${name}${trailingDims}'`
126659
127210
  );
126660
127211
  }
126661
127212
  /**
@@ -126762,7 +127313,8 @@ ${assignments}`;
126762
127313
  typeCtx,
126763
127314
  callbacks
126764
127315
  );
126765
- const hasEmptyArrayDim = arrayDims.some((dim) => !dim.expression());
127316
+ const hasEmptyArrayDim = arrayDims.some((dim) => !dim.expression()) || (typeCtx.arrayType()?.arrayTypeDimension().some((dim) => !dim.expression()) ?? false);
127317
+ const hasEmptyArrayTypeDim = typeCtx.arrayType()?.arrayTypeDimension().some((dim) => !dim.expression()) ?? false;
126766
127318
  const declaredSize = _VariableDeclHelper.parseArrayTypeDimension(typeCtx) ?? _VariableDeclHelper.parseFirstArrayDimension(arrayDims);
126767
127319
  if (ctx.expression()) {
126768
127320
  const arrayInitResult = ArrayInitHelper_default.processArrayInit(
@@ -126780,7 +127332,7 @@ ${assignments}`;
126780
127332
  );
126781
127333
  if (arrayInitResult) {
126782
127334
  CodeGenState.localArrays.add(name);
126783
- const fullDimSuffix = arrayTypeDimStr + arrayInitResult.dimensionSuffix;
127335
+ const fullDimSuffix = hasEmptyArrayTypeDim ? arrayInitResult.dimensionSuffix : arrayTypeDimStr + arrayInitResult.dimensionSuffix;
126784
127336
  return {
126785
127337
  handled: true,
126786
127338
  code: `${decl}${fullDimSuffix} = ${arrayInitResult.initValue};`,
@@ -126815,7 +127367,9 @@ ${assignments}`;
126815
127367
  getExpressionType: callbacks.getExpressionType
126816
127368
  });
126817
127369
  return CodeGenState.withExpectedType(typeName, () => {
126818
- let exprCode = callbacks.generateExpression(ctx.expression());
127370
+ let exprCode = CodeGenState.withDeclarationInit(
127371
+ () => callbacks.generateExpression(ctx.expression())
127372
+ );
126819
127373
  const exprType = callbacks.getExpressionType(ctx.expression());
126820
127374
  if (exprType && NarrowingCastHelper_default.isCrossTypeCategoryConversion(exprType, typeName)) {
126821
127375
  if (NarrowingCastHelper_default.isIntegerType(exprType) && NarrowingCastHelper_default.isFloatType(typeName)) {
@@ -127603,31 +128157,29 @@ var TypeGenerationHelper = class _TypeGenerationHelper {
127603
128157
  return "char";
127604
128158
  }
127605
128159
  /**
127606
- * Full type generation using all dependencies.
127607
- * This is the main entry point that handles all type contexts.
128160
+ * Dispatch type generation for contexts that share common type accessors.
128161
+ * Handles scoped, qualified, global, primitive, string, and user types.
128162
+ * Used by both bare type contexts and array element type contexts.
128163
+ *
128164
+ * @returns The resolved C type string, or null if no matching type accessor found
127608
128165
  */
127609
- static generate(ctx, deps) {
127610
- if (ctx.primitiveType()) {
127611
- const type = ctx.primitiveType().getText();
127612
- const result = _TypeGenerationHelper.generatePrimitiveType(type);
127613
- return result.cType;
127614
- }
127615
- if (ctx.stringType()) {
128166
+ static dispatchTypeGeneration(accessors, deps) {
128167
+ if (accessors.stringType()) {
127616
128168
  return _TypeGenerationHelper.generateStringType();
127617
128169
  }
127618
- if (ctx.scopedType()) {
127619
- const typeName = ctx.scopedType().IDENTIFIER().getText();
128170
+ if (accessors.scopedType()) {
128171
+ const typeName = accessors.scopedType().IDENTIFIER().getText();
127620
128172
  return _TypeGenerationHelper.generateScopedType(
127621
128173
  typeName,
127622
128174
  deps.currentScope
127623
128175
  );
127624
128176
  }
127625
- if (ctx.globalType()) {
127626
- const typeName = ctx.globalType().IDENTIFIER().getText();
128177
+ if (accessors.globalType()) {
128178
+ const typeName = accessors.globalType().IDENTIFIER().getText();
127627
128179
  return _TypeGenerationHelper.generateGlobalType(typeName);
127628
128180
  }
127629
- if (ctx.qualifiedType()) {
127630
- const identifiers = ctx.qualifiedType().IDENTIFIER();
128181
+ if (accessors.qualifiedType()) {
128182
+ const identifiers = accessors.qualifiedType().IDENTIFIER();
127631
128183
  const identifierNames = identifiers.map((id) => id.getText());
127632
128184
  const isCpp = deps.isCppScopeSymbol(identifierNames[0]);
127633
128185
  return _TypeGenerationHelper.generateQualifiedType(
@@ -127636,24 +128188,36 @@ var TypeGenerationHelper = class _TypeGenerationHelper {
127636
128188
  deps.validateCrossScopeVisibility
127637
128189
  );
127638
128190
  }
127639
- if (ctx.userType()) {
127640
- const typeName = ctx.userType().getText();
128191
+ if (accessors.primitiveType()) {
128192
+ const type = accessors.primitiveType().getText();
128193
+ return TYPE_MAP_default[type] || type;
128194
+ }
128195
+ if (accessors.userType()) {
128196
+ const typeName = accessors.userType().getText();
128197
+ if (typeName === "cstring") {
128198
+ return "char*";
128199
+ }
127641
128200
  const needsStruct = deps.checkNeedsStructKeyword(typeName);
127642
128201
  return _TypeGenerationHelper.generateUserType(typeName, needsStruct);
127643
128202
  }
128203
+ return null;
128204
+ }
128205
+ /**
128206
+ * Full type generation using all dependencies.
128207
+ * This is the main entry point that handles all type contexts.
128208
+ */
128209
+ static generate(ctx, deps) {
127644
128210
  if (ctx.arrayType()) {
127645
128211
  const arrCtx = ctx.arrayType();
127646
- if (arrCtx.stringType()) {
127647
- return "char";
127648
- }
127649
- const primitiveText = arrCtx.primitiveType()?.getText() ?? null;
127650
- const userTypeName = arrCtx.userType()?.getText() ?? null;
127651
- const needsStruct = userTypeName ? deps.checkNeedsStructKeyword(userTypeName) : false;
127652
- return _TypeGenerationHelper.generateArrayBaseType(
127653
- primitiveText,
127654
- userTypeName,
127655
- needsStruct
127656
- );
128212
+ const result2 = _TypeGenerationHelper.dispatchTypeGeneration(arrCtx, deps);
128213
+ if (result2 !== null) {
128214
+ return result2;
128215
+ }
128216
+ return ctx.getText();
128217
+ }
128218
+ const result = _TypeGenerationHelper.dispatchTypeGeneration(ctx, deps);
128219
+ if (result !== null) {
128220
+ return result;
127657
128221
  }
127658
128222
  if (ctx.getText() === "void") {
127659
128223
  return "void";
@@ -129014,6 +129578,7 @@ var ParameterInputAdapter = class {
129014
129578
  const isKnownStruct = deps.isKnownStruct(typeName);
129015
129579
  const isKnownPrimitive = !!deps.typeMap[typeName];
129016
129580
  const isTypedefStruct = deps.isTypedefStructType(typeName);
129581
+ const isOpaque = deps.isOpaqueType?.(typeName) ?? false;
129017
129582
  const isAutoConst = !deps.isCallbackCompatible && !deps.isModified && !isConst;
129018
129583
  const isPassByReference = deps.forcePassByReference || isKnownStruct || isKnownPrimitive || isTypedefStruct;
129019
129584
  return {
@@ -129031,7 +129596,9 @@ var ParameterInputAdapter = class {
129031
129596
  // and typedef struct params (C types expect pointers, not C++ references)
129032
129597
  forcePointerSyntax: deps.forcePassByReference || isTypedefStruct || void 0,
129033
129598
  // Issue #895: Preserve const from callback typedef signature
129034
- forceConst: deps.forceConst
129599
+ forceConst: deps.forceConst,
129600
+ // Issue #995: Pass through opaque handle detection — rule applied in builder
129601
+ isOpaqueHandle: isOpaque || void 0
129035
129602
  };
129036
129603
  }
129037
129604
  /**
@@ -129078,7 +129645,9 @@ var ParameterInputAdapter = class {
129078
129645
  isPassByValue: isCallbackPointer ? false : deps.isPassByValue,
129079
129646
  isPassByReference: isCallbackPointer ? true : !deps.isPassByValue,
129080
129647
  forcePointerSyntax: isCallbackPointer || void 0,
129081
- forceConst: param.isCallbackConst || void 0
129648
+ forceConst: param.isCallbackConst || void 0,
129649
+ // Issue #995: Pass through opaque handle detection — rule applied in builder
129650
+ isOpaqueHandle: param.isOpaqueHandle || void 0
129082
129651
  };
129083
129652
  }
129084
129653
  /**
@@ -129120,13 +129689,12 @@ var ParameterInputAdapter = class {
129120
129689
  dims.push(String(capacity + 1));
129121
129690
  }
129122
129691
  }
129123
- const isAutoConst = !deps.isModified && !isConst;
129124
129692
  return {
129125
129693
  name,
129126
129694
  baseType: typeName,
129127
129695
  mappedType,
129128
129696
  isConst,
129129
- isAutoConst,
129697
+ isAutoConst: false,
129130
129698
  isArray: true,
129131
129699
  arrayDimensions: dims,
129132
129700
  isCallback: false,
@@ -129204,6 +129772,9 @@ var ParameterSignatureBuilder = class {
129204
129772
  if (param.isString && !param.isArray) {
129205
129773
  return this._buildStringParam(param);
129206
129774
  }
129775
+ if (param.isOpaqueHandle) {
129776
+ return this._buildRefParam(param, refSuffix);
129777
+ }
129207
129778
  if (param.isPassByReference) {
129208
129779
  return this._buildRefParam(param, refSuffix);
129209
129780
  }
@@ -129248,14 +129819,16 @@ var ParameterSignatureBuilder = class {
129248
129819
  /**
129249
129820
  * Build pass-by-reference parameter signature.
129250
129821
  * C mode: const Point* p
129251
- * C++ mode: const Point& p (unless forcePointerSyntax)
129822
+ * C++ mode: const Point& p (unless forcePointerSyntax or isOpaqueHandle)
129252
129823
  *
129253
129824
  * Issue #895: When forcePointerSyntax is set, always use pointer syntax
129254
129825
  * because C callback typedefs expect pointers, not C++ references.
129826
+ * Issue #995: Opaque handles must use pointer syntax because C APIs
129827
+ * expect pointers to incomplete struct types (e.g., widget_t*).
129255
129828
  */
129256
129829
  static _buildRefParam(param, refSuffix) {
129257
129830
  const constPrefix = this._getConstPrefix(param);
129258
- const actualSuffix = param.forcePointerSyntax ? "*" : refSuffix;
129831
+ const actualSuffix = param.forcePointerSyntax || param.isOpaqueHandle ? "*" : refSuffix;
129259
129832
  return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
129260
129833
  }
129261
129834
  /**
@@ -129269,9 +129842,12 @@ var ParameterSignatureBuilder = class {
129269
129842
  * Get const prefix combining explicit const, auto-const, and forced const.
129270
129843
  * Priority: forceConst > isConst > isAutoConst
129271
129844
  * Issue #895: forceConst preserves const from callback typedef signature.
129845
+ * Issue #995: Opaque handles suppress auto-const (they must be passed to
129846
+ * C APIs that expect non-const pointers).
129272
129847
  */
129273
129848
  static _getConstPrefix(param) {
129274
- if (param.forceConst || param.isConst || param.isAutoConst) {
129849
+ const effectiveAutoConst = param.isOpaqueHandle ? false : param.isAutoConst;
129850
+ if (param.forceConst || param.isConst || effectiveAutoConst) {
129275
129851
  return "const ";
129276
129852
  }
129277
129853
  return "";
@@ -129868,6 +130444,7 @@ var CodeGenerator = class _CodeGenerator {
129868
130444
  * Part of IOrchestrator interface.
129869
130445
  * ADR-045: Used to detect string comparisons and generate strcmp().
129870
130446
  * Issue #137: Extended to handle array element access (e.g., names[0])
130447
+ * Issue #1030: Extended to handle struct member access (e.g., person.name)
129871
130448
  */
129872
130449
  isStringExpression(ctx) {
129873
130450
  const text = ctx.getText();
@@ -129880,6 +130457,9 @@ var CodeGenerator = class _CodeGenerator {
129880
130457
  return true;
129881
130458
  }
129882
130459
  }
130460
+ if (this._isStructMemberStringExpression(text)) {
130461
+ return true;
130462
+ }
129883
130463
  return this._isArrayAccessStringExpression(text);
129884
130464
  }
129885
130465
  /**
@@ -129907,6 +130487,36 @@ var CodeGenerator = class _CodeGenerator {
129907
130487
  typeInfo.isArray && typeInfo.baseType && TypeCheckUtils_default.isString(typeInfo.baseType)
129908
130488
  );
129909
130489
  }
130490
+ /**
130491
+ * Check if struct member access expression evaluates to a string.
130492
+ * Issue #1030: Handles patterns like person.name, config.key
130493
+ */
130494
+ _isStructMemberStringExpression(text) {
130495
+ if (text.endsWith(".char_count") || text.endsWith(".capacity") || text.endsWith(".size") || text.endsWith(".length") || text.endsWith(".bit_length") || text.endsWith(".byte_length") || text.endsWith(".element_count")) {
130496
+ return false;
130497
+ }
130498
+ const memberMatch = /^([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)$/.exec(text);
130499
+ if (!memberMatch) {
130500
+ return false;
130501
+ }
130502
+ const [, varName, fieldName] = memberMatch;
130503
+ const typeInfo = CodeGenState.getVariableTypeInfo(varName);
130504
+ if (!typeInfo) {
130505
+ return false;
130506
+ }
130507
+ const structTypeName = typeInfo.baseType;
130508
+ if (!structTypeName) {
130509
+ return false;
130510
+ }
130511
+ const fieldType = CodeGenState.getStructFieldType(
130512
+ structTypeName,
130513
+ fieldName
130514
+ );
130515
+ if (!fieldType) {
130516
+ return false;
130517
+ }
130518
+ return fieldType.startsWith("string");
130519
+ }
129910
130520
  /**
129911
130521
  * Get type of additive expression.
129912
130522
  * Part of IOrchestrator interface - delegates to private implementation.
@@ -130304,21 +130914,21 @@ var CodeGenerator = class _CodeGenerator {
130304
130914
  */
130305
130915
  getZeroInitializer(typeCtx, isArray) {
130306
130916
  if (isArray) {
130307
- return this._getArrayZeroInitializer(typeCtx);
130917
+ return this._getAggregateZeroInitBrace();
130308
130918
  }
130309
130919
  const resolved = this._resolveTypeNameFromContext(typeCtx);
130310
130920
  if (resolved) {
130311
130921
  if (CodeGenState.symbols.knownEnums.has(resolved.name)) {
130312
130922
  return this._getEnumZeroValue(resolved.name, resolved.separator);
130313
130923
  }
130314
- if (resolved.checkCppType && this._needsEmptyBraceInit(resolved.name)) {
130315
- return "{}";
130316
- }
130317
- return "{0}";
130924
+ return this._getAggregateZeroInitBrace();
130318
130925
  }
130319
130926
  if (typeCtx.templateType()) {
130320
130927
  return "{}";
130321
130928
  }
130929
+ if (typeCtx.stringType()) {
130930
+ return '""';
130931
+ }
130322
130932
  if (typeCtx.primitiveType()) {
130323
130933
  const primType = typeCtx.primitiveType().getText();
130324
130934
  return _CodeGenerator.PRIMITIVE_ZERO_VALUES.get(primType) ?? "0";
@@ -130430,7 +131040,7 @@ var CodeGenerator = class _CodeGenerator {
130430
131040
  const constMod = p.isConst ? "const " : "";
130431
131041
  if (p.isArray) {
130432
131042
  return `${constMod}${p.type} ${p.name}${p.arrayDims}`;
130433
- } else if (p.isPointer) {
131043
+ } else if (p.isStruct) {
130434
131044
  const ptrOrRef = this.isCppMode() ? "&" : "*";
130435
131045
  return `${constMod}${p.type}${ptrOrRef}`;
130436
131046
  } else {
@@ -130630,7 +131240,15 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
130630
131240
  return "__GLOBAL_PREFIX__";
130631
131241
  }
130632
131242
  if (ctx.IDENTIFIER()) {
130633
- return this._resolveIdentifierExpression(ctx.IDENTIFIER().getText());
131243
+ const id = ctx.IDENTIFIER().getText();
131244
+ if (id === "break" || id === "continue") {
131245
+ const line = ctx.start?.line ?? 0;
131246
+ const col = ctx.start?.column ?? 0;
131247
+ throw new Error(
131248
+ `${line}:${col} error[E0703]: '${id}' is not supported in C-Next - use structured conditions instead`
131249
+ );
131250
+ }
131251
+ return this._resolveIdentifierExpression(id);
130634
131252
  }
130635
131253
  if (ctx.literal()) {
130636
131254
  return this._generateLiteralExpression(ctx.literal());
@@ -130875,14 +131493,6 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
130875
131493
  }
130876
131494
  return identifiers.join("_");
130877
131495
  }
130878
- /**
130879
- * Issue #304: Check if a type name is from a C++ header
130880
- * Used to determine whether to use {} or {0} for initialization.
130881
- * C++ types with constructors may fail with {0} but work with {}.
130882
- */
130883
- isCppType(typeName) {
130884
- return SymbolLookupHelper_default.isCppType(CodeGenState.symbolTable, typeName);
130885
- }
130886
131496
  /**
130887
131497
  * Generate C code from a C-Next program
130888
131498
  * @param tree The parsed C-Next program
@@ -131313,6 +131923,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131313
131923
  const arrayTypeCtx = param.type().arrayType();
131314
131924
  const isArray = dims.length > 0 || arrayTypeCtx !== null;
131315
131925
  const isCallbackParam = CodeGenState.callbackTypes.has(typeName);
131926
+ const isStruct = this.isStructType(typeName);
131316
131927
  let paramType;
131317
131928
  let isPointer;
131318
131929
  if (isCallbackParam) {
@@ -131321,7 +131932,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131321
131932
  isPointer = false;
131322
131933
  } else {
131323
131934
  paramType = this.generateType(param.type());
131324
- isPointer = !isArray;
131935
+ isPointer = !isArray && isStruct;
131325
131936
  }
131326
131937
  let arrayDims;
131327
131938
  if (dims.length > 0) {
@@ -131339,6 +131950,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131339
131950
  type: paramType,
131340
131951
  isConst,
131341
131952
  isPointer,
131953
+ isStruct,
131342
131954
  isArray,
131343
131955
  arrayDims
131344
131956
  });
@@ -131684,7 +132296,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131684
132296
  }
131685
132297
  decl += this._getStringCapacityDimension(varDecl.type());
131686
132298
  if (varDecl.expression()) {
131687
- decl += ` = ${this.generateExpression(varDecl.expression())}`;
132299
+ decl += ` = ${CodeGenState.withDeclarationInit(() => this.generateExpression(varDecl.expression()))}`;
131688
132300
  } else {
131689
132301
  decl += ` = ${this.getZeroInitializer(varDecl.type(), isArray)}`;
131690
132302
  }
@@ -131877,21 +132489,13 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131877
132489
  needsStructKeyword
131878
132490
  );
131879
132491
  if (!fieldList) {
131880
- return isCppClass ? "{}" : `(${castType}){ 0 }`;
132492
+ if (isCppClass) return "{}";
132493
+ return CodeGenState.inDeclarationInit ? "{ 0 }" : `(${castType}){ 0 }`;
131881
132494
  }
131882
132495
  const structFieldTypes = CodeGenState.symbolTable?.getStructFieldTypes(typeName);
131883
132496
  const fields = fieldList.fieldInitializer().map((field) => {
131884
132497
  const fieldName = field.IDENTIFIER().getText();
131885
- let fieldType;
131886
- if (structFieldTypes?.has(fieldName)) {
131887
- fieldType = structFieldTypes.get(fieldName);
131888
- if (fieldType.includes("_")) {
131889
- const parts = fieldType.split("_");
131890
- if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
131891
- fieldType = parts.join("::");
131892
- }
131893
- }
131894
- }
132498
+ const fieldType = this._resolveFieldType(fieldName, structFieldTypes);
131895
132499
  const value = CodeGenState.withExpectedType(
131896
132500
  fieldType,
131897
132501
  () => this.generateExpression(field.expression())
@@ -131907,10 +132511,35 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
131907
132511
  return "{}";
131908
132512
  }
131909
132513
  const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
132514
+ return this.formatStructInitializer(typeName, castType, fieldInits);
132515
+ }
132516
+ formatStructInitializer(typeName, castType, fieldInits) {
132517
+ const initializer = `{ ${fieldInits.join(", ")} }`;
132518
+ if (CodeGenState.inDeclarationInit) {
132519
+ return initializer;
132520
+ }
131910
132521
  if (CodeGenState.cppMode && (typeName.startsWith("struct {") || typeName.startsWith("union {"))) {
131911
- return `{ ${fieldInits.join(", ")} }`;
132522
+ return initializer;
131912
132523
  }
131913
- return `(${castType}){ ${fieldInits.join(", ")} }`;
132524
+ if (!CodeGenState.inFunctionBody) {
132525
+ return initializer;
132526
+ }
132527
+ return `(${castType})${initializer}`;
132528
+ }
132529
+ /**
132530
+ * Resolve the C type string for a named struct field, converting C++ underscore-separated
132531
+ * names to :: notation. Returns undefined if the field is not in the type map.
132532
+ * Issue #502: C-Next stores C++ types with _ separator; codegen needs ::.
132533
+ */
132534
+ _resolveFieldType(fieldName, structFieldTypes) {
132535
+ if (!structFieldTypes?.has(fieldName)) return void 0;
132536
+ const fieldType = structFieldTypes.get(fieldName);
132537
+ if (!fieldType.includes("_")) return fieldType;
132538
+ const parts = fieldType.split("_");
132539
+ if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
132540
+ return parts.join("::");
132541
+ }
132542
+ return fieldType;
131914
132543
  }
131915
132544
  /**
131916
132545
  * ADR-035: Generate array initializer
@@ -132053,7 +132682,9 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
132053
132682
  isCallbackCompatible,
132054
132683
  forcePassByReference,
132055
132684
  forceConst,
132056
- isTypedefStructType: (t) => CodeGenState.symbolTable?.isTypedefStructType(t) ?? false
132685
+ isTypedefStructType: (t) => CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
132686
+ // Issue #995: Opaque handles should not get auto-const
132687
+ isOpaqueType: (t) => CodeGenState.isOpaqueType(t)
132057
132688
  });
132058
132689
  return ParameterSignatureBuilder_default.build(input, CppModeHelper_default.refOrPtr());
132059
132690
  }
@@ -132284,26 +132915,13 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
132284
132915
  // _generateVariableInitializer, _validateIntegerInitializer, _finalizeCppClassAssignments,
132285
132916
  // and _generateConstructorDecl have been extracted to VariableDeclHelper.ts
132286
132917
  /**
132287
- * Get zero initializer for array types.
132288
- * Issue #379: C++ class arrays must use {} instead of {0}
132918
+ * Brace initializer that zero-initializes an aggregate (struct or array).
132919
+ * Issue #379 / #1004: C++ uses value-initialization ({}), which is valid for
132920
+ * any aggregate element type (POD, struct, class) including enum-first
132921
+ * structs where {0} is an invalid int->enum narrowing; C uses {0}.
132289
132922
  */
132290
- _getArrayZeroInitializer(typeCtx) {
132291
- if (typeCtx.userType()) {
132292
- const typeName = typeCtx.userType().getText();
132293
- if (this._needsEmptyBraceInit(typeName)) {
132294
- return "{}";
132295
- }
132296
- }
132297
- if (typeCtx.arrayType()?.userType()) {
132298
- const typeName = typeCtx.arrayType().userType().getText();
132299
- if (this._needsEmptyBraceInit(typeName)) {
132300
- return "{}";
132301
- }
132302
- }
132303
- if (typeCtx.templateType()) {
132304
- return "{}";
132305
- }
132306
- return "{0}";
132923
+ _getAggregateZeroInitBrace() {
132924
+ return CodeGenState.cppMode ? "{}" : "{0}";
132307
132925
  }
132308
132926
  /**
132309
132927
  * Get zero initializer for an enum type.
@@ -132328,49 +132946,35 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
132328
132946
  }
132329
132947
  /**
132330
132948
  * Resolve full type name from any TypeContext variant.
132331
- * Returns { name, separator, checkCppType } or null if not a named type.
132949
+ * Returns { name, separator } or null if not a named type.
132332
132950
  * ADR-016: Handles scoped, global, qualified, and user types
132333
- * checkCppType: only true for userType (original behavior preserved)
132334
132951
  */
132335
132952
  _resolveTypeNameFromContext(typeCtx) {
132336
132953
  if (typeCtx.scopedType()) {
132337
132954
  const localName = typeCtx.scopedType().IDENTIFIER().getText();
132338
132955
  const name = CodeGenState.currentScope ? `${CodeGenState.currentScope}_${localName}` : localName;
132339
- return { name, separator: "_", checkCppType: false };
132956
+ return { name, separator: "_" };
132340
132957
  }
132341
132958
  if (typeCtx.globalType()) {
132342
132959
  return {
132343
132960
  name: typeCtx.globalType().IDENTIFIER().getText(),
132344
- separator: "_",
132345
- checkCppType: false
132961
+ separator: "_"
132346
132962
  };
132347
132963
  }
132348
132964
  if (typeCtx.qualifiedType()) {
132349
132965
  const parts = typeCtx.qualifiedType().IDENTIFIER();
132350
132966
  const name = this.resolveQualifiedType(parts.map((id) => id.getText()));
132351
132967
  const separator = name.includes("::") ? "::" : "_";
132352
- return { name, separator, checkCppType: false };
132968
+ return { name, separator };
132353
132969
  }
132354
132970
  if (typeCtx.userType()) {
132355
132971
  return {
132356
132972
  name: typeCtx.userType().getText(),
132357
- separator: "_",
132358
- checkCppType: true
132973
+ separator: "_"
132359
132974
  };
132360
132975
  }
132361
132976
  return null;
132362
132977
  }
132363
- /**
132364
- * Check if a type needs empty brace initialization {}.
132365
- * Issue #304: C++ types with constructors may fail with {0}
132366
- * Issue #309: Unknown user types in C++ mode may have non-trivial constructors
132367
- */
132368
- _needsEmptyBraceInit(typeName) {
132369
- if (this.isCppType(typeName)) {
132370
- return true;
132371
- }
132372
- return CodeGenState.cppMode && !this.isKnownStruct(typeName);
132373
- }
132374
132978
  /**
132375
132979
  * Generate a safe bit mask expression.
132376
132980
  * Avoids undefined behavior when width >= 32 for 32-bit integers.
@@ -132750,7 +133354,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
132750
133354
  const maxComparison = `((${floatCastType})${maxValue})`;
132751
133355
  const finalCast = CppModeHelper_default.cast(targetType, `(${expr})`);
132752
133356
  const castMax = CppModeHelper_default.cast(targetType, maxValue);
132753
- const castMin = minValue === "0" ? "0" : CppModeHelper_default.cast(targetType, minValue);
133357
+ const castMin = CppModeHelper_default.cast(targetType, minValue);
132754
133358
  return `((${expr}) > ${maxComparison} ? ${castMax} : (${expr}) < ${minComparison} ? ${castMin} : ${finalCast})`;
132755
133359
  }
132756
133360
  /**
@@ -133126,6 +133730,30 @@ var generateEnumHeader_default = generateEnumHeader;
133126
133730
 
133127
133731
  // src/transpiler/output/headers/generators/generateStructHeader.ts
133128
133732
  var { mapType: mapType2 } = mapType_default;
133733
+ function resolveFieldCType(fieldType, input) {
133734
+ const callbackInfo = input.callbackTypes?.get(fieldType);
133735
+ if (callbackInfo) {
133736
+ return callbackInfo.typedefName;
133737
+ }
133738
+ const convertedType = CppNamespaceUtils_default.convertToCppNamespace(
133739
+ fieldType,
133740
+ input.symbolTable
133741
+ );
133742
+ return mapType2(convertedType);
133743
+ }
133744
+ function generateFieldLine(fieldName, cType, dims) {
133745
+ const dimSuffix = dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
133746
+ const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
133747
+ if (!embeddedMatch) {
133748
+ return ` ${cType} ${fieldName}${dimSuffix};`;
133749
+ }
133750
+ const baseType = embeddedMatch[1];
133751
+ const embeddedDim = embeddedMatch[2];
133752
+ if (dims && dims.length > 0) {
133753
+ return ` ${baseType} ${fieldName}${dimSuffix};`;
133754
+ }
133755
+ return ` ${baseType} ${fieldName}[${embeddedDim}];`;
133756
+ }
133129
133757
  function generateStructHeader(name, input) {
133130
133758
  const fields = input.structFields.get(name);
133131
133759
  if (!fields || fields.size === 0) {
@@ -133135,25 +133763,9 @@ function generateStructHeader(name, input) {
133135
133763
  const lines = [];
133136
133764
  lines.push(`typedef struct ${name} {`);
133137
133765
  for (const [fieldName, fieldType] of fields) {
133138
- const convertedType = CppNamespaceUtils_default.convertToCppNamespace(
133139
- fieldType,
133140
- input.symbolTable
133141
- );
133142
- const cType = mapType2(convertedType);
133766
+ const cType = resolveFieldCType(fieldType, input);
133143
133767
  const dims = dimensions?.get(fieldName);
133144
- const dimSuffix = dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
133145
- const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
133146
- if (embeddedMatch) {
133147
- const baseType = embeddedMatch[1];
133148
- const embeddedDim = embeddedMatch[2];
133149
- if (dims && dims.length > 0) {
133150
- lines.push(` ${baseType} ${fieldName}${dimSuffix};`);
133151
- } else {
133152
- lines.push(` ${baseType} ${fieldName}[${embeddedDim}];`);
133153
- }
133154
- } else {
133155
- lines.push(` ${cType} ${fieldName}${dimSuffix};`);
133156
- }
133768
+ lines.push(generateFieldLine(fieldName, cType, dims));
133157
133769
  }
133158
133770
  lines.push(`} ${name};`);
133159
133771
  return lines.join("\n");
@@ -133546,6 +134158,59 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
133546
134158
  lines.push("");
133547
134159
  return lines;
133548
134160
  }
134161
+ /**
134162
+ * ADR-029: Generate forward declarations for structs used in callback typedefs.
134163
+ * This ensures struct types can be used in callback parameter types before
134164
+ * the full struct definition.
134165
+ */
134166
+ static generateCallbackStructForwardDecls(structs, typeInput) {
134167
+ if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
134168
+ return [];
134169
+ }
134170
+ const usedStructTypes = /* @__PURE__ */ new Set();
134171
+ for (const [, cbInfo] of typeInput.callbackTypes) {
134172
+ for (const p of cbInfo.parameters) {
134173
+ if (p.isStruct) {
134174
+ usedStructTypes.add(p.type);
134175
+ }
134176
+ }
134177
+ }
134178
+ if (usedStructTypes.size === 0) {
134179
+ return [];
134180
+ }
134181
+ const localStructNames = new Set(structs.map((s) => s.name));
134182
+ const lines = [];
134183
+ for (const structType of usedStructTypes) {
134184
+ if (localStructNames.has(structType)) {
134185
+ lines.push(`typedef struct ${structType} ${structType};`);
134186
+ }
134187
+ }
134188
+ return lines.length > 0 ? [...lines, ""] : [];
134189
+ }
134190
+ /**
134191
+ * ADR-029: Generate callback typedef section
134192
+ * Generates function pointer typedefs for callbacks used as struct field types
134193
+ */
134194
+ static generateCallbackTypedefSection(typeInput, isCppMode) {
134195
+ if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
134196
+ return [];
134197
+ }
134198
+ const lines = ["/* Callback typedefs */"];
134199
+ for (const [, cbInfo] of typeInput.callbackTypes) {
134200
+ const params = cbInfo.parameters.length > 0 ? cbInfo.parameters.map((p) => {
134201
+ if (p.isStruct) {
134202
+ const ptrOrRef = isCppMode ? "&" : "*";
134203
+ return `${p.type}${ptrOrRef}`;
134204
+ }
134205
+ return p.type;
134206
+ }).join(", ") : "void";
134207
+ lines.push(
134208
+ `typedef ${cbInfo.returnType} (*${cbInfo.typedefName})(${params});`
134209
+ );
134210
+ }
134211
+ lines.push("");
134212
+ return lines;
134213
+ }
133549
134214
  /**
133550
134215
  * Generate struct and class definitions section
133551
134216
  */
@@ -133673,6 +134338,14 @@ var BaseHeaderGenerator = class {
133673
134338
  ...HeaderGeneratorUtils_default.generateEnumSection(groups.enums, typeInput),
133674
134339
  ...HeaderGeneratorUtils_default.generateBitmapSection(groups.bitmaps, typeInput),
133675
134340
  ...HeaderGeneratorUtils_default.generateTypeAliasSection(groups.types),
134341
+ ...HeaderGeneratorUtils_default.generateCallbackStructForwardDecls(
134342
+ groups.structs,
134343
+ typeInput
134344
+ ),
134345
+ ...HeaderGeneratorUtils_default.generateCallbackTypedefSection(
134346
+ typeInput,
134347
+ options.cppMode
134348
+ ),
133676
134349
  ...HeaderGeneratorUtils_default.generateStructSection(
133677
134350
  groups.structs,
133678
134351
  groups.classes,
@@ -134011,24 +134684,32 @@ var EnumCollector = class {
134011
134684
  var EnumCollector_default = EnumCollector;
134012
134685
 
134013
134686
  // src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts
134014
- function resolveScopedType(scopedTypeCtx, scopeName) {
134015
- const typeName = scopedTypeCtx.IDENTIFIER().getText();
134016
- return scopeName ? `${scopeName}_${typeName}` : typeName;
134017
- }
134018
134687
  function resolveStringType(stringCtx) {
134019
134688
  const intLiteral = stringCtx.INTEGER_LITERAL();
134020
134689
  return intLiteral ? `string<${intLiteral.getText()}>` : "string";
134021
134690
  }
134022
- function resolveArrayInnerType(arrayTypeCtx) {
134023
- if (arrayTypeCtx.primitiveType()) {
134024
- return arrayTypeCtx.primitiveType().getText();
134691
+ function dispatchTypeResolution(accessors, scopeName) {
134692
+ if (accessors.scopedType()) {
134693
+ const typeName = accessors.scopedType().IDENTIFIER().getText();
134694
+ return scopeName ? `${scopeName}_${typeName}` : typeName;
134695
+ }
134696
+ if (accessors.globalType()) {
134697
+ return accessors.globalType().IDENTIFIER().getText();
134698
+ }
134699
+ if (accessors.qualifiedType()) {
134700
+ const identifiers = accessors.qualifiedType().IDENTIFIER();
134701
+ return identifiers.map((id) => id.getText()).join("_");
134025
134702
  }
134026
- if (arrayTypeCtx.userType()) {
134027
- return arrayTypeCtx.userType().getText();
134703
+ if (accessors.userType()) {
134704
+ return accessors.userType().getText();
134028
134705
  }
134029
- const text = arrayTypeCtx.getText();
134030
- const bracketIdx = text.indexOf("[");
134031
- return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
134706
+ if (accessors.primitiveType()) {
134707
+ return accessors.primitiveType().getText();
134708
+ }
134709
+ if (accessors.stringType()) {
134710
+ return resolveStringType(accessors.stringType());
134711
+ }
134712
+ return null;
134032
134713
  }
134033
134714
  var TypeUtils = class {
134034
134715
  /**
@@ -134042,27 +134723,18 @@ var TypeUtils = class {
134042
134723
  */
134043
134724
  static getTypeName(ctx, scopeName) {
134044
134725
  if (!ctx) return "void";
134045
- if (ctx.scopedType()) {
134046
- return resolveScopedType(ctx.scopedType(), scopeName);
134047
- }
134048
- if (ctx.globalType()) {
134049
- return ctx.globalType().IDENTIFIER().getText();
134050
- }
134051
- if (ctx.qualifiedType()) {
134052
- const identifiers = ctx.qualifiedType().IDENTIFIER();
134053
- return identifiers.map((id) => id.getText()).join("_");
134054
- }
134055
- if (ctx.userType()) {
134056
- return ctx.userType().getText();
134057
- }
134058
- if (ctx.primitiveType()) {
134059
- return ctx.primitiveType().getText();
134060
- }
134061
- if (ctx.stringType()) {
134062
- return resolveStringType(ctx.stringType());
134063
- }
134064
134726
  if (ctx.arrayType()) {
134065
- return resolveArrayInnerType(ctx.arrayType());
134727
+ const result2 = dispatchTypeResolution(ctx.arrayType(), scopeName);
134728
+ if (result2 !== null) {
134729
+ return result2;
134730
+ }
134731
+ const text = ctx.arrayType().getText();
134732
+ const bracketIdx = text.indexOf("[");
134733
+ return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
134734
+ }
134735
+ const result = dispatchTypeResolution(ctx, scopeName);
134736
+ if (result !== null) {
134737
+ return result;
134066
134738
  }
134067
134739
  return ctx.getText();
134068
134740
  }
@@ -134428,13 +135100,22 @@ var VariableCollector = class _VariableCollector {
134428
135100
  return dimensions;
134429
135101
  }
134430
135102
  /**
134431
- * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr).
135103
+ * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr, u16[] arr).
135104
+ * Handles size inference from initializer when dimension is empty.
134432
135105
  */
134433
- static collectArrayTypeDimensions(arrayTypeCtx, constValues) {
135106
+ static collectArrayTypeDimensions(arrayTypeCtx, constValues, initExpr) {
134434
135107
  const dimensions = [];
134435
135108
  for (const dim of arrayTypeCtx.arrayTypeDimension()) {
134436
135109
  const sizeExpr = dim.expression();
134437
- if (!sizeExpr) continue;
135110
+ if (!sizeExpr) {
135111
+ if (initExpr) {
135112
+ const inferredSize = ArrayInitializerUtils_default.getInferredSize(initExpr);
135113
+ if (inferredSize !== void 0) {
135114
+ dimensions.push(inferredSize);
135115
+ }
135116
+ }
135117
+ continue;
135118
+ }
134438
135119
  const dimText = sizeExpr.getText();
134439
135120
  const literalSize = LiteralUtils_default.parseIntegerLiteral(dimText);
134440
135121
  if (literalSize !== void 0) {
@@ -134476,7 +135157,8 @@ var VariableCollector = class _VariableCollector {
134476
135157
  arrayDimensions.push(
134477
135158
  ..._VariableCollector.collectArrayTypeDimensions(
134478
135159
  arrayTypeCtx,
134479
- constValues
135160
+ constValues,
135161
+ initExpr
134480
135162
  )
134481
135163
  );
134482
135164
  }
@@ -138838,19 +139520,28 @@ var InitializationListener = class extends CNextListener {
138838
139520
  if (!baseId) {
138839
139521
  return;
138840
139522
  }
139523
+ const target = this._resolveAssignmentTarget(baseId, postfixOps);
139524
+ const isCompoundAssignment = ctx.assignmentOperator().ASSIGN() === null;
139525
+ if (isCompoundAssignment) {
139526
+ const { line, column } = ParserUtils_default.getPosition(ctx);
139527
+ this.analyzer.checkRead(target.varName, line, column, target.fieldName);
139528
+ }
139529
+ this.analyzer.recordAssignment(target.varName, target.fieldName);
139530
+ };
139531
+ /**
139532
+ * Resolve an assignment target to its variable name and optional field name.
139533
+ * Single classification path used by both read checks and assignment recording.
139534
+ */
139535
+ _resolveAssignmentTarget(baseId, postfixOps) {
138841
139536
  if (postfixOps.length === 0) {
138842
- this.analyzer.recordAssignment(baseId);
138843
- return;
139537
+ return { varName: baseId };
138844
139538
  }
138845
139539
  const { identifiers, hasSubscript } = PostfixAnalysisUtils_default(baseId, postfixOps);
138846
139540
  if (identifiers.length >= 2 && !hasSubscript) {
138847
- const varName = identifiers[0];
138848
- const fieldName = identifiers[1];
138849
- this.analyzer.recordAssignment(varName, fieldName);
138850
- } else {
138851
- this.analyzer.recordAssignment(baseId);
139541
+ return { varName: identifiers[0], fieldName: identifiers[1] };
138852
139542
  }
138853
- };
139543
+ return { varName: baseId };
139544
+ }
138854
139545
  // ========================================================================
138855
139546
  // Function Call Arguments (ADR-006: pass-by-reference may initialize)
138856
139547
  // ========================================================================
@@ -139088,27 +139779,128 @@ var InitializationAnalyzer = class {
139088
139779
  }
139089
139780
  /**
139090
139781
  * Process scope member variable declarations (ADR-016)
139782
+ * Issue #1019: Scope members require explicit initialization like locals
139091
139783
  */
139092
139784
  _processScopeMembers(decl) {
139093
139785
  const scopeDecl = decl.scopeDeclaration();
139094
139786
  if (!scopeDecl) return;
139095
139787
  const scopeName = scopeDecl.IDENTIFIER().getText();
139788
+ const assignedMembers = this._findAssignedScopeMembers(scopeDecl);
139096
139789
  for (const member of scopeDecl.scopeMember()) {
139097
- this._processScopeMemberVariable(member, scopeName);
139790
+ this._processScopeMemberVariable(member, scopeName, assignedMembers);
139791
+ }
139792
+ }
139793
+ /**
139794
+ * Scan all functions in a scope to find which members are assigned.
139795
+ * Issue #1019: A member assigned in ANY function is considered initialized
139796
+ * for reads in other functions within the same scope.
139797
+ */
139798
+ _findAssignedScopeMembers(scopeDecl) {
139799
+ const assigned = /* @__PURE__ */ new Set();
139800
+ for (const member of scopeDecl.scopeMember()) {
139801
+ const funcDecl = member.functionDeclaration();
139802
+ if (!funcDecl) continue;
139803
+ const body = funcDecl.block();
139804
+ if (!body) continue;
139805
+ this._collectAssignmentsInBlock(body, assigned);
139806
+ }
139807
+ return assigned;
139808
+ }
139809
+ /**
139810
+ * Recursively collect variable names that are assigned in a block.
139811
+ * Looks for assignment statements targeting bare identifiers or this.member.
139812
+ */
139813
+ _collectAssignmentsInBlock(block, assigned) {
139814
+ for (const stmt of block.statement()) {
139815
+ this._collectAssignmentsInStatement(stmt, assigned);
139816
+ }
139817
+ }
139818
+ /**
139819
+ * Collect assignments from a single statement, recursing into nested blocks.
139820
+ */
139821
+ _collectAssignmentsInStatement(stmt, assigned) {
139822
+ this._collectDirectAssignment(stmt, assigned);
139823
+ this._collectFromControlFlow(stmt, assigned);
139824
+ this._collectFromSwitch(stmt, assigned);
139825
+ this._collectFromBlock(stmt, assigned);
139826
+ }
139827
+ /**
139828
+ * Collect assignment from the statement itself (if it's an assignment).
139829
+ */
139830
+ _collectDirectAssignment(stmt, assigned) {
139831
+ const assignStmt = stmt.assignmentStatement();
139832
+ if (!assignStmt) return;
139833
+ const target = assignStmt.assignmentTarget();
139834
+ if (!target) return;
139835
+ const id = target.IDENTIFIER()?.getText();
139836
+ if (id) {
139837
+ assigned.add(id);
139838
+ }
139839
+ }
139840
+ /**
139841
+ * Recurse into control flow statements (if, while, do-while, for).
139842
+ */
139843
+ _collectFromControlFlow(stmt, assigned) {
139844
+ const ifStmt = stmt.ifStatement();
139845
+ if (ifStmt) {
139846
+ for (const childStmt of ifStmt.statement()) {
139847
+ this._collectAssignmentsInStatement(childStmt, assigned);
139848
+ }
139849
+ }
139850
+ const whileBody = stmt.whileStatement()?.statement();
139851
+ if (whileBody) {
139852
+ this._collectAssignmentsInStatement(whileBody, assigned);
139853
+ }
139854
+ const doWhileBody = stmt.doWhileStatement()?.block();
139855
+ if (doWhileBody) {
139856
+ this._collectAssignmentsInBlock(doWhileBody, assigned);
139857
+ }
139858
+ const forBody = stmt.forStatement()?.statement();
139859
+ if (forBody) {
139860
+ this._collectAssignmentsInStatement(forBody, assigned);
139861
+ }
139862
+ }
139863
+ /**
139864
+ * Recurse into switch statement cases.
139865
+ */
139866
+ _collectFromSwitch(stmt, assigned) {
139867
+ const switchStmt = stmt.switchStatement();
139868
+ if (!switchStmt) return;
139869
+ for (const switchCase of switchStmt.switchCase()) {
139870
+ const caseBlock = switchCase.block();
139871
+ if (caseBlock) {
139872
+ this._collectAssignmentsInBlock(caseBlock, assigned);
139873
+ }
139874
+ }
139875
+ const defaultBlock = switchStmt.defaultCase()?.block();
139876
+ if (defaultBlock) {
139877
+ this._collectAssignmentsInBlock(defaultBlock, assigned);
139878
+ }
139879
+ }
139880
+ /**
139881
+ * Recurse into standalone block statement.
139882
+ */
139883
+ _collectFromBlock(stmt, assigned) {
139884
+ const block = stmt.block();
139885
+ if (block) {
139886
+ this._collectAssignmentsInBlock(block, assigned);
139098
139887
  }
139099
139888
  }
139100
139889
  /**
139101
139890
  * Process a single scope member variable
139102
139891
  */
139103
- _processScopeMemberVariable(member, scopeName) {
139892
+ _processScopeMemberVariable(member, scopeName, assignedMembers) {
139104
139893
  const memberVar = member.variableDeclaration();
139105
139894
  if (!memberVar) return;
139106
139895
  const varName = memberVar.IDENTIFIER().getText();
139107
139896
  const fullName = `${scopeName}_${varName}`;
139108
139897
  const { line, column } = ParserUtils_default.getPosition(memberVar);
139109
139898
  const typeName = this._extractUserTypeName(memberVar.type());
139110
- this.declareVariable(varName, line, column, true, typeName);
139111
- this.declareVariable(fullName, line, column, true, typeName);
139899
+ const hasInlineInit = memberVar.expression() !== null;
139900
+ const isAssignedInScope = assignedMembers.has(varName);
139901
+ const hasInitializer = hasInlineInit || isAssignedInScope;
139902
+ this.declareVariable(varName, line, column, hasInitializer, typeName);
139903
+ this.declareVariable(fullName, line, column, hasInitializer, typeName);
139112
139904
  }
139113
139905
  /**
139114
139906
  * Extract user type name from a type context
@@ -139607,14 +140399,18 @@ var FunctionCallListener = class extends CNextListener {
139607
140399
  const primary = ctx.primaryExpression();
139608
140400
  const baseName = this.extractBaseName(primary);
139609
140401
  if (!baseName) return;
139610
- const { resolvedName, foundCall } = this.resolveCallTarget(ops, baseName);
140402
+ const { resolvedName, foundCall, isGlobalCall } = this.resolveCallTarget(
140403
+ ops,
140404
+ baseName
140405
+ );
139611
140406
  if (!foundCall) return;
139612
140407
  const { line, column } = ParserUtils_default.getPosition(ctx);
139613
140408
  this.analyzer.checkFunctionCall(
139614
140409
  resolvedName,
139615
140410
  line,
139616
140411
  column,
139617
- this.currentScope
140412
+ this.currentScope,
140413
+ isGlobalCall
139618
140414
  );
139619
140415
  };
139620
140416
  /**
@@ -139627,6 +140423,9 @@ var FunctionCallListener = class extends CNextListener {
139627
140423
  if (primary.THIS()) {
139628
140424
  return "this";
139629
140425
  }
140426
+ if (primary.GLOBAL()) {
140427
+ return "global";
140428
+ }
139630
140429
  return null;
139631
140430
  }
139632
140431
  /**
@@ -139636,11 +140435,15 @@ var FunctionCallListener = class extends CNextListener {
139636
140435
  */
139637
140436
  resolveCallTarget(ops, baseName) {
139638
140437
  let resolvedName = baseName;
140438
+ let isGlobalCall = baseName === "global";
139639
140439
  for (const op of ops) {
139640
140440
  if (op.IDENTIFIER()) {
139641
140441
  const resolved = this.resolveMemberAccess(resolvedName, op);
139642
140442
  if (resolved === null) {
139643
- return { resolvedName, foundCall: false };
140443
+ return { resolvedName, foundCall: false, isGlobalCall };
140444
+ }
140445
+ if (isGlobalCall && this.analyzer.isScope(resolvedName)) {
140446
+ isGlobalCall = false;
139644
140447
  }
139645
140448
  resolvedName = resolved;
139646
140449
  continue;
@@ -139648,11 +140451,11 @@ var FunctionCallListener = class extends CNextListener {
139648
140451
  if (op.argumentList() || op.getChildCount() === 2) {
139649
140452
  const text = op.getText();
139650
140453
  if (text.startsWith("(")) {
139651
- return { resolvedName, foundCall: true };
140454
+ return { resolvedName, foundCall: true, isGlobalCall };
139652
140455
  }
139653
140456
  }
139654
140457
  }
139655
- return { resolvedName, foundCall: false };
140458
+ return { resolvedName, foundCall: false, isGlobalCall };
139656
140459
  }
139657
140460
  /**
139658
140461
  * Resolve member access pattern. Returns new name or null if not a C-Next function.
@@ -139662,6 +140465,9 @@ var FunctionCallListener = class extends CNextListener {
139662
140465
  if (resolvedName === "this" && this.currentScope) {
139663
140466
  return `${this.currentScope}_${memberName}`;
139664
140467
  }
140468
+ if (resolvedName === "global") {
140469
+ return memberName;
140470
+ }
139665
140471
  if (this.analyzer.isScope(resolvedName)) {
139666
140472
  return `${resolvedName}_${memberName}`;
139667
140473
  }
@@ -140060,8 +140866,9 @@ var FunctionCallAnalyzer = class {
140060
140866
  * @param line Source line number
140061
140867
  * @param column Source column number
140062
140868
  * @param currentScope The current scope name (if inside a scope)
140869
+ * @param isGlobalCall Whether the call used global. prefix
140063
140870
  */
140064
- checkFunctionCall(name, line, column, currentScope) {
140871
+ checkFunctionCall(name, line, column, currentScope, isGlobalCall = false) {
140065
140872
  if (this.currentFunctionName && name === this.currentFunctionName) {
140066
140873
  this.errors.push({
140067
140874
  code: "E0423",
@@ -140087,17 +140894,19 @@ var FunctionCallAnalyzer = class {
140087
140894
  if (this.callableVariables.has(name)) {
140088
140895
  return;
140089
140896
  }
140090
- if (currentScope) {
140897
+ if (currentScope && !isGlobalCall) {
140091
140898
  const qualifiedName = `${currentScope}_${name}`;
140092
140899
  if (this.definedFunctions.has(qualifiedName)) {
140093
140900
  return;
140094
140901
  }
140095
140902
  }
140903
+ const isLocalFunction = this.allLocalFunctions.has(name);
140096
140904
  const header = this.findStdlibHeader(name);
140097
- let message = `function '${name}' called before definition`;
140098
- if (header) {
140099
- message += `; hint: '${name}' is available from ${header} \u2014 try global.${name}()`;
140100
- }
140905
+ const message = this.buildUndefinedFunctionMessage(
140906
+ name,
140907
+ header,
140908
+ isGlobalCall && !isLocalFunction
140909
+ );
140101
140910
  this.errors.push({
140102
140911
  code: "E0422",
140103
140912
  functionName: name,
@@ -140106,6 +140915,23 @@ var FunctionCallAnalyzer = class {
140106
140915
  message
140107
140916
  });
140108
140917
  }
140918
+ /**
140919
+ * Issue #985: Build error message for undefined function calls.
140920
+ * Adjusts hint based on whether the call used global. prefix.
140921
+ */
140922
+ buildUndefinedFunctionMessage(name, header, isGlobalCall) {
140923
+ if (isGlobalCall && header) {
140924
+ return `'${name}' is not declared in any included header; add #include <${header}>`;
140925
+ }
140926
+ if (isGlobalCall) {
140927
+ return `'${name}' is not declared in any included header`;
140928
+ }
140929
+ let message = `function '${name}' called before definition`;
140930
+ if (header) {
140931
+ message += `; hint: '${name}' is available from ${header} \u2014 try global.${name}()`;
140932
+ }
140933
+ return message;
140934
+ }
140109
140935
  /**
140110
140936
  * Check if a function is defined externally (from included files)
140111
140937
  * This includes C/C++ headers AND C-Next includes.
@@ -140631,46 +141457,56 @@ var ArrayIndexTypeAnalyzer_default = ArrayIndexTypeAnalyzer;
140631
141457
  import { ParseTreeWalker as ParseTreeWalker9 } from "antlr4ng";
140632
141458
  var SignedVariableCollector = class extends CNextListener {
140633
141459
  signedVars = /* @__PURE__ */ new Set();
141460
+ // Track all variable types (for resolving struct member chains)
141461
+ varTypes = /* @__PURE__ */ new Map();
140634
141462
  getSignedVars() {
140635
141463
  return this.signedVars;
140636
141464
  }
141465
+ getVarTypes() {
141466
+ return this.varTypes;
141467
+ }
140637
141468
  /**
140638
- * Track a typed identifier if it has a signed type
141469
+ * Track a typed identifier - add to signedVars if signed, always track type
140639
141470
  */
140640
- trackIfSigned(typeCtx, identifier) {
140641
- if (!typeCtx) return;
141471
+ trackType(typeCtx, identifier) {
141472
+ if (!typeCtx || !identifier) return;
140642
141473
  const typeName = typeCtx.getText();
140643
- if (!TypeConstants_default.SIGNED_TYPES.includes(typeName)) return;
140644
- if (!identifier) return;
140645
- this.signedVars.add(identifier.getText());
141474
+ const varName = identifier.getText();
141475
+ this.varTypes.set(varName, typeName);
141476
+ if (TypeConstants_default.SIGNED_TYPES.includes(typeName)) {
141477
+ this.signedVars.add(varName);
141478
+ }
140646
141479
  }
140647
141480
  /**
140648
141481
  * Track variable declarations with signed types
140649
141482
  */
140650
141483
  enterVariableDeclaration = (ctx) => {
140651
- this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
141484
+ this.trackType(ctx.type(), ctx.IDENTIFIER());
140652
141485
  };
140653
141486
  /**
140654
141487
  * Track function parameters with signed types
140655
141488
  */
140656
141489
  enterParameter = (ctx) => {
140657
- this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
141490
+ this.trackType(ctx.type(), ctx.IDENTIFIER());
140658
141491
  };
140659
141492
  /**
140660
141493
  * Track for-loop variable declarations with signed types
140661
141494
  */
140662
141495
  enterForVarDecl = (ctx) => {
140663
- this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
141496
+ this.trackType(ctx.type(), ctx.IDENTIFIER());
140664
141497
  };
140665
141498
  };
140666
141499
  var SignedShiftListener = class extends CNextListener {
140667
141500
  analyzer;
140668
141501
  // eslint-disable-next-line @typescript-eslint/lines-between-class-members
140669
141502
  signedVars;
140670
- constructor(analyzer, signedVars) {
141503
+ // eslint-disable-next-line @typescript-eslint/lines-between-class-members
141504
+ varTypes;
141505
+ constructor(analyzer, signedVars, varTypes) {
140671
141506
  super();
140672
141507
  this.analyzer = analyzer;
140673
141508
  this.signedVars = signedVars;
141509
+ this.varTypes = varTypes;
140674
141510
  }
140675
141511
  /**
140676
141512
  * Check shift expressions for signed operands
@@ -140691,6 +141527,69 @@ var SignedShiftListener = class extends CNextListener {
140691
141527
  }
140692
141528
  }
140693
141529
  };
141530
+ /**
141531
+ * Check compound shift-assign statements for signed targets
141532
+ * assignmentStatement: assignmentTarget assignmentOperator expression ';'
141533
+ * Issue #1008: <<<- and >><- must also be rejected on signed types
141534
+ *
141535
+ * Handles both simple identifiers (x <<<- 2) and member chains (s.x <<<- 2)
141536
+ */
141537
+ enterAssignmentStatement = (ctx) => {
141538
+ const opCtx = ctx.assignmentOperator();
141539
+ if (!opCtx) return;
141540
+ const isLeftShiftAssign = opCtx.LSHIFT_ASSIGN() !== null;
141541
+ const isRightShiftAssign = opCtx.RSHIFT_ASSIGN() !== null;
141542
+ if (!isLeftShiftAssign && !isRightShiftAssign) return;
141543
+ const target = ctx.assignmentTarget();
141544
+ if (!target) return;
141545
+ const identifier = target.IDENTIFIER();
141546
+ if (!identifier) return;
141547
+ const baseName = identifier.getText();
141548
+ const postfixOps = target.postfixTargetOp();
141549
+ if (this.isSignedTarget(baseName, postfixOps)) {
141550
+ const operator = isLeftShiftAssign ? "<<<-" : ">><-";
141551
+ const { line, column } = ParserUtils_default.getPosition(target);
141552
+ this.analyzer.addError(line, column, operator);
141553
+ }
141554
+ };
141555
+ /**
141556
+ * Resolve the final type of an assignment target, handling member chains.
141557
+ * Returns true if the final target is a signed type.
141558
+ *
141559
+ * Examples:
141560
+ * - "x" with no postfix ops → check if x is signed
141561
+ * - "s" with postfixOps [".x"] → check if s.x field is signed
141562
+ * - "arr" with postfixOps ["[0]", ".field"] → check if field is signed
141563
+ */
141564
+ isSignedTarget(baseName, postfixOps) {
141565
+ if (postfixOps.length === 0) {
141566
+ return this.signedVars.has(baseName);
141567
+ }
141568
+ let currentType = this.varTypes.get(baseName);
141569
+ if (!currentType) {
141570
+ return false;
141571
+ }
141572
+ for (const op of postfixOps) {
141573
+ const memberIdent = op.IDENTIFIER();
141574
+ if (memberIdent) {
141575
+ const fieldName = memberIdent.getText();
141576
+ const fieldType = CodeGenState.getStructFieldType(
141577
+ currentType,
141578
+ fieldName
141579
+ );
141580
+ if (!fieldType) {
141581
+ return false;
141582
+ }
141583
+ currentType = fieldType;
141584
+ } else {
141585
+ const bracketIndex = currentType.indexOf("[");
141586
+ if (bracketIndex !== -1) {
141587
+ currentType = currentType.substring(0, bracketIndex);
141588
+ }
141589
+ }
141590
+ }
141591
+ return TypeConstants_default.SIGNED_TYPES.includes(currentType);
141592
+ }
140694
141593
  /**
140695
141594
  * Check if an additive expression contains a signed type operand
140696
141595
  */
@@ -140758,7 +141657,8 @@ var SignedShiftAnalyzer = class {
140758
141657
  const collector = new SignedVariableCollector();
140759
141658
  ParseTreeWalker9.DEFAULT.walk(collector, tree);
140760
141659
  const signedVars = collector.getSignedVars();
140761
- const listener = new SignedShiftListener(this, signedVars);
141660
+ const varTypes = collector.getVarTypes();
141661
+ const listener = new SignedShiftListener(this, signedVars, varTypes);
140762
141662
  ParseTreeWalker9.DEFAULT.walk(listener, tree);
140763
141663
  return this.errors;
140764
141664
  }
@@ -142466,7 +143366,12 @@ var Transpiler = class {
142466
143366
  this.state.getAllHeaderDirectives(),
142467
143367
  CodeGenState.symbolTable
142468
143368
  );
142469
- const typeInputWithSymbolTable = typeInput ? { ...typeInput, symbolTable: CodeGenState.symbolTable } : void 0;
143369
+ const callbackTypesForHeader = this._buildCallbackTypesForHeader();
143370
+ const typeInputWithSymbolTable = typeInput ? {
143371
+ ...typeInput,
143372
+ symbolTable: CodeGenState.symbolTable,
143373
+ callbackTypes: callbackTypesForHeader
143374
+ } : void 0;
142470
143375
  const unmodifiedParams = this.codeGenerator.getFunctionUnmodifiedParams();
142471
143376
  const headerSymbols = this.convertToHeaderSymbols(
142472
143377
  exportedSymbols,
@@ -142488,6 +143393,32 @@ var Transpiler = class {
142488
143393
  basename6(sourcePath)
142489
143394
  );
142490
143395
  }
143396
+ /**
143397
+ * ADR-029: Build callback types for header generation.
143398
+ * Only includes callbacks that are actually used as struct field types.
143399
+ * Converts CodeGenState.callbackTypes to the format expected by IHeaderTypeInput.
143400
+ */
143401
+ _buildCallbackTypesForHeader() {
143402
+ const result = /* @__PURE__ */ new Map();
143403
+ const usedCallbackTypes = /* @__PURE__ */ new Set();
143404
+ for (const [, funcName] of CodeGenState.callbackFieldTypes) {
143405
+ usedCallbackTypes.add(funcName);
143406
+ }
143407
+ for (const funcName of usedCallbackTypes) {
143408
+ const cbInfo = CodeGenState.callbackTypes.get(funcName);
143409
+ if (cbInfo) {
143410
+ result.set(funcName, {
143411
+ typedefName: cbInfo.typedefName,
143412
+ returnType: cbInfo.returnType,
143413
+ parameters: cbInfo.parameters.map((p) => ({
143414
+ type: p.type,
143415
+ isStruct: p.isStruct
143416
+ }))
143417
+ });
143418
+ }
143419
+ }
143420
+ return result;
143421
+ }
142491
143422
  /**
142492
143423
  * Collect external enum sources from included C-Next files.
142493
143424
  */
@@ -142534,25 +143465,27 @@ var Transpiler = class {
142534
143465
  );
142535
143466
  const callbackTypedefType = typedefName ? CodeGenState.getTypedefType(typedefName) : void 0;
142536
143467
  if (callbackTypedefType) {
142537
- const updatedParams = TypedefParamParser_default.resolveCallbackParams(
143468
+ const updatedParams2 = TypedefParamParser_default.resolveCallbackParams(
142538
143469
  headerSymbol.parameters,
142539
143470
  callbackTypedefType
142540
143471
  );
142541
- return { ...headerSymbol, parameters: updatedParams };
143472
+ return { ...headerSymbol, parameters: updatedParams2 };
142542
143473
  }
142543
143474
  const unmodified = unmodifiedParams.get(headerSymbol.name);
142544
- if (unmodified) {
142545
- const updatedParams = headerSymbol.parameters.map((param) => {
142546
- const isPointerParam = !param.isConst && !param.isArray && param.type !== "f32" && param.type !== "f64" && param.type !== "ISR" && !knownEnums.has(param.type ?? "");
142547
- const isArrayParam = param.isArray && !param.isConst;
142548
- if ((isPointerParam || isArrayParam) && unmodified.has(param.name)) {
142549
- return { ...param, isAutoConst: true };
142550
- }
142551
- return param;
142552
- });
142553
- return { ...headerSymbol, parameters: updatedParams };
142554
- }
142555
- return headerSymbol;
143475
+ const updatedParams = headerSymbol.parameters.map((param) => {
143476
+ const isOpaque = CodeGenState.isOpaqueType(param.type ?? "");
143477
+ const isPointerParam = !param.isConst && !param.isArray && param.type !== "f32" && param.type !== "f64" && param.type !== "ISR" && !knownEnums.has(param.type ?? "");
143478
+ const shouldAutoConst = unmodified && isPointerParam && unmodified.has(param.name);
143479
+ if (shouldAutoConst || isOpaque) {
143480
+ return {
143481
+ ...param,
143482
+ isAutoConst: shouldAutoConst || void 0,
143483
+ isOpaqueHandle: isOpaque || void 0
143484
+ };
143485
+ }
143486
+ return param;
143487
+ });
143488
+ return { ...headerSymbol, parameters: updatedParams };
142556
143489
  });
142557
143490
  }
142558
143491
  // ===========================================================================