c-next 0.2.8 → 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 (24) hide show
  1. package/dist/index.js +213 -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 +21 -6
  12. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +15 -2
  13. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +34 -11
  14. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +2 -1
  15. package/src/transpiler/output/codegen/generators/support/IncludeGenerator.ts +19 -7
  16. package/src/transpiler/output/codegen/generators/support/__tests__/IncludeGenerator.test.ts +68 -0
  17. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +14 -2
  18. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +3 -5
  19. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +56 -8
  20. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +5 -6
  21. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +60 -2
  22. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +14 -16
  23. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +25 -0
  24. 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.8",
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
  }
@@ -115138,7 +115179,12 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115138
115179
  orchestrator
115139
115180
  );
115140
115181
  }
115141
- let argCode = orchestrator.generateExpression(e);
115182
+ const argCode = CodeGenState.withExpectedType(
115183
+ targetParam?.baseType,
115184
+ () => orchestrator.generateExpression(e),
115185
+ true
115186
+ // suppressEnumResolution
115187
+ );
115142
115188
  if (!targetParam?.baseType?.endsWith("*")) {
115143
115189
  return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
115144
115190
  }
@@ -115154,10 +115200,13 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115154
115200
  isPointerVariable = true;
115155
115201
  }
115156
115202
  const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115157
- if (needsAddressOf) {
115158
- argCode = `&${argCode}`;
115159
- }
115160
- 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
+ );
115161
115210
  };
115162
115211
  var _shouldPassByValue = (funcExpr, idx, targetParam, isCrossFile, orchestrator) => {
115163
115212
  if (!targetParam) return false;
@@ -115204,7 +115253,12 @@ var generateFunctionCall = (funcExpr, argCtx, input, _state, orchestrator) => {
115204
115253
  resolved.isCrossFile,
115205
115254
  orchestrator
115206
115255
  )) {
115207
- const argCode = orchestrator.generateExpression(e);
115256
+ const argCode = CodeGenState.withExpectedType(
115257
+ targetParam?.baseType,
115258
+ () => orchestrator.generateExpression(e),
115259
+ true
115260
+ // suppressEnumResolution
115261
+ );
115208
115262
  return wrapWithCppEnumCast(
115209
115263
  argCode,
115210
115264
  e,
@@ -116556,12 +116610,15 @@ var generateSubscriptAccess = (ctx, input, state, orchestrator, effects) => {
116556
116610
  return output;
116557
116611
  };
116558
116612
  var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116559
- const index = orchestrator.generateExpression(expr);
116613
+ const index = CodeGenState.withExpectedType(
116614
+ "size_t",
116615
+ () => orchestrator.generateExpression(expr)
116616
+ );
116560
116617
  validateNotBitmapMember(ctx, input);
116561
116618
  const isRegisterAccess = checkRegisterAccess(ctx, input);
116562
116619
  const identifierTypeInfo = getIdentifierTypeInfo(ctx, input);
116563
116620
  if (isRegisterAccess) {
116564
- output.result = `((${ctx.result} >> ${index}) & 1)`;
116621
+ output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116565
116622
  return output;
116566
116623
  }
116567
116624
  if (ctx.currentMemberIsArray) {
@@ -116575,7 +116632,7 @@ var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116575
116632
  }
116576
116633
  const isPrimitiveIntMember = ctx.currentStructType && TypeCheckUtils_default.isInteger(ctx.currentStructType);
116577
116634
  if (isPrimitiveIntMember) {
116578
- output.result = `((${ctx.result} >> ${index}) & 1)`;
116635
+ output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116579
116636
  output.currentStructType = void 0;
116580
116637
  return output;
116581
116638
  }
@@ -116633,12 +116690,18 @@ var handleDefaultSubscript = (ctx, index, typeInfo, output) => {
116633
116690
  subscriptCount: 1,
116634
116691
  isRegisterAccess: false
116635
116692
  });
116636
- 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
+ }
116637
116698
  return output;
116638
116699
  };
116639
116700
  var handleBitRangeSubscript = (ctx, exprs, input, state, orchestrator, effects, output) => {
116640
- const start = orchestrator.generateExpression(exprs[0]);
116641
- 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
+ ]);
116642
116705
  const isFloatType = ctx.primaryTypeInfo?.baseType === "f32" || ctx.primaryTypeInfo?.baseType === "f64";
