c-next 0.2.7 → 0.2.9

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 (26) hide show
  1. package/dist/index.js +224 -69
  2. package/dist/index.js.map +2 -2
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +4 -1
  5. package/src/transpiler/logic/IncludeExtractor.ts +12 -6
  6. package/src/transpiler/logic/__tests__/IncludeExtractor.test.ts +24 -0
  7. package/src/transpiler/output/codegen/CodeGenerator.ts +32 -24
  8. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
  9. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +16 -16
  10. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +8 -1
  11. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +41 -6
  12. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +15 -2
  13. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +38 -11
  14. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +60 -0
  15. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +2 -1
  16. package/src/transpiler/output/codegen/generators/support/IncludeGenerator.ts +19 -7
  17. package/src/transpiler/output/codegen/generators/support/__tests__/IncludeGenerator.test.ts +68 -0
  18. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +14 -2
  19. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +3 -5
  20. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +56 -8
  21. package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +8 -4
  22. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +5 -6
  23. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +60 -2
  24. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +14 -16
  25. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +25 -0
  26. package/src/transpiler/state/CodeGenState.ts +40 -0
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "c-next",
13
- version: "0.2.7",
13
+ version: "0.2.9",
14
14
  description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
15
15
  packageManager: "npm@11.9.0",
16
16
  type: "module",
