c-next 0.2.8 → 0.2.10

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 (71) hide show
  1. package/dist/index.js +1019 -267
  2. package/dist/index.js.map +4 -4
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +101 -8
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +170 -0
  6. package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +246 -0
  7. package/src/transpiler/logic/IncludeExtractor.ts +12 -6
  8. package/src/transpiler/logic/__tests__/IncludeExtractor.test.ts +24 -0
  9. package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +12 -1
  10. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +172 -0
  11. package/src/transpiler/logic/symbols/SymbolTable.ts +84 -0
  12. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +43 -0
  13. package/src/transpiler/logic/symbols/__tests__/TransitiveEnumCollector.test.ts +1 -0
  14. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +98 -1
  15. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +23 -5
  16. package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +69 -2
  17. package/src/transpiler/logic/symbols/c/index.ts +85 -30
  18. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +18 -0
  19. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +90 -0
  20. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +40 -39
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +55 -25
  22. package/src/transpiler/output/codegen/TypeResolver.ts +14 -0
  23. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
  24. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +35 -30
  25. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +7 -7
  26. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +1 -0
  27. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +27 -4
  28. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +6 -0
  29. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +73 -0
  30. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +4 -0
  31. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +92 -0
  32. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +5 -2
  33. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +1 -0
  34. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +14 -0
  35. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +25 -3
  36. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +49 -0
  37. package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +17 -5
  38. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +26 -6
  39. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +31 -2
  40. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +72 -13
  41. package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +25 -1
  42. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +2 -1
  43. package/src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts +2 -17
  44. package/src/transpiler/output/codegen/generators/support/IncludeGenerator.ts +19 -7
  45. package/src/transpiler/output/codegen/generators/support/__tests__/IncludeGenerator.test.ts +68 -0
  46. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +14 -2
  47. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +3 -0
  48. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +3 -5
  49. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +56 -8
  50. package/src/transpiler/output/codegen/helpers/BitRangeHelper.ts +19 -3
  51. package/src/transpiler/output/codegen/helpers/NarrowingCastHelper.ts +191 -0
  52. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +35 -5
  53. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +131 -1
  54. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +61 -2
  55. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +1 -0
  56. package/src/transpiler/output/codegen/helpers/__tests__/BitRangeHelper.test.ts +53 -2
  57. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +1 -0
  58. package/src/transpiler/output/codegen/helpers/__tests__/NarrowingCastHelper.test.ts +159 -0
  59. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +1 -0
  60. package/src/transpiler/output/codegen/types/COMPOUND_TO_BINARY.ts +21 -0
  61. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +2 -0
  62. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +14 -16
  63. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +25 -0
  64. package/src/transpiler/state/CodeGenState.ts +89 -0
  65. package/src/transpiler/state/__tests__/CodeGenState.test.ts +53 -0
  66. package/src/transpiler/state/__tests__/TranspilerState.test.ts +1 -0
  67. package/src/transpiler/types/ICachedFileEntry.ts +2 -0
  68. package/src/transpiler/types/ICodeGenSymbols.ts +8 -0
  69. package/src/utils/BitUtils.ts +33 -4
  70. package/src/utils/cache/CacheManager.ts +9 -1
  71. package/src/utils/cache/__tests__/CacheManager.test.ts +1 -1
@@ -466,6 +466,9 @@ function createMockOrchestrator(
466
466
  generateCallbackTypedef: vi.fn(() => null),
467
467
  isConstValue: vi.fn(() => true),
468
468
  tryEvaluateConstant: vi.fn(() => undefined),
469
+ // Issue #948: Opaque type helpers
470
+ isOpaqueType: vi.fn(() => false),
471
+ markOpaqueScopeVariable: vi.fn(),
469
472
  ...overrides,
470
473
  } as unknown as IOrchestrator;
471
474
  }
@@ -721,6 +724,52 @@ describe("ScopeGenerator", () => {
721
724
  expect(result.code).toContain("static uint32_t Device_status = 0;");
722
725
  expect(orchestrator.getZeroInitializer).toHaveBeenCalled();
723
726
  });
