c-next 0.2.16 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +8897 -6260
  3. package/dist/index.js.map +4 -4
  4. package/grammar/CNext.g4 +12 -0
  5. package/package.json +4 -2
  6. package/src/transpiler/Transpiler.ts +376 -48
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
  9. package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
  10. package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
  11. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
  12. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  13. package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
  14. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
  15. package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
  16. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  17. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  18. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  19. package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
  20. package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
  21. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  22. package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
  23. package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
  24. package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
  25. package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
  26. package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
  27. package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
  28. package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
  29. package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
  30. package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
  31. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  32. package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
  33. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  34. package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
  35. package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
  36. package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
  37. package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
  38. package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
  39. package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
  40. package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
  41. package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
  42. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
  43. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
  44. package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
  45. package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
  46. package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
  47. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
  48. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  49. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  50. package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
  51. package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
  52. package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
  53. package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
  54. package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
  55. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  56. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
  57. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  58. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
  59. package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
  60. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
  61. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
  62. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
  63. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
  64. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
  65. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
  66. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
  67. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
  68. package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
  69. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
  70. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
  71. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
  72. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
  73. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
  74. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  75. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  76. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  77. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
  78. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  79. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
  80. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  81. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  82. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
  83. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
  84. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
  85. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
  86. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  87. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  88. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
  89. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  90. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  91. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  92. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  93. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
  94. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  95. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  96. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
  97. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  98. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  99. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  100. package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
  101. package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
  102. package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
  103. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  104. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  105. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  106. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
  107. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  108. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  109. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  110. package/src/transpiler/state/CodeGenState.ts +91 -22
  111. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  112. package/src/transpiler/types/ICachedFileEntry.ts +7 -0
  113. package/src/utils/LiteralUtils.ts +23 -0
  114. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  115. package/src/utils/cache/CacheManager.ts +13 -2
  116. package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
  117. package/src/utils/constants/TypeConstants.ts +13 -0
  118. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -126,6 +126,8 @@ function createMockVariableDecl(options: {
126
126
  name: string;
127
127
  type: string;
128
128
  isConst?: boolean;
129
+ isAtomic?: boolean;
130
+ isVolatile?: boolean;
129
131
  initialValue?: string;
130
132
  arrayDims?: string[];
131
133
  hasStringType?: boolean;
@@ -142,6 +144,10 @@ function createMockVariableDecl(options: {
142
144
  options.arrayTypeSize,
143
145
  ),
144
146
  constModifier: () => createMockConstModifier(options.isConst ?? false),
147
+ // Issue #998: atomic and volatile modifiers for scope variables
148
+ // Uses VariableModifierBuilder for consistent handling
149
+ atomicModifier: () => (options.isAtomic ? ({} as unknown) : null),
150
+ volatileModifier: () => (options.isVolatile ? ({} as unknown) : null),
145
151
  expression: () =>
146
152
  options.initialValue ? createMockExpression(options.initialValue) : null,
147
153
  arrayDimension: () =>
@@ -772,6 +778,86 @@ describe("ScopeGenerator", () => {
772
778
  expect(result.code).toContain("static Point Canvas_point = 0;");
773
779
  expect(result.code).not.toContain("Point*");
774
780
  });
781
+
782
+ it("generates atomic variable with volatile modifier (Issue #998)", () => {
783
+ const varDecl = createMockVariableDecl({
784
+ name: "counter",
785
+ type: "u32",
786
+ isAtomic: true,
787
+ initialValue: "0",
788
+ });
789
+ const member = createMockScopeMember({ variableDecl: varDecl });
790
+ const ctx = createMockScopeContext("ISR", [member]);
791
+ const input = createMockInput();
792
+ const state = createMockState();
793
+ const orchestrator = createMockOrchestrator();
794
+
795
+ const result = generateScope(ctx, input, state, orchestrator);
796
+
797
+ expect(result.code).toContain(
798
+ "static volatile uint32_t ISR_counter = 0;",
799
+ );
800
+ });
801
+
802
+ it("generates public atomic variable with volatile but without static (Issue #998)", () => {
803
+ const varDecl = createMockVariableDecl({
804
+ name: "flag",
805
+ type: "bool",
806
+ isAtomic: true,
807
+ initialValue: "false",
808
+ });
809
+ const member = createMockScopeMember({
810
+ visibility: "public",
811
+ variableDecl: varDecl,
812
+ });
813
+ const ctx = createMockScopeContext("Shared", [member]);
814
+ const input = createMockInput();
815
+ const state = createMockState();
816
+ const orchestrator = createMockOrchestrator();
817
+
818
+ const result = generateScope(ctx, input, state, orchestrator);
819
+
820
+ expect(result.code).toContain("volatile bool Shared_flag = false;");
821
+ expect(result.code).not.toContain("static volatile bool Shared_flag");
822
+ });
823
+
824
+ it("generates volatile-keyword variable with volatile modifier (Issue #998)", () => {
825
+ const varDecl = createMockVariableDecl({
826
+ name: "reg",
827
+ type: "u8",
828
+ isVolatile: true,
829
+ initialValue: "0",
830
+ });
831
+ const member = createMockScopeMember({ variableDecl: varDecl });
832
+ const ctx = createMockScopeContext("HW", [member]);
833
+ const input = createMockInput();
834
+ const state = createMockState();
835
+ const orchestrator = createMockOrchestrator();
836
+
837
+ const result = generateScope(ctx, input, state, orchestrator);
838
+
839
+ expect(result.code).toContain("static volatile uint8_t HW_reg = 0;");
840
+ });
841
+
842
+ it("throws error when both atomic and volatile are specified (Issue #998)", () => {
843
+ const varDecl = createMockVariableDecl({
844
+ name: "bad",
845
+ type: "u8",
846
+ isAtomic: true,
847
+ isVolatile: true,
848
+ initialValue: "0",
849
+ startLine: 10,
850
+ });
851
+ const member = createMockScopeMember({ variableDecl: varDecl });
852
+ const ctx = createMockScopeContext("Test", [member]);
853
+ const input = createMockInput();
854
+ const state = createMockState();
855
+ const orchestrator = createMockOrchestrator();
856
+
857
+ expect(() => generateScope(ctx, input, state, orchestrator)).toThrow(
858
+ /Cannot use both 'atomic' and 'volatile' modifiers/,
859
+ );
860
+ });
775
861
  });
776
862
 
777
863
  // ========================================================================
@@ -18,6 +18,7 @@ import IGeneratorInput from "../IGeneratorInput";
18
18
  import IGeneratorState from "../IGeneratorState";
19
19
  import IOrchestrator from "../IOrchestrator";
20
20
  import BinaryExprUtils from "./BinaryExprUtils";
21
+ import CodeGenState from "../../../../state/CodeGenState";
21
22
 
22
23
  /**
23
24
  * Generator context passed to child generators.
@@ -116,6 +117,7 @@ const generateAndExpr = (
116
117
  * ADR-001: = becomes == in C
117
118
  * ADR-017: Enum type safety validation
118
119
  * ADR-045: String comparison via strcmp()
120
+ * Issue #1032: Clear expectedType for comparison operands
119
121
  */
120
122
  const generateEqualityExpr = (
121
123
  node: Parser.EqualityExpressionContext,
@@ -151,18 +153,14 @@ const generateEqualityExpr = (
151
153
  // Generate strcmp for string comparison - needs string.h
152
154
  effects.push({ type: "include", header: "string" });
153
155
 
154
- const leftResult = generateRelationalExpr(
155
- exprs[0],
156
- input,
157
- state,
158
- orchestrator,
159
- );
160
- const rightResult = generateRelationalExpr(
161
- exprs[1],
162
- input,
163
- state,
164
- orchestrator,
165
- );
156
+ // Issue #1032: Clear expectedType for equality comparisons.
157
+ // Use CodeGenState.withoutExpectedType() to clear the global state that
158
+ // generators read via getState(). The passed state is not used for
159
+ // expectedType lookup - generators read from CodeGenState directly.
160
+ const [leftResult, rightResult] = CodeGenState.withoutExpectedType(() => [
161
+ generateRelationalExpr(exprs[0], input, state, orchestrator),
162
+ generateRelationalExpr(exprs[1], input, state, orchestrator),
163
+ ]);
166
164
  effects.push(...leftResult.effects, ...rightResult.effects);
167
165
 
168
166
  const fullText = node.getText();
@@ -183,18 +181,29 @@ const generateEqualityExpr = (
183
181
  // Issue #152: Extract operators in order from parse tree children
184
182
  // ADR-001: C-Next uses = for equality, transpile to ==
185
183
  const operators = orchestrator.getOperatorsFromChildren(node);
186
- return accumulateBinaryExprs(
187
- exprs,
188
- operators,
189
- "=",
190
- generateRelationalExpr,
191
- { input, state, orchestrator },
192
- BinaryExprUtils.mapEqualityOperator,
184
+
185
+ // Issue #1032: Clear expectedType for equality comparisons.
186
+ // The U suffix for MISRA 7.2 compliance applies to assignments, not comparisons.
187
+ // Use CodeGenState.withoutExpectedType() to clear the global state that
188
+ // generators read via getState(). The passed state is not used for
189
+ // expectedType lookup - generators read from CodeGenState directly.
190
+ return CodeGenState.withoutExpectedType(() =>
191
+ accumulateBinaryExprs(
192
+ exprs,
193
+ operators,
194
+ "=",
195
+ generateRelationalExpr,
196
+ { input, state, orchestrator },
197
+ BinaryExprUtils.mapEqualityOperator,
198
+ ),
193
199
  );
194
200
  };
195
201
 
196
202
  /**
197
203
  * Generate C code for a relational expression.
204
+ * Issue #1032: Clear expectedType for comparison operands - MISRA 7.2 suffix
205
+ * should not apply to comparisons, only to assignments. This prevents
206
+ * `i32 < 0` from becoming `signedIdx < 0U` which changes comparison semantics.
198
207
  */
199
208
  const generateRelationalExpr = (
200
209
  node: Parser.RelationalExpressionContext,
@@ -210,11 +219,21 @@ const generateRelationalExpr = (
210
219
 
211
220
  // Issue #152: Extract operators in order from parse tree children
212
221
  const operators = orchestrator.getOperatorsFromChildren(node);
213
- return accumulateBinaryExprs(exprs, operators, "<", generateBitwiseOrExpr, {
214
- input,
215
- state,
216
- orchestrator,
217
- });
222
+
223
+ // Issue #1032: Clear expectedType for relational comparisons.
224
+ // The U suffix for MISRA 7.2 compliance applies to assignments, not comparisons.
225
+ // Comparing `i32 < 0` should NOT generate `signedIdx < 0U` because that
226
+ // changes semantics due to C's integer promotion rules.
227
+ // Use CodeGenState.withoutExpectedType() to clear the global state that
228
+ // generators read via getState(). The passed state is not used for
229
+ // expectedType lookup - generators read from CodeGenState directly.
230
+ return CodeGenState.withoutExpectedType(() =>
231
+ accumulateBinaryExprs(exprs, operators, "<", generateBitwiseOrExpr, {
232
+ input,
233
+ state,
234
+ orchestrator,
235
+ }),
236
+ );
218
237
  };
219
238
 
220
239
  /**
@@ -104,6 +104,29 @@ const _parameterExpectsAddressOf = (
104
104
  return paramBaseType === argType;
105
105
  };
106
106
 
107
+ /**
108
+ * Resolve an argument expression's C-Next type for the address-of decision.
109
+ * Prefers the expression type, then the codegen variable registry, then the C
110
+ * symbol table — the last covers extern C globals (e.g. an lvgl font
111
+ * `lv_font_montserrat_32` of type `lv_font_t`, Issue #985) whose type only
112
+ * header symbol collection knows. Returns null when the type can't be resolved
113
+ * or the argument is already an address-of expression.
114
+ */
115
+ const _resolveArgType = (
116
+ e: ExpressionContext,
117
+ argCode: string,
118
+ typeInfoBaseType: string | undefined,
119
+ orchestrator: IOrchestrator,
120
+ ): string | null => {
121
+ const exprType = orchestrator.getExpressionType(e);
122
+ if (exprType) return exprType;
123
+ if (argCode.startsWith("&")) return null;
124
+ if (typeInfoBaseType) return typeInfoBaseType;
125
+ const cSymbol = CodeGenState.symbolTable?.getCSymbol(argCode);
126
+ if (cSymbol?.kind === "variable" && !cSymbol.isArray) return cSymbol.type;
127
+ return null;
128
+ };
129
+
107
130
  /**
108
131
  * Generate argument code for a C/C++ function call.
109
132
  * Handles automatic address-of (&) for struct arguments passed to pointer params.
@@ -148,25 +171,16 @@ const _generateCFunctionArg = (
148
171
  return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
149
172
  }
150
173
 
151
- // Try getExpressionType first
152
- let argType = orchestrator.getExpressionType(e);
153
-
154
- // Issue #322: If getExpressionType returns null (e.g., for this.member),
155
- // fall back to looking up the generated code in the type registry
156
- let isPointerVariable = false;
174
+ // Resolve the argument's type (expression type → variable registry → C symbol
175
+ // table for extern globals) to decide whether it needs address-of.
157
176
  const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
158
- if (!argType && !argCode.startsWith("&")) {
159
- if (typeInfo) {
160
- argType = typeInfo.baseType;
161
- }
162
- }
163
- // Issue #895 Bug B: Check if variable was inferred as a pointer
164
- if (typeInfo?.isPointer) {
165
- isPointerVariable = true;
166
- }
177
+ const argType = _resolveArgType(e, argCode, typeInfo?.baseType, orchestrator);
178
+ // Issue #895 Bug B: a variable already inferred as a pointer must not get `&`.
179
+ const isPointerVariable = typeInfo?.isPointer ?? false;
167
180
 
168
181
  // Issue #948: Check if argument is an opaque scope variable (already a pointer)
169
- const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariable(argCode);
182
+ // Issue #996: ...including an element of an opaque-handle array (arr[i])
183
+ const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariableAccess(argCode);
170
184
 
171
185
  // Add & if argument needs address-of to match parameter type.
172
186
  // Issue #322: struct types passed to pointer params.
@@ -284,50 +298,54 @@ const generateFunctionCall = (
284
298
  // Get function signature once for all arguments
285
299
  const sig = input.functionSignatures.get(funcExpr);
286
300
 
287
- const args = argExprs
288
- .map((e, idx) => {
289
- // Get parameter type info from local signature or cross-file SymbolTable
290
- const resolved = CallExprUtils.resolveTargetParam(
291
- sig,
292
- idx,
293
- funcExpr,
294
- input.symbolTable,
295
- );
296
- const targetParam = resolved.param;
297
-
298
- // C/C++ function: use pass-by-value semantics
299
- if (!isCNextFunc) {
300
- return _generateCFunctionArg(e, targetParam, input, orchestrator);
301
- }
302
-
303
- // C-Next function: check if target parameter should be passed by value
304
- if (
305
- _shouldPassByValue(
306
- funcExpr,
301
+ // Issue #992: Clear inDeclarationInit for function call arguments — struct
302
+ // initializers inside function args need compound literals, not plain designated initializers.
303
+ const args = CodeGenState.withoutDeclarationInit(() =>
304
+ argExprs
305
+ .map((e, idx) => {
306
+ // Get parameter type info from local signature or cross-file SymbolTable
307
+ const resolved = CallExprUtils.resolveTargetParam(
308
+ sig,
307
309
  idx,
308
- targetParam,
309
- resolved.isCrossFile,
310
- orchestrator,
311
- )
312
- ) {
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
- );
319
- return wrapWithCppEnumCast(
320
- argCode,
321
- e,
322
- targetParam?.baseType,
323
- orchestrator,
310
+ funcExpr,
311
+ input.symbolTable,
324
312
  );
325
- }
326
-
327
- // Target parameter is pass-by-reference: use & logic
328
- return orchestrator.generateFunctionArg(e, targetParam?.baseType);
329
- })
330
- .join(", ");
313
+ const targetParam = resolved.param;
314
+
315
+ // C/C++ function: use pass-by-value semantics
316
+ if (!isCNextFunc) {
317
+ return _generateCFunctionArg(e, targetParam, input, orchestrator);
318
+ }
319
+
320
+ // C-Next function: check if target parameter should be passed by value
321
+ if (
322
+ _shouldPassByValue(
323
+ funcExpr,
324
+ idx,
325
+ targetParam,
326
+ resolved.isCrossFile,
327
+ orchestrator,
328
+ )
329
+ ) {
330
+ // Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
331
+ const argCode = CodeGenState.withExpectedType(
332
+ targetParam?.baseType,
333
+ () => orchestrator.generateExpression(e),
334
+ true, // suppressEnumResolution
335
+ );
336
+ return wrapWithCppEnumCast(
337
+ argCode,
338
+ e,
339
+ targetParam?.baseType,
340
+ orchestrator,
341
+ );
342
+ }
343
+
344
+ // Target parameter is pass-by-reference: use & logic
345
+ return orchestrator.generateFunctionArg(e, targetParam?.baseType);
346
+ })
347
+ .join(", "),
348
+ );
331
349
 
332
350
  return { code: `${funcExpr}(${args})`, effects };
333
351
  };
@@ -15,6 +15,7 @@ import TGeneratorEffect from "../TGeneratorEffect";
15
15
  import IGeneratorInput from "../IGeneratorInput";
16
16
  import IGeneratorState from "../IGeneratorState";
17
17
  import IOrchestrator from "../IOrchestrator";
18
+ import CodeGenState from "../../../../state/CodeGenState";
18
19
 
19
20
  /**
20
21
  * Generate C code for an expression (entry point).
@@ -74,9 +75,15 @@ const generateTernaryExpr = (
74
75
  orchestrator.validateTernaryConditionNoFunctionCall(condition);
75
76
 
76
77
  // Generate C output - parentheses already present from grammar
78
+ // Issue #992: Clear inDeclarationInit in ternary arms — struct initializers
79
+ // inside ternary branches need compound literals, not plain designated initializers.
77
80
  const condCode = orchestrator.generateOrExpr(condition);
78
- const trueCode = orchestrator.generateOrExpr(trueExpr);
79
- const falseCode = orchestrator.generateOrExpr(falseExpr);
81
+ const trueCode = CodeGenState.withoutDeclarationInit(() =>
82
+ orchestrator.generateOrExpr(trueExpr),
83
+ );
84
+ const falseCode = CodeGenState.withoutDeclarationInit(() =>
85
+ orchestrator.generateOrExpr(falseExpr),
86
+ );
80
87
 
81
88
  return { code: `(${condCode}) ? ${trueCode} : ${falseCode}`, effects };
82
89
  };
@@ -155,17 +155,14 @@ const generatePostfixExpression = (
155
155
  // Issue #895: Callback-compatible params need pointer semantics even in C++ mode
156
156
  const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
157
157
 
158
- // Issue #579: Check if we have subscript access on a non-array parameter
159
- const hasSubscriptOps = ops.some((op) => op.expression().length > 0);
160
- const isNonArrayParamWithSubscript =
161
- paramInfo && !paramInfo.isArray && !paramInfo.isStruct && hasSubscriptOps;
162
-
163
- let result: string;
164
- if (isNonArrayParamWithSubscript) {
165
- result = rootIdentifier!;
166
- } else {
167
- result = orchestrator.generatePrimaryExpr(primary);
168
- }
158
+ // Issue #1100: Subscripted parameters resolve through the normal primary
159
+ // expression path (ParameterDereferenceResolver), same as any other
160
+ // parameter reference. This is a no-op for array/struct/string params
161
+ // (already pointer-like, so `buf[idx]` is unaffected), and correctly
162
+ // dereferences a scalar parameter that became a pointer because it's
163
+ // modified elsewhere in the function, so bit access (`v[4]`) reads
164
+ // through the pointer instead of pointer-indexing past it.
165
+ const result: string = orchestrator.generatePrimaryExpr(primary);
169
166
 
170
167
  const primaryTypeInfo = rootIdentifier
171
168
  ? CodeGenState.getVariableTypeInfo(rootIdentifier)
@@ -1805,6 +1802,14 @@ const handleBitRangeSubscript = (
1805
1802
  orchestrator.generateExpression(exprs[1]),
1806
1803
  ]);
1807
1804
 
1805
+ // Issue #1094: resolve a const/macro width to its numeric value so the mask is
1806
+ // precomputed (byte-identical to a literal width) instead of a runtime
1807
+ // ((1U << W) - 1) — which is UB at full width (1U << 32) and uses the wrong
1808
+ // base type for >32-bit widths. The "U" suffix matches the literal path, which
1809
+ // generates bit widths under a size_t expectedType.
1810
+ const widthConst = orchestrator.tryEvaluateConstant(exprs[1]);
1811
+ const maskWidth = widthConst === undefined ? width : `${widthConst}U`;
1812
+
1808
1813
  const isFloatType =
1809
1814
  ctx.primaryTypeInfo?.baseType === "f32" ||
1810
1815
  ctx.primaryTypeInfo?.baseType === "f64";
@@ -1817,13 +1822,19 @@ const handleBitRangeSubscript = (
1817
1822
  baseType: ctx.primaryTypeInfo!.baseType,
1818
1823
  start,
1819
1824
  width,
1825
+ maskWidth,
1820
1826
  },
1821
1827
  state,
1822
1828
  orchestrator,
1823
1829
  effects,
1824
1830
  );
1825
1831
  } else {
1826
- const mask = orchestrator.generateBitMask(width);
1832
+ // Issue #1094: 64-bit operands need a 64-bit mask base (1ULL / wide hex) so
1833
+ // widths > 32 don't shift past a 32-bit literal's width.
1834
+ const is64BitOperand =
1835
+ ctx.primaryTypeInfo?.baseType === "u64" ||
1836
+ ctx.primaryTypeInfo?.baseType === "i64";
1837
+ const mask = orchestrator.generateBitMask(maskWidth, is64BitOperand);
1827
1838
  // Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
1828
1839
  let expr: string;
1829
1840
  if (start === "0" || start === "0U") {
@@ -1861,6 +1872,8 @@ interface IFloatBitRangeContext {
1861
1872
  baseType: string;
1862
1873
  start: string;
1863
1874
  width: string;
1875
+ /** Width formatted for mask generation (const-resolved when possible, #1094). */
1876
+ maskWidth: string;
1864
1877
  }
1865
1878
 
1866
1879
  /**
@@ -1892,7 +1905,7 @@ const handleFloatBitRange = (
1892
1905
  const floatType = getFloatTypeName(ctx.baseType);
1893
1906
  const intType = isF64 ? "uint64_t" : "uint32_t";
1894
1907
  const shadowName = `__bits_${ctx.rootIdentifier}`;
1895
- const mask = orchestrator.generateBitMask(ctx.width, isF64);
1908
+ const mask = orchestrator.generateBitMask(ctx.maskWidth, isF64);
1896
1909
 
1897
1910
  const needsDeclaration = !orchestrator.hasFloatBitShadow(shadowName);
1898
1911
  if (needsDeclaration) {
@@ -1477,4 +1477,48 @@ describe("CallExprGenerator", () => {
1477
1477
  expect(result.code).toBe("use_handle(my_handle)");
1478
1478
  });
1479
1479
  });
1480
+
1481
+ describe("inDeclarationInit clearing (Issue #992)", () => {
1482
+ it("clears inDeclarationInit during function argument generation", () => {
1483
+ CodeGenState.inDeclarationInit = true;
1484
+
1485
+ const argExprs = [createMockExpressionContext("myArg")];
1486
+ const argCtx = createMockArgListContext(argExprs);
1487
+ const input = createMockInput();
1488
+ const state = createMockState();
1489
+
1490
+ let flagDuringArg = true;
1491
+ const orchestrator = createMockOrchestrator({
1492
+ isCNextFunction: vi.fn(() => false),
1493
+ generateExpression: vi.fn((ctx: Parser.ExpressionContext) => {
1494
+ flagDuringArg = CodeGenState.inDeclarationInit;
1495
+ return ctx.getText();
1496
+ }),
1497
+ });
1498
+
1499
+ generateFunctionCall("myFunc", argCtx, input, state, orchestrator);
1500
+
1501
+ expect(flagDuringArg).toBe(false);
1502
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1503
+ });
1504
+
1505
+ it("restores inDeclarationInit after argument generation", () => {
1506
+ CodeGenState.inDeclarationInit = true;
1507
+
1508
+ const argExprs = [
1509
+ createMockExpressionContext("a"),
1510
+ createMockExpressionContext("b"),
1511
+ ];
1512
+ const argCtx = createMockArgListContext(argExprs);
1513
+ const input = createMockInput();
1514
+ const state = createMockState();
1515
+ const orchestrator = createMockOrchestrator({
1516
+ isCNextFunction: vi.fn(() => false),
1517
+ });
1518
+
1519
+ generateFunctionCall("myFunc", argCtx, input, state, orchestrator);
1520
+
1521
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1522
+ });
1523
+ });
1480
1524
  });
@@ -1,9 +1,10 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import expressionGenerators from "../ExpressionGenerator";
3
3
  import IGeneratorInput from "../../IGeneratorInput";
4
4
  import IGeneratorState from "../../IGeneratorState";
5
5
  import IOrchestrator from "../../IOrchestrator";
6
6
  import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
7
+ import CodeGenState from "../../../../../state/CodeGenState";
7
8
 
8
9
  // ========================================================================
9
10
  // Test Helpers
@@ -455,6 +456,86 @@ describe("ExpressionGenerator", () => {
455
456
  });
456
457
  });
457
458
 
459
+ describe("inDeclarationInit clearing (Issue #992)", () => {
460
+ beforeEach(() => {
461
+ CodeGenState.reset();
462
+ });
463
+
464
+ it("clears inDeclarationInit in ternary true and false arms", () => {
465
+ CodeGenState.inDeclarationInit = true;
466
+
467
+ const condition = createMockOrExpr("x > 0");
468
+ const trueExpr = createMockOrExpr("a");
469
+ const falseExpr = createMockOrExpr("b");
470
+ const ctx = createMockTernaryContext([condition, trueExpr, falseExpr]);
471
+
472
+ const input = createMockInput();
473
+ const state = createMockState();
474
+
475
+ const flagDuringTrue: boolean[] = [];
476
+ const flagDuringFalse: boolean[] = [];
477
+
478
+ const orchestrator = createMockOrchestrator();
479
+ let callCount = 0;
480
+ (
481
+ orchestrator.generateOrExpr as ReturnType<typeof vi.fn>
482
+ ).mockImplementation((ctx: Parser.OrExpressionContext) => {
483
+ callCount++;
484
+ if (callCount === 2) {
485
+ flagDuringTrue.push(CodeGenState.inDeclarationInit);
486
+ } else if (callCount === 3) {
487
+ flagDuringFalse.push(CodeGenState.inDeclarationInit);
488
+ }
489
+ return ctx.getText();
490
+ });
491
+
492
+ expressionGenerators.generateTernaryExpr(
493
+ ctx,
494
+ input,
495
+ state,
496
+ orchestrator,
497
+ );
498
+
499
+ expect(flagDuringTrue).toEqual([false]);
500
+ expect(flagDuringFalse).toEqual([false]);
501
+ expect(CodeGenState.inDeclarationInit).toBe(true);
502
+ });
503
+
504
+ it("does not affect condition generation", () => {
505
+ CodeGenState.inDeclarationInit = true;
506
+
507
+ const condition = createMockOrExpr("x > 0");
508
+ const trueExpr = createMockOrExpr("a");
509
+ const falseExpr = createMockOrExpr("b");
510
+ const ctx = createMockTernaryContext([condition, trueExpr, falseExpr]);
511
+
512
+ const input = createMockInput();
513
+ const state = createMockState();
514
+
515
+ let flagDuringCondition = false;
516
+ const orchestrator = createMockOrchestrator();
517
+ let callCount = 0;
518
+ (
519
+ orchestrator.generateOrExpr as ReturnType<typeof vi.fn>
520
+ ).mockImplementation((ctx: Parser.OrExpressionContext) => {
521
+ callCount++;
522
+ if (callCount === 1) {
523
+ flagDuringCondition = CodeGenState.inDeclarationInit;
524
+ }
525
+ return ctx.getText();
526
+ });
527
+
528
+ expressionGenerators.generateTernaryExpr(
529
+ ctx,
530
+ input,
531
+ state,
532
+ orchestrator,
533
+ );
534
+
535
+ expect(flagDuringCondition).toBe(true);
536
+ });
537
+ });
538
+
458
539
  describe("effects", () => {
459
540
  it("returns empty effects array for non-ternary", () => {
460
541
  const orExpr = createMockOrExpr("value");