@@ -110828,6 +110828,13 @@ var CodeGenState = class {
110828
110828
  static inFunctionBody = false;
110829
110829
  /** Expected type for struct initializers and enum inference */
110830
110830
  static expectedType = null;
110831
+ /**
110832
+ * Suppress bare enum resolution even when expectedType is set.
110833
+ * Issue #872: MISRA 7.2 requires expectedType for U suffix on function args,
110834
+ * but bare enum resolution in function args was never allowed and changing
110835
+ * that would require ADR approval.
110836
+ */
110837
+ static suppressBareEnumResolution = false;
110831
110838
  /** Track args parameter name for main() translation */
110832
110839
  static mainArgsName = null;
110833
110840
  /** ADR-044: Current assignment context for overflow behavior */
@@ -110922,6 +110929,7 @@ var CodeGenState = class {
110922
110929
  this.indentLevel = 0;
110923
110930
  this.inFunctionBody = false;
110924
110931
  this.expectedType = null;
110932
+ this.suppressBareEnumResolution = false;
110925
110933
  this.mainArgsName = null;
110926
110934
  this.assignmentContext = {
110927
110935
  targetName: null,
@@ -110972,6 +110980,32 @@ var CodeGenState = class {
110972
110980
  this.floatBitShadows.clear();
110973
110981
  this.floatShadowCurrent.clear();
110974
110982
  }
110983
+ /**
110984
+ * Execute a function with a temporary expectedType, restoring on completion.
110985
+ * Issue #872: Extracted to eliminate duplicate save/restore pattern and add exception safety.
110986
+ *
110987
+ * @param type - The expected type to set (if falsy, no change is made)
110988
+ * @param fn - The function to execute
110989
+ * @param suppressEnumResolution - If true, suppress bare enum resolution (for MISRA-only contexts)
110990
+ * @returns The result of the function
110991
+ */
110992
+ static withExpectedType(type, fn, suppressEnumResolution = false) {
110993
+ if (!type) {
110994
+ return fn();
110995
+ }
110996
+ const savedType = this.expectedType;
110997
+ const savedSuppress = this.suppressBareEnumResolution;
110998
+ this.expectedType = type;
110999
+ if (suppressEnumResolution) {
111000
+ this.suppressBareEnumResolution = true;
111001
+ }
111002
+ try {
111003
+ return fn();
111004
+ } finally {
111005
+ this.expectedType = savedType;
111006
+ this.suppressBareEnumResolution = savedSuppress;
111007
+ }
111008
+ }
110975
111009
  // ===========================================================================
110976
111010
  // CONVENIENCE LOOKUP METHODS
110977
111011
  // ===========================================================================
@@ -114424,8 +114458,14 @@ var UNSIGNED_TYPES3 = /* @__PURE__ */ new Set([
114424
114458
  "u32",
114425
114459
  "uint8_t",
114426
114460
  "uint16_t",
114427
- "uint32_t"
114461
+ "uint32_t",
114462
+ "size_t"
114463
+ // Array indices (MISRA 7.2)
114428
114464
  ]);
114465
+ function resolveTypedef(typeName) {
114466
+ const underlyingType = CodeGenState.getTypedefType(typeName);
114467
+ return underlyingType ?? typeName;
114468
+ }
114429
114469
  function isNumericIntegerLiteral(text) {
114430
114470
  if (text.startsWith('"') || text.startsWith("'")) {
114431
114471
  return false;
@@ -114471,9 +114511,10 @@ var generateLiteral = (node, _input, state, _orchestrator) => {
114471
114511
  }
114472
114512
  const expectedType = state?.expectedType;
114473
114513
  if (expectedType && isNumericIntegerLiteral(literalText) && !hasUnsignedSuffix(literalText)) {
114474
- if (UNSIGNED_64_TYPES.has(expectedType)) {
114514
+ const resolvedType = resolveTypedef(expectedType);
114515
+ if (UNSIGNED_64_TYPES.has(resolvedType)) {
114475
114516
  literalText = literalText + "ULL";
114476
- } else if (UNSIGNED_TYPES3.has(expectedType)) {
114517
+ } else if (UNSIGNED_TYPES3.has(resolvedType)) {
114477
114518
  literalText = literalText + "U";
114478
114519
  }
114479
114520
  }
@@ -115127,7 +115168,23 @@ var _parameterExpectsAddressOf = (paramType, argType, orchestrator) => {
115127
115168
  return paramBaseType === argType;
115128
115169
  };
115129
115170
  var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115130
- let argCode = orchestrator.generateExpression(e);
115171
+ const argIdentifier = orchestrator.getSimpleIdentifier(e);
115172
+ const paramInfo = argIdentifier ? CodeGenState.currentParameters.get(argIdentifier) : void 0;
115173
+ const isCallbackPromotedParam = paramInfo?.forcePointerSemantics ?? false;
115174
+ if (targetParam?.baseType?.endsWith("*") && isCallbackPromotedParam) {
115175
+ return wrapWithCppEnumCast(
115176
+ argIdentifier,
115177
+ e,
115178
+ targetParam?.baseType,
115179
+ orchestrator
115180
+ );
115181
+ }
115182
+ const argCode = CodeGenState.withExpectedType(
115183
+ targetParam?.baseType,
115184
+ () => orchestrator.generateExpression(e),
115185
+ true
115186
+ // suppressEnumResolution
115187
+ );
115131
115188
  if (!targetParam?.baseType?.endsWith("*")) {
115132
115189
  return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
115133
115190
  }
@@ -115143,10 +115200,13 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115143
115200
  isPointerVariable = true;
115144
115201
  }
115145
115202
  const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115146
- if (needsAddressOf) {
115147
- argCode = `&${argCode}`;
115148
- }
115149
- return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
115203
+ const finalArgCode = needsAddressOf ? `&${argCode}` : argCode;
115204
+ return wrapWithCppEnumCast(
115205
+ finalArgCode,
115206
+ e,
115207
+ targetParam?.baseType,
115208
+ orchestrator
115209
+ );
115150
115210
  };
115151
115211
  var _shouldPassByValue = (funcExpr, idx, targetParam, isCrossFile, orchestrator) => {
115152
115212
  if (!targetParam) return false;
@@ -115193,7 +115253,12 @@ var generateFunctionCall = (funcExpr, argCtx, input, _state, orchestrator) => {
115193
115253
  resolved.isCrossFile,
115194
115254
  orchestrator
115195
115255
  )) {
115196
- const argCode = orchestrator.generateExpression(e);
115256
+ const argCode = CodeGenState.withExpectedType(
115257
+ targetParam?.baseType,
115258
+ () => orchestrator.generateExpression(e),
115259
+ true
115260
+ // suppressEnumResolution
115261
+ );
115197
115262
  return wrapWithCppEnumCast(
115198
115263
  argCode,
115199
115264
  e,
@@ -116545,12 +116610,15 @@ var generateSubscriptAccess = (ctx, input, state, orchestrator, effects) => {
116545
116610
  return output;
116546
116611
  };
116547
116612
  var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116548
- const index = orchestrator.generateExpression(expr);
116613
+ const index = CodeGenState.withExpectedType(
116614
+ "size_t",
116615
+ () => orchestrator.generateExpression(expr)
116616
+ );
116549
116617
  validateNotBitmapMember(ctx, input);
116550
116618
  const isRegisterAccess = checkRegisterAccess(ctx, input);
116551
116619
  const identifierTypeInfo = getIdentifierTypeInfo(ctx, input);
116552
116620
  if (isRegisterAccess) {
116553
- output.result = `((${ctx.result} >> ${index}) & 1)`;
116621
+ output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116554
116622
  return output;
116555
116623
  }
116556
116624
  if (ctx.currentMemberIsArray) {
@@ -116564,7 +116632,7 @@ var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116564
116632
  }
116565
116633
  const isPrimitiveIntMember = ctx.currentStructType && TypeCheckUtils_default.isInteger(ctx.currentStructType);
116566
116634
  if (isPrimitiveIntMember) {
116567
- output.result = `((${ctx.result} >> ${index}) & 1)`;
116635
+ output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116568
116636
  output.currentStructType = void 0;
116569
116637
  return output;
116570
116638
  }
@@ -116622,12 +116690,18 @@ var handleDefaultSubscript = (ctx, index, typeInfo, output) => {
116622
116690
  subscriptCount: 1,
116623
116691
  isRegisterAccess: false
116624
116692
  });
116625
- output.result = subscriptKind === "bit_single" ? `((${ctx.result} >> ${index}) & 1)` : `${ctx.result}[${index}]`;
116693
+ if (subscriptKind === "bit_single") {
116694
+ output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116695
+ } else {
116696
+ output.result = `${ctx.result}[${index}]`;
116697
+ }
116626
116698
  return output;
116627
116699
  };
116628
116700
  var handleBitRangeSubscript = (ctx, exprs, input, state, orchestrator, effects, output) => {
116629
- const start = orchestrator.generateExpression(exprs[0]);
116630
- const width = orchestrator.generateExpression(exprs[1]);
116701
+ const [start, width] = CodeGenState.withExpectedType("size_t", () => [
116702
+ orchestrator.generateExpression(exprs[0]),
116703
+ orchestrator.generateExpression(exprs[1])
116704
+ ]);
116631
116705
  const isFloatType = ctx.primaryTypeInfo?.baseType === "f32" || ctx.primaryTypeInfo?.baseType === "f64";
116632
116706
  if (isFloatType && ctx.rootIdentifier) {
116633
116707
  output.result = handleFloatBitRange(
@@ -116644,7 +116718,7 @@ var handleBitRangeSubscript = (ctx, exprs, input, state, orchestrator, effects,
116644
116718
  );
116645
116719
  } else {
116646
116720
  const mask = orchestrator.generateBitMask(width);
116647
- if (start === "0") {
116721
+ if (start === "0" || start === "0U") {
116648
116722
  output.result = `((${ctx.result}) & ${mask})`;
116649
116723
  } else {
116650
116724
  output.result = `((${ctx.result} >> ${start}) & ${mask})`;
@@ -116679,7 +116753,7 @@ var handleFloatBitRange = (ctx, state, orchestrator, effects) => {
116679
116753
  if (!shadowIsCurrent) {
116680
116754
  orchestrator.addPendingTempDeclaration(`${shadowName}.f = ${ctx.result};`);
116681
116755
  }
116682
- if (ctx.start === "0") {
116756
+ if (ctx.start === "0" || ctx.start === "0U") {
116683
116757
  return `(${shadowName}.u & ${mask})`;
116684
116758
  }
116685
116759
  return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
@@ -117601,7 +117675,11 @@ var FunctionGenerator_default = generateFunction;
117601
117675
  // src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts
117602
117676
  function generateInitializer(varDecl, isArray, orchestrator) {
117603
117677
  if (varDecl.expression()) {
117604
- return ` = ${orchestrator.generateExpression(varDecl.expression())}`;
117678
+ const typeName = orchestrator.generateType(varDecl.type());
117679
+ return CodeGenState.withExpectedType(
117680
+ typeName,
117681
+ () => ` = ${orchestrator.generateExpression(varDecl.expression())}`
117682
+ );
117605
117683
  }
117606
117684
  return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
117607
117685
  }
@@ -118793,7 +118871,7 @@ var CnxFileResolver = class {
118793
118871
  var CnxFileResolver_default = CnxFileResolver;
118794
118872
 
118795
118873
  // src/transpiler/output/codegen/generators/support/IncludeGenerator.ts
118796
- var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs) => {
118874
+ var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs, cppMode) => {
118797
118875
  if (inputs.length === 0) {
118798
118876
  return null;
118799
118877
  }
@@ -118807,25 +118885,33 @@ var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs) => {
118807
118885
  foundPath,
118808
118886
  inputs
118809
118887
  );
118810
- return relativePath ? relativePath.replace(/\.cnx$/, ".h") : null;
118888
+ const ext = cppMode ? ".hpp" : ".h";
118889
+ return relativePath ? relativePath.replace(/\.cnx$/, ext) : null;
118811
118890
  };
118812
118891
  var transformAngleInclude = (includeText, filename, options) => {
118813
- const { sourcePath, includeDirs = [], inputs = [] } = options;
118892
+ const {
118893
+ sourcePath,
118894
+ includeDirs = [],
118895
+ inputs = [],
118896
+ cppMode = false
118897
+ } = options;
118814
118898
  if (sourcePath) {
118815
118899
  const resolvedPath = resolveAngleIncludePath(
118816
118900
  filename,
118817
118901
  sourcePath,
118818
118902
  includeDirs,
118819
- inputs
118903
+ inputs,
118904
+ cppMode
118820
118905
  );
118821
118906
  if (resolvedPath) {
118822
118907
  return includeText.replace(`<${filename}.cnx>`, `<${resolvedPath}>`);
118823
118908
  }
118824
118909
  }
118825
- return includeText.replace(`<${filename}.cnx>`, `<${filename}.h>`);
118910
+ const ext = cppMode ? ".hpp" : ".h";
118911
+ return includeText.replace(`<${filename}.cnx>`, `<${filename}${ext}>`);
118826
118912
  };
118827
118913
  var transformQuoteInclude = (includeText, filepath, options) => {
118828
- const { sourcePath } = options;
118914
+ const { sourcePath, cppMode = false } = options;
118829
118915
  if (sourcePath) {
118830
118916
  const sourceDir = path.dirname(sourcePath);
118831
118917
  const cnxPath = path.resolve(sourceDir, `${filepath}.cnx`);
@@ -118837,7 +118923,8 @@ var transformQuoteInclude = (includeText, filepath, options) => {
118837
118923
  );
118838
118924
  }
118839
118925
  }
118840
- return includeText.replace(`"${filepath}.cnx"`, `"${filepath}.h"`);
118926
+ const ext = cppMode ? ".hpp" : ".h";
118927
+ return includeText.replace(`"${filepath}.cnx"`, `"${filepath}${ext}"`);
118841
118928
  };
118842
118929
  var transformIncludeDirective = (includeText, options) => {
118843
118930
  const angleMatch = /#\s*include\s*<([^>]+)\.cnx>/.exec(includeText);
@@ -122607,6 +122694,7 @@ var ArgumentGenerator = class _ArgumentGenerator {
122607
122694
  }
122608
122695
  /**
122609
122696
  * Handle rvalue argument (literals or complex expressions).
122697
+ * Issue #872: Sets expectedType for MISRA 7.2 U suffix on unsigned literals.
122610
122698
  */
122611
122699
  static handleRvalueArg(ctx, targetParamBaseType, callbacks) {
122612
122700
  if (!targetParamBaseType) {
@@ -122614,9 +122702,19 @@ var ArgumentGenerator = class _ArgumentGenerator {
122614
122702
  }
122615
122703
  const cType = TYPE_MAP_default[targetParamBaseType];
122616
122704
  if (!cType || cType === "void") {
122617
- return callbacks.generateExpression(ctx);
122705
+ return CodeGenState.withExpectedType(
122706
+ targetParamBaseType,
122707
+ () => callbacks.generateExpression(ctx),
122708
+ true
122709
+ // suppressEnumResolution
122710
+ );
122618
122711
  }
122619
- const value = callbacks.generateExpression(ctx);
122712
+ const value = CodeGenState.withExpectedType(
122713
+ targetParamBaseType,
122714
+ () => callbacks.generateExpression(ctx),
122715
+ true
122716
+ // suppressEnumResolution
122717
+ );
122620
122718
  if (CodeGenState.cppMode) {
122621
122719
  return value;
122622
122720
  }
@@ -123035,6 +123133,14 @@ var AssignmentExpectedTypeResolver = class _AssignmentExpectedTypeResolver {
123035
123133
  identifiers
123036
123134
  );
123037
123135
  }
123136
+ if (identifiers.length === 1 && hasSubscript) {
123137
+ return _AssignmentExpectedTypeResolver.resolveForArrayElement(baseId);
123138
+ }
123139
+ if (identifiers.length >= 2 && hasSubscript) {
123140
+ return _AssignmentExpectedTypeResolver.resolveForMemberArrayElement(
123141
+ identifiers
123142
+ );
123143
+ }
123038
123144
  }
123039
123145
  return { expectedType: null, assignmentContext: null };
123040
123146
  }
@@ -123061,8 +123167,40 @@ var AssignmentExpectedTypeResolver = class _AssignmentExpectedTypeResolver {
123061
123167
  *
123062
123168
  * Issue #452: Enables type-aware resolution of unqualified enum members
123063
123169
  * for nested access (e.g., config.nested.field).
123170
+ *
123171
+ * Delegates to walkMemberChain shared implementation.
123064
123172
  */
123065
123173
  static resolveForMemberChain(identifiers) {
123174
+ return _AssignmentExpectedTypeResolver.walkMemberChain(identifiers);
123175
+ }
123176
+ /**
123177
+ * Resolve expected type for array element access.
123178
+ * Issue #872: arr[i] <- value needs baseType for MISRA 7.2 U suffix.
123179
+ */
123180
+ static resolveForArrayElement(id) {
123181
+ const typeInfo = CodeGenState.getVariableTypeInfo(id);
123182
+ if (!typeInfo?.isArray) {
123183
+ return { expectedType: null, assignmentContext: null };
123184
+ }
123185
+ return { expectedType: typeInfo.baseType, assignmentContext: null };
123186
+ }
123187
+ /**
123188
+ * Resolve expected type for member chain ending with array access.
123189
+ * Issue #872: struct.arr[i] <- value needs element type for MISRA 7.2.
123190
+ *
123191
+ * Delegates to walkMemberChain which handles both member chain and
123192
+ * member-array-element patterns identically (both return final field type).
123193
+ */
123194
+ static resolveForMemberArrayElement(identifiers) {
123195
+ return _AssignmentExpectedTypeResolver.walkMemberChain(identifiers);
123196
+ }
123197
+ /**
123198
+ * Walk a struct member chain to find the final field's type.
123199
+ * Shared implementation for both member chain and member-array-element patterns.
123200
+ *
123201
+ * Issue #831: Uses SymbolTable as single source of truth for struct fields.
123202
+ */
123203
+ static walkMemberChain(identifiers) {
123066
123204
  if (identifiers.length < 2) {
123067
123205
  return { expectedType: null, assignmentContext: null };
123068
123206
  }
@@ -123608,11 +123746,10 @@ var ArrayInitHelper = class _ArrayInitHelper {
123608
123746
  */
123609
123747
  static _generateArrayInitValue(typeCtx, expression, callbacks) {
123610
123748
  const typeName = callbacks.getTypeName(typeCtx);
123611
- const savedExpectedType = CodeGenState.expectedType;
123612
- CodeGenState.expectedType = typeName;
123613
- const initValue = callbacks.generateExpression(expression);
123614
- CodeGenState.expectedType = savedExpectedType;
123615
- return initValue;
123749
+ return CodeGenState.withExpectedType(
123750
+ typeName,
123751
+ () => callbacks.generateExpression(expression)
123752
+ );
123616
123753
  }
123617
123754
  /**
123618
123755
  * Check if the last expression was an array initializer
@@ -124305,15 +124442,14 @@ ${assignments}`;
124305
124442
  return `${decl} = ${callbacks.getZeroInitializer(typeCtx, isArray)}`;
124306
124443
  }
124307
124444
  const typeName = callbacks.getTypeName(typeCtx);
124308
- const savedExpectedType = CodeGenState.expectedType;
124309
- CodeGenState.expectedType = typeName;
124310
124445
  EnumAssignmentValidator_default.validateEnumAssignment(typeName, ctx.expression());
124311
124446
  _VariableDeclHelper.validateIntegerInitializer(ctx, typeName, {
124312
124447
  getExpressionType: callbacks.getExpressionType
124313
124448
  });
124314
- const result = `${decl} = ${callbacks.generateExpression(ctx.expression())}`;
124315
- CodeGenState.expectedType = savedExpectedType;
124316
- return result;
124449
+ return CodeGenState.withExpectedType(
124450
+ typeName,
124451
+ () => `${decl} = ${callbacks.generateExpression(ctx.expression())}`
124452
+ );
124317
124453
  }
124318
124454
  // ========================================================================
124319
124455
  // Tier 4: Orchestrators (main entry points)
@@ -127234,13 +127370,16 @@ var CodeGenerator = class _CodeGenerator {
127234
127370
  /**
127235
127371
  * Issue #477: Generate expression with a specific expected type context.
127236
127372
  * Used by return statements to resolve unqualified enum values.
127373
+ * Note: Uses explicit save/restore (not withExpectedType) to support null values.
127237
127374
  */
127238
127375
  generateExpressionWithExpectedType(ctx, expectedType) {
127239
- const savedExpectedType = CodeGenState.expectedType;
127376
+ const saved = CodeGenState.expectedType;
127240
127377
  CodeGenState.expectedType = expectedType;
127241
- const result = this.generateExpression(ctx);
127242
- CodeGenState.expectedType = savedExpectedType;
127243
- return result;
127378
+ try {
127379
+ return this.generateExpression(ctx);
127380
+ } finally {
127381
+ CodeGenState.expectedType = saved;
127382
+ }
127244
127383
  }
127245
127384
  /**
127246
127385
  * Generate type translation (C-Next type -> C type).
@@ -128585,15 +128724,17 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
128585
128724
  return DEFAULT_TARGET2;
128586
128725
  }
128587
128726
  /**
128588
- * ADR-010: Transform #include directives, converting .cnx to .h
128727
+ * ADR-010: Transform #include directives, converting .cnx to .h or .hpp
128589
128728
  * ADR-053 A5: Delegates to IncludeGenerator
128590
128729
  * Issue #349: Now passes includeDirs and inputs for angle-bracket resolution
128730
+ * Issue #941: Now passes cppMode for .hpp extension in C++ mode
128591
128731
  */
128592
128732
  transformIncludeDirective(includeText) {
128593
128733
  return includeTransformIncludeDirective(includeText, {
128594
128734
  sourcePath: CodeGenState.sourcePath,
128595
128735
  includeDirs: CodeGenState.includeDirs,
128596
- inputs: CodeGenState.inputs
128736
+ inputs: CodeGenState.inputs,
128737
+ cppMode: CodeGenState.cppMode
128597
128738
  });
128598
128739
  }
128599
128740
  // Issue #63: validateIncludeNotImplementationFile moved to TypeValidator
@@ -129331,19 +129472,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129331
129472
  const structFieldTypes = CodeGenState.symbolTable?.getStructFieldTypes(typeName);
129332
129473
  const fields = fieldList.fieldInitializer().map((field) => {
129333
129474
  const fieldName = field.IDENTIFIER().getText();
129334
- const savedExpectedType = CodeGenState.expectedType;
129475
+ let fieldType;
129335
129476
  if (structFieldTypes?.has(fieldName)) {
129336
- let fieldType = structFieldTypes.get(fieldName);
129477
+ fieldType = structFieldTypes.get(fieldName);
129337
129478
  if (fieldType.includes("_")) {
129338
129479
  const parts = fieldType.split("_");
129339
129480
  if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
129340
129481
  fieldType = parts.join("::");
129341
129482
  }
129342
129483
  }
129343
- CodeGenState.expectedType = fieldType;
129344
129484
  }
129345
- const value = this.generateExpression(field.expression());
129346
- CodeGenState.expectedType = savedExpectedType;
129485
+ const value = CodeGenState.withExpectedType(
129486
+ fieldType,
129487
+ () => this.generateExpression(field.expression())
129488
+ );
129347
129489
  return { fieldName, value };
129348
129490
  });
129349
129491
  if (isCppClass) {
@@ -129858,18 +130000,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129858
130000
  // ADR-001: <- becomes = in C, with compound assignment operators
129859
130001
  generateAssignment(ctx) {
129860
130002
  const targetCtx = ctx.assignmentTarget();
129861
- const savedExpectedType = CodeGenState.expectedType;
129862
130003
  const savedAssignmentContext = { ...CodeGenState.assignmentContext };
129863
130004
  const resolved = AssignmentExpectedTypeResolver_default.resolve(targetCtx);
129864
- if (resolved.expectedType) {
129865
- CodeGenState.expectedType = resolved.expectedType;
129866
- }
129867
130005
  if (resolved.assignmentContext) {
129868
130006
  CodeGenState.assignmentContext = resolved.assignmentContext;
129869
130007
  }
129870
- const value = this.generateExpression(ctx.expression());
129871
- CodeGenState.expectedType = savedExpectedType;
129872
- CodeGenState.assignmentContext = savedAssignmentContext;
130008
+ let value;
130009
+ try {
130010
+ value = CodeGenState.withExpectedType(
130011
+ resolved.expectedType,
130012
+ () => this.generateExpression(ctx.expression())
130013
+ );
130014
+ } finally {
130015
+ CodeGenState.assignmentContext = savedAssignmentContext;
130016
+ }
129873
130017
  const operatorCtx = ctx.assignmentOperator();
129874
130018
  const cnextOp = operatorCtx.getText();
129875
130019
  const cOp = ASSIGNMENT_OPERATOR_MAP2[cnextOp] || "=";
@@ -130079,7 +130223,11 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
130079
130223
  * @returns The qualified enum member access, or null if not an enum member
130080
130224
  */
130081
130225
  _resolveUnqualifiedEnumMember(id) {
130082
- if (CodeGenState.expectedType && CodeGenState.symbols.knownEnums.has(CodeGenState.expectedType)) {
130226
+ if (CodeGenState.suppressBareEnumResolution) {
130227
+ } else if (
130228
+ // Type-aware resolution: check only the expected enum type
130229
+ CodeGenState.expectedType && CodeGenState.symbols.knownEnums.has(CodeGenState.expectedType)
130230
+ ) {
130083
130231
  const members = CodeGenState.symbols.enumMembers.get(
130084
130232
  CodeGenState.expectedType
130085
130233
  );
@@ -130877,15 +131025,16 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
130877
131025
  }
130878
131026
  if (options.userIncludes && options.userIncludes.length > 0) {
130879
131027
  for (const include of options.userIncludes) {
130880
- const transformedInclude = options.cppMode ? include.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : include;
130881
- lines.push(transformedInclude);
131028
+ lines.push(include);
131029
+ }
131030
+ }
131031
+ const userIncludeSet = new Set(options.userIncludes ?? []);
131032
+ if (options.cppMode && options.userIncludes) {
131033
+ for (const inc of options.userIncludes) {
131034
+ const hVersion = inc.replace(/\.hpp"/, '.h"').replace(/\.hpp>/, ".h>");
131035
+ userIncludeSet.add(hVersion);
130882
131036
  }
130883
131037
  }
130884
- const userIncludeSet = new Set(
130885
- options.userIncludes?.map(
130886
- (inc) => options.cppMode ? inc.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : inc
130887
- ) ?? []
130888
- );
130889
131038
  for (const directive of headersToInclude) {
130890
131039
  if (!userIncludeSet.has(directive)) {
130891
131040
  lines.push(directive);
@@ -131263,18 +131412,21 @@ var IncludeExtractor = class {
131263
131412
  /**
131264
131413
  * Extract user includes from a parsed C-Next program.
131265
131414
  *
131266
- * Extracts #include directives for .cnx files and transforms them to .h includes.
131415
+ * Extracts #include directives for .cnx files and transforms them to .h or .hpp includes.
131416
+ * Issue #941: Uses .hpp extension when cppMode is true.
131267
131417
  * This enables cross-file type definitions in generated headers.
131268
131418
  *
131269
131419
  * @param tree The parsed C-Next program
131270
- * @returns Array of transformed include strings (e.g., '#include "types.h"')
131420
+ * @param cppMode Whether to use .hpp extension (C++ mode)
131421
+ * @returns Array of transformed include strings (e.g., '#include "types.h"' or '#include "types.hpp"')
131271
131422
  */
131272
- static collectUserIncludes(tree) {
131423
+ static collectUserIncludes(tree, cppMode = false) {
131273
131424
  const userIncludes = [];
131425
+ const ext = cppMode ? ".hpp" : ".h";
131274
131426
  for (const includeDir of tree.includeDirective()) {
131275
131427
  const includeText = includeDir.getText();
131276
131428
  if (includeText.includes(".cnx")) {
131277
- const transformedInclude = includeText.replace(/\.cnx"/, '.h"').replace(/\.cnx>/, ".h>");
131429
+ const transformedInclude = includeText.replace(/\.cnx"/, `${ext}"`).replace(/\.cnx>/, `${ext}>`);
131278
131430
  userIncludes.push(transformedInclude);
131279
131431
  }
131280
131432
  }
@@ -138912,7 +139064,10 @@ var Transpiler = class {
138912
139064
  cppMode: this.cppDetected,
138913
139065
  symbolInfo
138914
139066
  });
138915
- const userIncludes = IncludeExtractor_default.collectUserIncludes(tree);
139067
+ const userIncludes = IncludeExtractor_default.collectUserIncludes(
139068
+ tree,
139069
+ this.cppDetected
139070
+ );
138916
139071
  const passByValue = this.codeGenerator.getPassByValueParams();
138917
139072
  const passByValueCopy = MapUtils_default.deepCopyStringSetMap(passByValue);
138918
139073
  this.state.setSymbolInfo(sourcePath, symbolInfo);