727
+
728
+ it("generates opaque type variable as pointer with NULL (Issue #948)", () => {
729
+ const varDecl = createMockVariableDecl({
730
+ name: "widget",
731
+ type: "widget_t",
732
+ });
733
+ const member = createMockScopeMember({ variableDecl: varDecl });
734
+ const ctx = createMockScopeContext("GUI", [member]);
735
+ const input = createMockInput();
736
+ const state = createMockState();
737
+ const orchestrator = createMockOrchestrator({
738
+ ...createMockOrchestrator(),
739
+ isOpaqueType: vi.fn(() => true),
740
+ markOpaqueScopeVariable: vi.fn(),
741
+ });
742
+
743
+ const result = generateScope(ctx, input, state, orchestrator);
744
+
745
+ // Should generate as pointer with NULL initialization
746
+ expect(result.code).toContain("static widget_t* GUI_widget = NULL;");
747
+ // Should call markOpaqueScopeVariable
748
+ expect(orchestrator.markOpaqueScopeVariable).toHaveBeenCalledWith(
749
+ "GUI_widget",
750
+ );
751
+ });
752
+
753
+ it("does not generate pointer for non-opaque struct types", () => {
754
+ const varDecl = createMockVariableDecl({
755
+ name: "point",
756
+ type: "Point",
757
+ });
758
+ const member = createMockScopeMember({ variableDecl: varDecl });
759
+ const ctx = createMockScopeContext("Canvas", [member]);
760
+ const input = createMockInput();
761
+ const state = createMockState();
762
+ const orchestrator = createMockOrchestrator({
763
+ ...createMockOrchestrator(),
764
+ isOpaqueType: vi.fn(() => false),
765
+ });
766
+
767
+ const result = generateScope(ctx, input, state, orchestrator);
768
+
769
+ // Should generate as value with {0} initialization
770
+ expect(result.code).toContain("static Point Canvas_point = 0;");
771
+ expect(result.code).not.toContain("Point*");
772
+ });
724
773
  });
725
774
 
726
775
  // ========================================================================
@@ -13,6 +13,8 @@
13
13
  */
14
14
  import IGeneratorOutput from "../IGeneratorOutput";
15
15
  import TTypeInfo from "../../types/TTypeInfo";
16
+ import CodeGenState from "../../../../state/CodeGenState.js";
17
+ import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
16
18
 
17
19
  /**
18
20
  * Generate code for .capacity property access.
@@ -56,22 +58,32 @@ interface BitmapFieldInfo {
56
58
  *
57
59
  * Single bit fields generate: ((value >> offset) & 1)
58
60
  * Multi-bit fields generate: ((value >> offset) & mask)
61
+ *
62
+ * MISRA C:2012 Rule 10.3: When target type is known (via CodeGenState.expectedType),
63
+ * wraps expression with appropriate cast. Bool targets use != 0U comparison.
59
64
  */
60
65
  const generateBitmapFieldAccess = (
61
66
  result: string,
62
67
  fieldInfo: BitmapFieldInfo,
63
68
  ): IGeneratorOutput => {
69
+ let expr: string;
64
70
  if (fieldInfo.width === 1) {
65
71
  // Single bit: ((value >> offset) & 1)
66
- return { code: `((${result} >> ${fieldInfo.offset}) & 1)`, effects: [] };
72
+ expr = `((${result} >> ${fieldInfo.offset}) & 1)`;
67
73
  } else {
68
74
  // Multi-bit: ((value >> offset) & mask)
69
75
  const mask = (1 << fieldInfo.width) - 1;
70
- return {
71
- code: `((${result} >> ${fieldInfo.offset}) & 0x${mask.toString(16).toUpperCase()})`,
72
- effects: [],
73
- };
76
+ expr = `((${result} >> ${fieldInfo.offset}) & 0x${mask.toString(16).toUpperCase()})`;
77
+ }
78
+
79
+ // MISRA 10.3: Add narrowing cast if target type is known
80
+ const targetType = CodeGenState.expectedType;
81
+ if (targetType) {
82
+ // Bitmap operations on small types produce int in C
83
+ expr = NarrowingCastHelper.wrap(expr, "int", targetType);
74
84
  }
85
+
86
+ return { code: expr, effects: [] };
75
87
  };
76
88
 
77
89
  // Export all generators