116643
116706
  if (isFloatType && ctx.rootIdentifier) {
116644
116707
  output.result = handleFloatBitRange(
@@ -116655,7 +116718,7 @@ var handleBitRangeSubscript = (ctx, exprs, input, state, orchestrator, effects,
116655
116718
  );
116656
116719
  } else {
116657
116720
  const mask = orchestrator.generateBitMask(width);
116658
- if (start === "0") {
116721
+ if (start === "0" || start === "0U") {
116659
116722
  output.result = `((${ctx.result}) & ${mask})`;
116660
116723
  } else {
116661
116724
  output.result = `((${ctx.result} >> ${start}) & ${mask})`;
@@ -116690,7 +116753,7 @@ var handleFloatBitRange = (ctx, state, orchestrator, effects) => {
116690
116753
  if (!shadowIsCurrent) {
116691
116754
  orchestrator.addPendingTempDeclaration(`${shadowName}.f = ${ctx.result};`);
116692
116755
  }
116693
- if (ctx.start === "0") {
116756
+ if (ctx.start === "0" || ctx.start === "0U") {
116694
116757
  return `(${shadowName}.u & ${mask})`;
116695
116758
  }
116696
116759
  return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
@@ -117612,7 +117675,11 @@ var FunctionGenerator_default = generateFunction;
117612
117675
  // src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts
117613
117676
  function generateInitializer(varDecl, isArray, orchestrator) {
117614
117677
  if (varDecl.expression()) {
117615
- return ` = ${orchestrator.generateExpression(varDecl.expression())}`;
117678
+ const typeName = orchestrator.generateType(varDecl.type());
117679
+ return CodeGenState.withExpectedType(
117680
+ typeName,
117681
+ () => ` = ${orchestrator.generateExpression(varDecl.expression())}`
117682
+ );
117616
117683
  }
117617
117684
  return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
117618
117685
  }
@@ -118804,7 +118871,7 @@ var CnxFileResolver = class {
118804
118871
  var CnxFileResolver_default = CnxFileResolver;
118805
118872
 
118806
118873
  // src/transpiler/output/codegen/generators/support/IncludeGenerator.ts
118807
- var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs) => {
118874
+ var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs, cppMode) => {
118808
118875
  if (inputs.length === 0) {
118809
118876
  return null;
118810
118877
  }
@@ -118818,25 +118885,33 @@ var resolveAngleIncludePath = (filename, sourcePath, includeDirs, inputs) => {
118818
118885
  foundPath,
118819
118886
  inputs
118820
118887
  );
118821
- return relativePath ? relativePath.replace(/\.cnx$/, ".h") : null;
118888
+ const ext = cppMode ? ".hpp" : ".h";
118889
+ return relativePath ? relativePath.replace(/\.cnx$/, ext) : null;
118822
118890
  };
118823
118891
  var transformAngleInclude = (includeText, filename, options) => {
118824
- const { sourcePath, includeDirs = [], inputs = [] } = options;
118892
+ const {
118893
+ sourcePath,
118894
+ includeDirs = [],
118895
+ inputs = [],
118896
+ cppMode = false
118897
+ } = options;
118825
118898
  if (sourcePath) {
118826
118899
  const resolvedPath = resolveAngleIncludePath(
118827
118900
  filename,
118828
118901
  sourcePath,
118829
118902
  includeDirs,
118830
- inputs
118903
+ inputs,
118904
+ cppMode
118831
118905
  );
118832
118906
  if (resolvedPath) {
118833
118907
  return includeText.replace(`<${filename}.cnx>`, `<${resolvedPath}>`);
118834
118908
  }
118835
118909
  }
118836
- return includeText.replace(`<${filename}.cnx>`, `<${filename}.h>`);
118910
+ const ext = cppMode ? ".hpp" : ".h";
118911
+ return includeText.replace(`<${filename}.cnx>`, `<${filename}${ext}>`);
118837
118912
  };
118838
118913
  var transformQuoteInclude = (includeText, filepath, options) => {
118839
- const { sourcePath } = options;
118914
+ const { sourcePath, cppMode = false } = options;
118840
118915
  if (sourcePath) {
118841
118916
  const sourceDir = path.dirname(sourcePath);
118842
118917
  const cnxPath = path.resolve(sourceDir, `${filepath}.cnx`);
@@ -118848,7 +118923,8 @@ var transformQuoteInclude = (includeText, filepath, options) => {
118848
118923
  );
118849
118924
  }
118850
118925
  }
118851
- return includeText.replace(`"${filepath}.cnx"`, `"${filepath}.h"`);
118926
+ const ext = cppMode ? ".hpp" : ".h";
118927
+ return includeText.replace(`"${filepath}.cnx"`, `"${filepath}${ext}"`);
118852
118928
  };
118853
118929
  var transformIncludeDirective = (includeText, options) => {
118854
118930
  const angleMatch = /#\s*include\s*<([^>]+)\.cnx>/.exec(includeText);
@@ -122618,6 +122694,7 @@ var ArgumentGenerator = class _ArgumentGenerator {
122618
122694
  }
122619
122695
  /**
122620
122696
  * Handle rvalue argument (literals or complex expressions).
122697
+ * Issue #872: Sets expectedType for MISRA 7.2 U suffix on unsigned literals.
122621
122698
  */
122622
122699
  static handleRvalueArg(ctx, targetParamBaseType, callbacks) {
122623
122700
  if (!targetParamBaseType) {
@@ -122625,9 +122702,19 @@ var ArgumentGenerator = class _ArgumentGenerator {
122625
122702
  }
122626
122703
  const cType = TYPE_MAP_default[targetParamBaseType];
122627
122704
  if (!cType || cType === "void") {
122628
- return callbacks.generateExpression(ctx);
122705
+ return CodeGenState.withExpectedType(
122706
+ targetParamBaseType,
122707
+ () => callbacks.generateExpression(ctx),
122708
+ true
122709
+ // suppressEnumResolution
122710
+ );
122629
122711
  }
122630
- const value = callbacks.generateExpression(ctx);
122712
+ const value = CodeGenState.withExpectedType(
122713
+ targetParamBaseType,
122714
+ () => callbacks.generateExpression(ctx),
122715
+ true
122716
+ // suppressEnumResolution
122717
+ );
122631
122718
  if (CodeGenState.cppMode) {
122632
122719
  return value;
122633
122720
  }
@@ -123046,6 +123133,14 @@ var AssignmentExpectedTypeResolver = class _AssignmentExpectedTypeResolver {
123046
123133
  identifiers
123047
123134
  );
123048
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
+ }
123049
123144
  }
123050
123145
  return { expectedType: null, assignmentContext: null };
123051
123146
  }
@@ -123072,8 +123167,40 @@ var AssignmentExpectedTypeResolver = class _AssignmentExpectedTypeResolver {
123072
123167
  *
123073
123168
  * Issue #452: Enables type-aware resolution of unqualified enum members
123074
123169
  * for nested access (e.g., config.nested.field).
123170
+ *
123171
+ * Delegates to walkMemberChain shared implementation.
123075
123172
  */
123076
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) {
123077
123204
  if (identifiers.length < 2) {
123078
123205
  return { expectedType: null, assignmentContext: null };
123079
123206
  }
@@ -123619,11 +123746,10 @@ var ArrayInitHelper = class _ArrayInitHelper {
123619
123746
  */
123620
123747
  static _generateArrayInitValue(typeCtx, expression, callbacks) {
123621
123748
  const typeName = callbacks.getTypeName(typeCtx);
123622
- const savedExpectedType = CodeGenState.expectedType;
123623
- CodeGenState.expectedType = typeName;
123624
- const initValue = callbacks.generateExpression(expression);
123625
- CodeGenState.expectedType = savedExpectedType;
123626
- return initValue;
123749
+ return CodeGenState.withExpectedType(
123750
+ typeName,
123751
+ () => callbacks.generateExpression(expression)
123752
+ );
123627
123753
  }
123628
123754
  /**
123629
123755
  * Check if the last expression was an array initializer
@@ -124316,15 +124442,14 @@ ${assignments}`;
124316
124442
  return `${decl} = ${callbacks.getZeroInitializer(typeCtx, isArray)}`;
124317
124443
  }
124318
124444
  const typeName = callbacks.getTypeName(typeCtx);
124319
- const savedExpectedType = CodeGenState.expectedType;
124320
- CodeGenState.expectedType = typeName;
124321
124445
  EnumAssignmentValidator_default.validateEnumAssignment(typeName, ctx.expression());
124322
124446
  _VariableDeclHelper.validateIntegerInitializer(ctx, typeName, {
124323
124447
  getExpressionType: callbacks.getExpressionType
124324
124448
  });
124325
- const result = `${decl} = ${callbacks.generateExpression(ctx.expression())}`;
124326
- CodeGenState.expectedType = savedExpectedType;
124327
- return result;
124449
+ return CodeGenState.withExpectedType(
124450
+ typeName,
124451
+ () => `${decl} = ${callbacks.generateExpression(ctx.expression())}`
124452
+ );
124328
124453
  }
124329
124454
  // ========================================================================
124330
124455
  // Tier 4: Orchestrators (main entry points)
@@ -127245,13 +127370,16 @@ var CodeGenerator = class _CodeGenerator {
127245
127370
  /**
127246
127371
  * Issue #477: Generate expression with a specific expected type context.
127247
127372
  * Used by return statements to resolve unqualified enum values.
127373
+ * Note: Uses explicit save/restore (not withExpectedType) to support null values.
127248
127374
  */
127249
127375
  generateExpressionWithExpectedType(ctx, expectedType) {
127250
- const savedExpectedType = CodeGenState.expectedType;
127376
+ const saved = CodeGenState.expectedType;
127251
127377
  CodeGenState.expectedType = expectedType;
127252
- const result = this.generateExpression(ctx);
127253
- CodeGenState.expectedType = savedExpectedType;
127254
- return result;
127378
+ try {
127379
+ return this.generateExpression(ctx);
127380
+ } finally {
127381
+ CodeGenState.expectedType = saved;
127382
+ }
127255
127383
  }
127256
127384
  /**
127257
127385
  * Generate type translation (C-Next type -> C type).
@@ -128596,15 +128724,17 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
128596
128724
  return DEFAULT_TARGET2;
128597
128725
  }
128598
128726
  /**
128599
- * ADR-010: Transform #include directives, converting .cnx to .h
128727
+ * ADR-010: Transform #include directives, converting .cnx to .h or .hpp
128600
128728
  * ADR-053 A5: Delegates to IncludeGenerator
128601
128729
  * Issue #349: Now passes includeDirs and inputs for angle-bracket resolution
128730
+ * Issue #941: Now passes cppMode for .hpp extension in C++ mode
128602
128731
  */
128603
128732
  transformIncludeDirective(includeText) {
128604
128733
  return includeTransformIncludeDirective(includeText, {
128605
128734
  sourcePath: CodeGenState.sourcePath,
128606
128735
  includeDirs: CodeGenState.includeDirs,
128607
- inputs: CodeGenState.inputs
128736
+ inputs: CodeGenState.inputs,
128737
+ cppMode: CodeGenState.cppMode
128608
128738
  });
128609
128739
  }
128610
128740
  // Issue #63: validateIncludeNotImplementationFile moved to TypeValidator
@@ -129342,19 +129472,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129342
129472
  const structFieldTypes = CodeGenState.symbolTable?.getStructFieldTypes(typeName);
129343
129473
  const fields = fieldList.fieldInitializer().map((field) => {
129344
129474
  const fieldName = field.IDENTIFIER().getText();
129345
- const savedExpectedType = CodeGenState.expectedType;
129475
+ let fieldType;
129346
129476
  if (structFieldTypes?.has(fieldName)) {
129347
- let fieldType = structFieldTypes.get(fieldName);
129477
+ fieldType = structFieldTypes.get(fieldName);
129348
129478
  if (fieldType.includes("_")) {
129349
129479
  const parts = fieldType.split("_");
129350
129480
  if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
129351
129481
  fieldType = parts.join("::");
129352
129482
  }
129353
129483
  }
129354
- CodeGenState.expectedType = fieldType;
129355
129484
  }
129356
- const value = this.generateExpression(field.expression());
129357
- CodeGenState.expectedType = savedExpectedType;
129485
+ const value = CodeGenState.withExpectedType(
129486
+ fieldType,
129487
+ () => this.generateExpression(field.expression())
129488
+ );
129358
129489
  return { fieldName, value };
129359
129490
  });
129360
129491
  if (isCppClass) {
@@ -129869,18 +130000,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129869
130000
  // ADR-001: <- becomes = in C, with compound assignment operators
129870
130001
  generateAssignment(ctx) {
129871
130002
  const targetCtx = ctx.assignmentTarget();
129872
- const savedExpectedType = CodeGenState.expectedType;
129873
130003
  const savedAssignmentContext = { ...CodeGenState.assignmentContext };
129874
130004
  const resolved = AssignmentExpectedTypeResolver_default.resolve(targetCtx);
129875
- if (resolved.expectedType) {
129876
- CodeGenState.expectedType = resolved.expectedType;
129877
- }
129878
130005
  if (resolved.assignmentContext) {
129879
130006
  CodeGenState.assignmentContext = resolved.assignmentContext;
129880
130007
  }
129881
- const value = this.generateExpression(ctx.expression());
129882
- CodeGenState.expectedType = savedExpectedType;
129883
- 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
+ }
129884
130017
  const operatorCtx = ctx.assignmentOperator();
129885
130018
  const cnextOp = operatorCtx.getText();
129886
130019
  const cOp = ASSIGNMENT_OPERATOR_MAP2[cnextOp] || "=";
@@ -130090,7 +130223,11 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
130090
130223
  * @returns The qualified enum member access, or null if not an enum member
130091
130224
  */
130092
130225
  _resolveUnqualifiedEnumMember(id) {
130093
- 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
+ ) {
130094
130231
  const members = CodeGenState.symbols.enumMembers.get(
130095
130232
  CodeGenState.expectedType
130096
130233
  );
@@ -130888,15 +131025,16 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
130888
131025
  }
130889
131026
  if (options.userIncludes && options.userIncludes.length > 0) {
130890
131027
  for (const include of options.userIncludes) {
130891
- const transformedInclude = options.cppMode ? include.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : include;
130892
- 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);
130893
131036
  }
130894
131037
  }
130895
- const userIncludeSet = new Set(
130896
- options.userIncludes?.map(
130897
- (inc) => options.cppMode ? inc.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : inc
130898
- ) ?? []
130899
- );
130900
131038
  for (const directive of headersToInclude) {
130901
131039
  if (!userIncludeSet.has(directive)) {
130902
131040
  lines.push(directive);
@@ -131274,18 +131412,21 @@ var IncludeExtractor = class {
131274
131412
  /**
131275
131413
  * Extract user includes from a parsed C-Next program.
131276
131414
  *
131277
- * 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.
131278
131417
  * This enables cross-file type definitions in generated headers.
131279
131418
  *
131280
131419
  * @param tree The parsed C-Next program
131281
- * @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"')
131282
131422
  */
131283
- static collectUserIncludes(tree) {
131423
+ static collectUserIncludes(tree, cppMode = false) {
131284
131424
  const userIncludes = [];
131425
+ const ext = cppMode ? ".hpp" : ".h";
131285
131426
  for (const includeDir of tree.includeDirective()) {
131286
131427
  const includeText = includeDir.getText();
131287
131428
  if (includeText.includes(".cnx")) {
131288
- const transformedInclude = includeText.replace(/\.cnx"/, '.h"').replace(/\.cnx>/, ".h>");
131429
+ const transformedInclude = includeText.replace(/\.cnx"/, `${ext}"`).replace(/\.cnx>/, `${ext}>`);
131289
131430
  userIncludes.push(transformedInclude);
131290
131431
  }
131291
131432
  }
@@ -138923,7 +139064,10 @@ var Transpiler = class {
138923
139064
  cppMode: this.cppDetected,
138924
139065
  symbolInfo
138925
139066
  });
138926
- const userIncludes = IncludeExtractor_default.collectUserIncludes(tree);
139067
+ const userIncludes = IncludeExtractor_default.collectUserIncludes(
139068
+ tree,
139069
+ this.cppDetected
139070
+ );
138927
139071
  const passByValue = this.codeGenerator.getPassByValueParams();
138928
139072
  const passByValueCopy = MapUtils_default.deepCopyStringSetMap(passByValue);
138929
139073
  this.state.setSymbolInfo(sourcePath, symbolInfo);