@@ -107,6 +107,7 @@ const _parameterExpectsAddressOf = (
107
107
  /**
108
108
  * Generate argument code for a C/C++ function call.
109
109
  * Handles automatic address-of (&) for struct arguments passed to pointer params.
110
+ * Issue #872: Sets expectedType for MISRA 7.2 U suffix on unsigned literals.
110
111
  */
111
112
  const _generateCFunctionArg = (
112
113
  e: ExpressionContext,
@@ -134,7 +135,13 @@ const _generateCFunctionArg = (
134
135
  );
135
136
  }
136
137
 
137
- let argCode = orchestrator.generateExpression(e);
138
+ // Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
139
+ // (bare enums in function args was never allowed - changing that requires ADR approval)
140
+ const argCode = CodeGenState.withExpectedType(
141
+ targetParam?.baseType,
142
+ () => orchestrator.generateExpression(e),
143
+ true, // suppressEnumResolution
144
+ );
138
145
 
139
146
  // Issue #322: Check if parameter expects a pointer and argument is a struct
140
147
  if (!targetParam?.baseType?.endsWith("*")) {
@@ -158,24 +165,32 @@ const _generateCFunctionArg = (
158
165
  isPointerVariable = true;
159
166
  }
160
167
 
168
+ // Issue #948: Check if argument is an opaque scope variable (already a pointer)
169
+ const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariable(argCode);
170
+
161
171
  // Add & if argument needs address-of to match parameter type.
162
172
  // Issue #322: struct types passed to pointer params.
163
173
  // Issue #832: typedef'd pointer types (e.g., handle_t passed to handle_t*).
164
174
  // Issue #895 Bug B: Skip address-of for variables that are already pointers
175
+ // Issue #948: Skip address-of for opaque scope variables (already pointers)
165
176
  const needsAddressOf =
166
177
  argType &&
167
178
  !argType.endsWith("*") &&
168
179
  !argCode.startsWith("&") &&
169
180
  !targetParam.isArray &&
170
181
  !isPointerVariable &&
182
+ !isOpaqueScopeVar &&
171
183
  (orchestrator.isStructType(argType) ||
172
184
  _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
173
185
 
174
- if (needsAddressOf) {
175
- argCode = `&${argCode}`;
176
- }
186
+ const finalArgCode = needsAddressOf ? `&${argCode}` : argCode;
177
187
 
178
- return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
188
+ return wrapWithCppEnumCast(
189
+ finalArgCode,
190
+ e,
191
+ targetParam?.baseType,
192
+ orchestrator,
193
+ );
179
194
  };
180
195
 
181
196
  /**
@@ -295,7 +310,12 @@ const generateFunctionCall = (
295
310
  orchestrator,
296
311
  )
297
312
  ) {
298
- const argCode = orchestrator.generateExpression(e);
313
+ // Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
314
+ const argCode = CodeGenState.withExpectedType(
315
+ targetParam?.baseType,
316
+ () => orchestrator.generateExpression(e),
317
+ true, // suppressEnumResolution
318
+ );
299
319
  return wrapWithCppEnumCast(
300
320
  argCode,
301
321
  e,
@@ -14,6 +14,8 @@ import TGeneratorEffect from "../TGeneratorEffect";
14
14
  import IGeneratorInput from "../IGeneratorInput";
15
15
  import IGeneratorState from "../IGeneratorState";
16
16
  import IOrchestrator from "../IOrchestrator";
17
+ import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
18
+ import CodeGenState from "../../../../state/CodeGenState";
17
19
 
18
20
  /**
19
21
  * Unsigned type patterns for MISRA Rule 7.2 compliance.
@@ -27,8 +29,18 @@ const UNSIGNED_TYPES = new Set([
27
29
  "uint8_t",
28
30
  "uint16_t",
29
31
  "uint32_t",
32
+ "size_t", // Array indices (MISRA 7.2)
30
33
  ]);
31
34
 
35
+ /**
36
+ * Resolve typedef aliases to their underlying type.
37
+ * For C typedef'd types (e.g., "byte_t" -> "uint8_t"), look up the symbol table.
38
+ */
39
+ function resolveTypedef(typeName: string): string {
40
+ const underlyingType = CodeGenState.getTypedefType(typeName);
41
+ return underlyingType ?? typeName;
42
+ }
43
+
32
44
  /**
33
45
  * Check if a literal is a numeric integer (decimal, hex, octal, or binary).
34
46
  * Excludes strings, floats, and booleans.
@@ -83,6 +95,21 @@ const generateLiteral = (
83
95
  return { code: literalText, effects };
84
96
  }
85
97
 
98
+ // MISRA 10.3: Character literals have type int in C
99
+ // When assigning to narrower types (u8, i8, u16, i16), add explicit cast
100
+ if (literalText.startsWith("'")) {
101
+ const expectedType = state?.expectedType;
102
+ if (expectedType) {
103
+ const wrappedCode = NarrowingCastHelper.wrap(
104
+ literalText,
105
+ "int",
106
+ expectedType,
107
+ );
108
+ return { code: wrappedCode, effects };
109
+ }
110
+ return { code: literalText, effects };
111
+ }
112
+
86
113
  // ADR-024: Transform C-Next float suffixes to standard C syntax
87
114
  // 3.14f32 -> 3.14f (C float)
88
115
  // 3.14f64 -> 3.14 (C double, no suffix needed)
@@ -120,9 +147,11 @@ const generateLiteral = (
120
147
  isNumericIntegerLiteral(literalText) &&
121
148
  !hasUnsignedSuffix(literalText)
122
149
  ) {
123
- if (UNSIGNED_64_TYPES.has(expectedType)) {
150
+ // Resolve typedef aliases (e.g., "byte_t" -> "uint8_t")
151
+ const resolvedType = resolveTypedef(expectedType);
152
+ if (UNSIGNED_64_TYPES.has(resolvedType)) {
124
153
  literalText = literalText + "ULL";
125
- } else if (UNSIGNED_TYPES.has(expectedType)) {
154
+ } else if (UNSIGNED_TYPES.has(resolvedType)) {
126
155
  literalText = literalText + "U";
127
156
  }
128
157
  }
@@ -22,6 +22,7 @@ import generateFunctionCall from "./CallExprGenerator";
22
22
  import memberAccessChain from "../../memberAccessChain";
23
23
  import MemberAccessValidator from "../../helpers/MemberAccessValidator";
24
24
  import BitmapAccessHelper from "./BitmapAccessHelper";
25
+ import NarrowingCastHelper from "../../helpers/NarrowingCastHelper";
25
26
  import TypeCheckUtils from "../../../../../utils/TypeCheckUtils";
26
27
  import SubscriptClassifier from "../../subscript/SubscriptClassifier";
27
28
  import TYPE_WIDTH from "../../types/TYPE_WIDTH";
@@ -1593,7 +1594,11 @@ const handleSingleSubscript = (
1593
1594
  orchestrator: IOrchestrator,
1594
1595
  output: SubscriptAccessResult,
1595
1596
  ): SubscriptAccessResult => {
1596
- const index = orchestrator.generateExpression(expr);
1597
+ // Set expectedType to size_t (unsigned) for array indices per MISRA 7.2
1598
+ // This ensures index literals get U suffix regardless of element type
1599
+ const index = CodeGenState.withExpectedType("size_t", () =>
1600
+ orchestrator.generateExpression(expr),
1601
+ );
1597
1602
 
1598
1603
  // Check if result is a register member with bitmap type (throws)
1599
1604
  validateNotBitmapMember(ctx, input);
@@ -1603,7 +1608,17 @@ const handleSingleSubscript = (
1603
1608
 
1604
1609
  // Register access: bit extraction
1605
1610
  if (isRegisterAccess) {
1606
- output.result = `((${ctx.result} >> ${index}) & 1)`;
1611
+ // Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
1612
+ let expr =
1613
+ index === "0" || index === "0U"
1614
+ ? `((${ctx.result}) & 1)`
1615
+ : `((${ctx.result} >> ${index}) & 1)`;
1616
+ // MISRA 10.3: Add narrowing cast if expected type is narrower than int
1617
+ const targetType = CodeGenState.expectedType;
1618
+ if (targetType) {
1619
+ expr = NarrowingCastHelper.wrap(expr, "int", targetType);
1620
+ }
1621
+ output.result = expr;
1607
1622
  return output;
1608
1623
  }
1609
1624
 
@@ -1624,7 +1639,17 @@ const handleSingleSubscript = (
1624
1639
  const isPrimitiveIntMember =
1625
1640
  ctx.currentStructType && TypeCheckUtils.isInteger(ctx.currentStructType);
1626
1641
  if (isPrimitiveIntMember) {
1627
- output.result = `((${ctx.result} >> ${index}) & 1)`;
1642
+ // Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
1643
+ let expr =
1644
+ index === "0" || index === "0U"
1645
+ ? `((${ctx.result}) & 1)`
1646
+ : `((${ctx.result} >> ${index}) & 1)`;
1647
+ // MISRA 10.3: Add narrowing cast if expected type is narrower than int
1648
+ const targetType = CodeGenState.expectedType;
1649
+ if (targetType) {
1650
+ expr = NarrowingCastHelper.wrap(expr, "int", targetType);
1651
+ }
1652
+ output.result = expr;
1628
1653
  output.currentStructType = undefined;
1629
1654
  return output;
1630
1655
  }
@@ -1742,10 +1767,21 @@ const handleDefaultSubscript = (
1742
1767
  isRegisterAccess: false,
1743
1768
  });
1744
1769
 
1745
- output.result =
1746
- subscriptKind === "bit_single"
1747
- ? `((${ctx.result} >> ${index}) & 1)`
1748
- : `${ctx.result}[${index}]`;
1770
+ if (subscriptKind === "bit_single") {
1771
+ // Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
1772
+ let expr =
1773
+ index === "0" || index === "0U"
1774
+ ? `((${ctx.result}) & 1)`
1775
+ : `((${ctx.result} >> ${index}) & 1)`;
1776
+ // MISRA 10.3: Add narrowing cast if expected type is narrower than int
1777
+ const targetType = CodeGenState.expectedType;
1778
+ if (targetType) {
1779
+ expr = NarrowingCastHelper.wrap(expr, "int", targetType);
1780
+ }
1781
+ output.result = expr;
1782
+ } else {
1783
+ output.result = `${ctx.result}[${index}]`;
1784
+ }
1749
1785
 
1750
1786
  return output;
1751
1787
  };
@@ -1762,8 +1798,12 @@ const handleBitRangeSubscript = (
1762
1798
  effects: TGeneratorEffect[],
1763
1799
  output: SubscriptAccessResult,
1764
1800
  ): SubscriptAccessResult => {
1765
- const start = orchestrator.generateExpression(exprs[0]);
1766
- const width = orchestrator.generateExpression(exprs[1]);
1801
+ // Set expectedType to size_t (unsigned) for bit indices per MISRA 7.2
1802
+ // Bit positions are inherently unsigned values
1803
+ const [start, width] = CodeGenState.withExpectedType("size_t", () => [
1804
+ orchestrator.generateExpression(exprs[0]),
1805
+ orchestrator.generateExpression(exprs[1]),
1806
+ ]);
1767
1807
 
1768
1808
  const isFloatType =
1769
1809
  ctx.primaryTypeInfo?.baseType === "f32" ||
@@ -1784,10 +1824,28 @@ const handleBitRangeSubscript = (
1784
1824
  );
1785
1825
  } else {
1786
1826
  const mask = orchestrator.generateBitMask(width);
1787
- if (start === "0") {
1788
- output.result = `((${ctx.result}) & ${mask})`;
1827
+ // Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
1828
+ let expr: string;
1829
+ if (start === "0" || start === "0U") {
1830
+ expr = `((${ctx.result}) & ${mask})`;
1831
+ } else {
1832
+ expr = `((${ctx.result} >> ${start}) & ${mask})`;
1833
+ }
1834
+
1835
+ // MISRA 10.3: Add narrowing cast if expected type is known
1836
+ // Bit operations promote to int, so wrap with cast when assigning to narrower types
1837
+ const targetType = CodeGenState.expectedType;
1838
+ if (targetType && ctx.primaryTypeInfo?.baseType) {
1839
+ const promotedSourceType = NarrowingCastHelper.getPromotedType(
1840
+ ctx.primaryTypeInfo.baseType,
1841
+ );
1842
+ output.result = NarrowingCastHelper.wrap(
1843
+ expr,
1844
+ promotedSourceType,
1845
+ targetType,
1846
+ );
1789
1847
  } else {
1790
- output.result = `((${ctx.result} >> ${start}) & ${mask})`;
1848
+ output.result = expr;
1791
1849
  }
1792
1850
  }
1793
1851
 
@@ -1854,7 +1912,8 @@ const handleFloatBitRange = (
1854
1912
  }
1855
1913
 
1856
1914
  // Return just the bit read expression using union member .u
1857
- if (ctx.start === "0") {
1915
+ // Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
1916
+ if (ctx.start === "0" || ctx.start === "0U") {
1858
1917
  return `(${shadowName}.u & ${mask})`;
1859
1918
  }
1860
1919
  return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { UnaryExpressionContext } from "../../../../logic/parser/grammar/CNextParser";
10
10
  import IGeneratorOutput from "../IGeneratorOutput";
11
+ import TGeneratorEffect from "../TGeneratorEffect";
11
12
  import IGeneratorInput from "../IGeneratorInput";
12
13
  import IGeneratorState from "../IGeneratorState";
13
14
  import IOrchestrator from "../IOrchestrator";
@@ -15,6 +16,14 @@ import TypeResolver from "../../TypeResolver";
15
16
  import TYPE_MAP from "../../types/TYPE_MAP";
16
17
  import CppModeHelper from "../../helpers/CppModeHelper";
17
18
 
19
+ /**
20
+ * Problematic negative literals that overflow their signed types in C.
21
+ * -2147483648 is parsed as -(2147483648) where 2147483648 > INT32_MAX.
22
+ * These need to be rewritten to avoid the overflow issue.
23
+ */
24
+ const INT32_MIN_LITERAL = "2147483648";
25
+ const INT64_MIN_LITERAL = "9223372036854775808";
26
+
18
27
  /**
19
28
  * Generate C code for a unary expression.
20
29
  *
@@ -45,7 +54,22 @@ const generateUnaryExpr = (
45
54
 
46
55
  // Determine the operator and generate output
47
56
  if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
48
- if (text.startsWith("-")) return { code: `-${inner}`, effects: [] };
57
+ if (text.startsWith("-")) {
58
+ // MISRA 10.3: Handle problematic negative literals that overflow in C
59
+ // -2147483648 is parsed as -(2147483648) where 2147483648 > INT32_MAX
60
+ // Must use INT32_MIN or INT64_MIN to avoid the overflow
61
+ // Cast is needed because INT32_MIN has type 'int', not 'int32_t'
62
+ const effects: TGeneratorEffect[] = [];
63
+ if (inner === INT32_MIN_LITERAL) {
64
+ effects.push({ type: "include", header: "limits" });
65
+ return { code: "(int32_t)INT32_MIN", effects };
66
+ }
67
+ if (inner === INT64_MIN_LITERAL || inner === INT64_MIN_LITERAL + "LL") {
68
+ effects.push({ type: "include", header: "limits" });
69
+ return { code: "(int64_t)INT64_MIN", effects };
70
+ }
71
+ return { code: `-${inner}`, effects };
72
+ }
49
73
  if (text.startsWith("~")) {
50
74
  const innerType = TypeResolver.getUnaryExpressionType(
51
75
  node.unaryExpression()!,
@@ -1179,7 +1179,8 @@ describe("PostfixExpressionGenerator", () => {
1179
1179
  });
1180
1180
 
1181
1181
  const result = generatePostfixExpression(ctx, input, state, orchestrator);
1182
- expect(result.code).toBe("((GPIO >> 0) & 1)");
1182
+ // Optimization: no shift when index is 0
1183
+ expect(result.code).toBe("((GPIO) & 1)");
1183
1184
  });
1184
1185
 
1185
1186
  it("throws for bracket indexing on bitmap type", () => {
@@ -13,6 +13,7 @@ import IGeneratorOutput from "../IGeneratorOutput";
13
13
  import TGeneratorEffect from "../TGeneratorEffect";
14
14
  import ITargetCapabilities from "../../types/ITargetCapabilities";
15
15
  import TYPE_WIDTH from "../../types/TYPE_WIDTH";
16
+ import COMPOUND_TO_BINARY from "../../types/COMPOUND_TO_BINARY";
16
17
 
17
18
  /**
18
19
  * Maps C-Next types to C types (for atomic operations)
@@ -54,22 +55,6 @@ const STREX_MAP: Record<string, string> = {
54
55
  i32: "__STREXW",
55
56
  };
56
57
 
57
- /**
58
- * Map compound operators to simple operators
59
- */
60
- const SIMPLE_OP_MAP: Record<string, string> = {
61
- "+=": "+",
62
- "-=": "-",
63
- "*=": "*",
64
- "/=": "/",
65
- "%=": "%",
66
- "&=": "&",
67
- "|=": "|",
68
- "^=": "^",
69
- "<<=": "<<",
70
- ">>=": ">>",
71
- };
72
-
73
58
  /**
74
59
  * Map compound operators to clamp helper operation names
75
60
  */
@@ -106,7 +91,7 @@ function generateInnerAtomicOp(
106
91
  typeInfo: TTypeInfo,
107
92
  ): IGeneratorOutput {
108
93
  const effects: TGeneratorEffect[] = [];
109
- const simpleOp = SIMPLE_OP_MAP[cOp] || "+";
94
+ const simpleOp = COMPOUND_TO_BINARY[cOp] || "+";
110
95
 
111
96
  // Handle clamp behavior for arithmetic operations (integers only)
112
97
  const helperOp = getClampHelperOp(cOp, typeInfo);
@@ -13,6 +13,7 @@ interface IIncludeTransformOptions {
13
13
  sourcePath: string | null;
14
14
  includeDirs?: string[];
15
15
  inputs?: string[];
16
+ cppMode?: boolean;
16
17
  }
17
18
 
18
19
  /**
@@ -24,6 +25,7 @@ const resolveAngleIncludePath = (
24
25
  sourcePath: string,
25
26
  includeDirs: string[],
26
27
  inputs: string[],
28
+ cppMode: boolean,
27
29
  ): string | null => {
28
30
  if (inputs.length === 0) {
29
31
  return null;
@@ -42,7 +44,8 @@ const resolveAngleIncludePath = (
42
44
  inputs,
43
45
  );
44
46
 
45
- return relativePath ? relativePath.replace(/\.cnx$/, ".h") : null;
47
+ const ext = cppMode ? ".hpp" : ".h";
48
+ return relativePath ? relativePath.replace(/\.cnx$/, ext) : null;
46
49
  };
47
50
 
48
51
  /**
@@ -54,7 +57,12 @@ const transformAngleInclude = (
54
57
  filename: string,
55
58
  options: IIncludeTransformOptions,
56
59
  ): string => {
57
- const { sourcePath, includeDirs = [], inputs = [] } = options;
60
+ const {
61
+ sourcePath,
62
+ includeDirs = [],
63
+ inputs = [],
64
+ cppMode = false,
65
+ } = options;
58
66
 
59
67
  // Try to resolve the correct output path
60
68
  if (sourcePath) {
@@ -63,6 +71,7 @@ const transformAngleInclude = (
63
71
  sourcePath,
64
72
  includeDirs,
65
73
  inputs,
74
+ cppMode,
66
75
  );
67
76
  if (resolvedPath) {
68
77
  return includeText.replace(`<${filename}.cnx>`, `<${resolvedPath}>`);
@@ -70,7 +79,8 @@ const transformAngleInclude = (
70
79
  }
71
80
 
72
81
  // Fallback: simple replacement
73
- return includeText.replace(`<${filename}.cnx>`, `<${filename}.h>`);
82
+ const ext = cppMode ? ".hpp" : ".h";
83
+ return includeText.replace(`<${filename}.cnx>`, `<${filename}${ext}>`);
74
84
  };
75
85
 
76
86
  /**
@@ -82,7 +92,7 @@ const transformQuoteInclude = (
82
92
  filepath: string,
83
93
  options: IIncludeTransformOptions,
84
94
  ): string => {
85
- const { sourcePath } = options;
95
+ const { sourcePath, cppMode = false } = options;
86
96
 
87
97
  // Validate .cnx file exists if we have source path
88
98
  if (sourcePath) {
@@ -98,12 +108,14 @@ const transformQuoteInclude = (
98
108
  }
99
109
  }
100
110
 
101
- // Transform to .h
102
- return includeText.replace(`"${filepath}.cnx"`, `"${filepath}.h"`);
111
+ // Transform to .h or .hpp
112
+ const ext = cppMode ? ".hpp" : ".h";
113
+ return includeText.replace(`"${filepath}.cnx"`, `"${filepath}${ext}"`);
103
114
  };
104
115
 
105
116
  /**
106
- * ADR-010: Transform #include directives, converting .cnx to .h
117
+ * ADR-010: Transform #include directives, converting .cnx to .h or .hpp
118
+ * Issue #941: Uses .hpp extension when cppMode is true
107
119
  * Validates that .cnx files exist if sourcePath is available
108
120
  * Supports both <file.cnx> and "file.cnx" forms
109
121
  *
@@ -156,6 +156,44 @@ describe("IncludeGenerator", () => {
156
156
 
157
157
  expect(result).toBe("#include <lib.h>");
158
158
  });
159
+
160
+ it("transforms angle bracket .cnx include to .hpp in C++ mode", () => {
161
+ const result = transformIncludeDirective("#include <utils.cnx>", {
162
+ sourcePath: null,
163
+ cppMode: true,
164
+ });
165
+ expect(result).toBe("#include <utils.hpp>");
166
+ });
167
+
168
+ it("resolves path from inputs with .hpp in C++ mode", () => {
169
+ vi.mocked(CnxFileResolver.findCnxFile).mockReturnValue(
170
+ "/project/src/Display/utils.cnx",
171
+ );
172
+ vi.mocked(CnxFileResolver.getRelativePathFromInputs).mockReturnValue(
173
+ "Display/utils.cnx",
174
+ );
175
+
176
+ const result = transformIncludeDirective("#include <utils.cnx>", {
177
+ sourcePath: "/project/src/main.cnx",
178
+ includeDirs: ["/project/src/Display"],
179
+ inputs: ["/project/src"],
180
+ cppMode: true,
181
+ });
182
+
183
+ expect(result).toBe("#include <Display/utils.hpp>");
184
+ });
185
+
186
+ it("falls back to .hpp in C++ mode when file not found", () => {
187
+ vi.mocked(CnxFileResolver.findCnxFile).mockReturnValue(null);
188
+
189
+ const result = transformIncludeDirective("#include <missing.cnx>", {
190
+ sourcePath: "/project/src/main.cnx",
191
+ inputs: ["/project/src"],
192
+ cppMode: true,
193
+ });
194
+
195
+ expect(result).toBe("#include <missing.hpp>");
196
+ });
159
197
  });
160
198
 
161
199
  // ==========================================================================
@@ -221,6 +259,28 @@ describe("IncludeGenerator", () => {
221
259
  }),
222
260
  ).toThrow(/Referenced in:.*main\.cnx/);
223
261
  });
262
+
263
+ it("transforms quoted .cnx include to .hpp in C++ mode", () => {
264
+ vi.mocked(CnxFileResolver.cnxFileExists).mockReturnValue(true);
265
+
266
+ const result = transformIncludeDirective('#include "helper.cnx"', {
267
+ sourcePath: "/project/src/main.cnx",
268
+ cppMode: true,
269
+ });
270
+
271
+ expect(result).toBe('#include "helper.hpp"');
272
+ });
273
+
274
+ it("transforms quoted include with relative path to .hpp in C++ mode", () => {
275
+ vi.mocked(CnxFileResolver.cnxFileExists).mockReturnValue(true);
276
+
277
+ const result = transformIncludeDirective('#include "../lib/utils.cnx"', {
278
+ sourcePath: "/project/src/main.cnx",
279
+ cppMode: true,
280
+ });
281
+
282
+ expect(result).toBe('#include "../lib/utils.hpp"');
283
+ });
224
284
  });
225
285
 
226
286
  // ==========================================================================
@@ -262,6 +322,14 @@ describe("IncludeGenerator", () => {
262
322
  });
263
323
  expect(result).toBe("int x = 5;");
264
324
  });
325
+
326
+ it("passes through .h includes unchanged even in C++ mode", () => {
327
+ const result = transformIncludeDirective('#include "myheader.h"', {
328
+ sourcePath: "/project/main.cnx",
329
+ cppMode: true,
330
+ });
331
+ expect(result).toBe('#include "myheader.h"');
332
+ });
265
333
  });
266
334
 
267
335
  // ==========================================================================