c-next 0.2.4 → 0.2.6

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 (51) hide show
  1. package/dist/index.js +561 -78
  2. package/dist/index.js.map +3 -3
  3. package/package.json +3 -1
  4. package/src/transpiler/Transpiler.ts +1 -1
  5. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +194 -8
  6. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +140 -0
  7. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +41 -0
  8. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
  9. package/src/transpiler/output/codegen/CodeGenerator.ts +195 -17
  10. package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
  11. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
  12. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
  13. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
  14. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
  15. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
  16. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
  17. package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
  18. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
  19. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
  20. package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
  21. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
  22. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
  23. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
  24. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
  25. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
  26. package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
  27. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
  28. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
  29. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
  30. package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
  31. package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
  32. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
  33. package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
  34. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
  35. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
  36. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
  37. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
  38. package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
  39. package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
  40. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
  41. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
  42. package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
  43. package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
  44. package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
  45. package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
  46. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
  47. package/src/transpiler/state/CodeGenState.ts +21 -2
  48. package/src/utils/BitUtils.ts +17 -13
  49. package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
  50. package/src/utils/__tests__/BitUtils.test.ts +56 -56
  51. package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
@@ -102,7 +102,7 @@ describe("BitmapHandlers", () => {
102
102
  const result = getHandler()!(ctx);
103
103
 
104
104
  expect(result).toContain("flags =");
105
- expect(result).toContain("& ~(1 << 0)");
105
+ expect(result).toContain("& ~(1U << 0)");
106
106
  expect(result).toContain("<< 0");
107
107
  });
108
108
 
@@ -204,8 +204,8 @@ describe("BitmapHandlers", () => {
204
204
  const result = getHandler()!(ctx);
205
205
 
206
206
  expect(result).toContain("flags =");
207
- expect(result).toContain("& ~(0x7 << 4)");
208
- expect(result).toContain("(3 & 0x7)");
207
+ expect(result).toContain("& ~(0x7U << 4)");
208
+ expect(result).toContain("(3 & 0x7U)");
209
209
  expect(result).toContain("<< 4");
210
210
  });
211
211
 
@@ -255,7 +255,7 @@ describe("BitmapHandlers", () => {
255
255
  const result = getHandler()!(ctx);
256
256
 
257
257
  expect(result).toContain("flagsArray[i] =");
258
- expect(result).toContain("& ~(1 << 0)");
258
+ expect(result).toContain("& ~(1U << 0)");
259
259
  });
260
260
  });
261
261
 
@@ -309,7 +309,7 @@ describe("BitmapHandlers", () => {
309
309
  const result = getHandler()!(ctx);
310
310
 
311
311
  expect(result).toContain("MOTOR_CTRL =");
312
- expect(result).toContain("& ~(1 << 0)");
312
+ expect(result).toContain("& ~(1U << 0)");
313
313
  });
314
314
  });
315
315
 
@@ -84,7 +84,7 @@ describe("RegisterHandlers", () => {
84
84
  const result = getHandler()!(ctx);
85
85
 
86
86
  expect(result).toContain("GPIO7_DR_SET");
87
- expect(result).toContain("& ~(1 <<");
87
+ expect(result).toContain("& ~(1U <<");
88
88
  expect(result).toContain("LED_BIT");
89
89
  });
90
90
 
@@ -99,7 +99,7 @@ describe("RegisterHandlers", () => {
99
99
 
100
100
  const result = getHandler()!(ctx);
101
101
 
102
- expect(result).toBe("GPIO7_DR_SET = (1 << LED_BIT);");
102
+ expect(result).toBe("GPIO7_DR_SET = (1U << LED_BIT);");
103
103
  });
104
104
 
105
105
  it("throws on write-only register with false value", () => {
@@ -309,7 +309,7 @@ describe("RegisterHandlers", () => {
309
309
  const result = getHandler()!(ctx);
310
310
 
311
311
  expect(result).toContain("Motor_GPIO7_DR_SET");
312
- expect(result).toContain("& ~(1 <<");
312
+ expect(result).toContain("& ~(1U <<");
313
313
  });
314
314
 
315
315
  it("generates simple write for write-only scoped register", () => {
@@ -327,7 +327,7 @@ describe("RegisterHandlers", () => {
327
327
 
328
328
  const result = getHandler()!(ctx);
329
329
 
330
- expect(result).toBe("Motor_GPIO7_DR_SET = (1 << LED_BIT);");
330
+ expect(result).toBe("Motor_GPIO7_DR_SET = (1U << LED_BIT);");
331
331
  });
332
332
 
333
333
  it("throws when used outside scope", () => {
@@ -126,21 +126,28 @@ const _generateCFunctionArg = (
126
126
 
127
127
  // Issue #322: If getExpressionType returns null (e.g., for this.member),
128
128
  // fall back to looking up the generated code in the type registry
129
+ let isPointerVariable = false;
130
+ const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
129
131
  if (!argType && !argCode.startsWith("&")) {
130
- const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
131
132
  if (typeInfo) {
132
133
  argType = typeInfo.baseType;
133
134
  }
134
135
  }
136
+ // Issue #895 Bug B: Check if variable was inferred as a pointer
137
+ if (typeInfo?.isPointer) {
138
+ isPointerVariable = true;
139
+ }
135
140
 
136
141
  // Add & if argument needs address-of to match parameter type.
137
142
  // Issue #322: struct types passed to pointer params.
138
143
  // Issue #832: typedef'd pointer types (e.g., handle_t passed to handle_t*).
144
+ // Issue #895 Bug B: Skip address-of for variables that are already pointers
139
145
  const needsAddressOf =
140
146
  argType &&
141
147
  !argType.endsWith("*") &&
142
148
  !argCode.startsWith("&") &&
143
149
  !targetParam.isArray &&
150
+ !isPointerVariable &&
144
151
  (orchestrator.isStructType(argType) ||
145
152
  _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
146
153
 
@@ -110,6 +110,8 @@ const initializeTrackingState = (
110
110
  interface IPostfixContext {
111
111
  rootIdentifier: string | undefined;
112
112
  isStructParam: boolean;
113
+ /** Issue #895: Force pointer semantics for callback-compatible params */
114
+ forcePointerSemantics: boolean;
113
115
  input: IGeneratorInput;
114
116
  state: IGeneratorState;
115
117
  orchestrator: IOrchestrator;
@@ -149,6 +151,8 @@ const generatePostfixExpression = (
149
151
  ? state.currentParameters.get(rootIdentifier)
150
152
  : null;
151
153
  const isStructParam = paramInfo?.isStruct ?? false;
154
+ // Issue #895: Callback-compatible params need pointer semantics even in C++ mode
155
+ const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
152
156
 
153
157
  // Issue #579: Check if we have subscript access on a non-array parameter
154
158
  const hasSubscriptOps = ops.some((op) => op.expression().length > 0);
@@ -178,6 +182,7 @@ const generatePostfixExpression = (
178
182
  const postfixCtx: IPostfixContext = {
179
183
  rootIdentifier,
180
184
  isStructParam,
185
+ forcePointerSemantics,
181
186
  input,
182
187
  state,
183
188
  orchestrator,
@@ -303,6 +308,7 @@ const handleMemberOp = (
303
308
  memberName,
304
309
  rootIdentifier: ctx.rootIdentifier,
305
310
  isStructParam: ctx.isStructParam,
311
+ forcePointerSemantics: ctx.forcePointerSemantics,
306
312
  isGlobalAccess: tracking.isGlobalAccess,
307
313
  isCppAccessChain: tracking.isCppAccessChain,
308
314
  currentStructType: tracking.currentStructType,
@@ -1409,6 +1415,8 @@ interface IMemberAccessContext {
1409
1415
  memberName: string;
1410
1416
  rootIdentifier: string | undefined;
1411
1417
  isStructParam: boolean;
1418
+ /** Issue #895: Force pointer semantics for callback-compatible params */
1419
+ forcePointerSemantics: boolean;
1412
1420
  isGlobalAccess: boolean;
1413
1421
  isCppAccessChain: boolean;
1414
1422
  currentStructType: string | undefined;
@@ -1657,9 +1665,13 @@ const tryStructParamAccess = (
1657
1665
  return null;
1658
1666
  }
1659
1667
 
1660
- const structParamSep = memberAccessChain.getStructParamSeparator({
1661
- cppMode: orchestrator.isCppMode(),
1662
- });
1668
+ // Issue #895: Force pointer semantics for callback-compatible params
1669
+ // even in C++ mode (use -> instead of .)
1670
+ const structParamSep = ctx.forcePointerSemantics
1671
+ ? "->"
1672
+ : memberAccessChain.getStructParamSeparator({
1673
+ cppMode: orchestrator.isCppMode(),
1674
+ });
1663
1675
 
1664
1676
  const output = initializeMemberOutput(ctx);
1665
1677
  output.result = `${ctx.result}${structParamSep}${ctx.memberName}`;
@@ -46,6 +46,11 @@ class ArgumentGenerator {
46
46
  return id;
47
47
  }
48
48
 
49
+ // Issue #895 Bug B: Inferred pointers are already pointers, don't add &
50
+ if (typeInfo?.isPointer) {
51
+ return id;
52
+ }
53
+
49
54
  // Scope member - may need prefixing
50
55
  if (CodeGenState.currentScope) {
51
56
  const members = CodeGenState.getScopeMembers(CodeGenState.currentScope);
@@ -15,6 +15,8 @@ import CodeGenState from "../../../state/CodeGenState.js";
15
15
  import TYPE_WIDTH from "../types/TYPE_WIDTH.js";
16
16
  import ArrayDimensionParser from "./ArrayDimensionParser.js";
17
17
  import IFunctionContextCallbacks from "../types/IFunctionContextCallbacks.js";
18
+ // Issue #895: Parse typedef signatures to determine pointer vs value params
19
+ import TypedefParamParser from "./TypedefParamParser.js";
18
20
 
19
21
  /**
20
22
  * Result from resolving parameter type information.
@@ -124,8 +126,9 @@ class FunctionContextManager {
124
126
  CodeGenState.currentParameters.clear();
125
127
  if (!params) return;
126
128
 
127
- for (const param of params.parameter()) {
128
- FunctionContextManager.processParameter(param, callbacks);
129
+ const paramList = params.parameter();
130
+ for (let i = 0; i < paramList.length; i++) {
131
+ FunctionContextManager.processParameter(paramList[i], callbacks, i);
129
132
  }
130
133
  }
131
134
 
@@ -135,6 +138,7 @@ class FunctionContextManager {
135
138
  static processParameter(
136
139
  param: Parser.ParameterContext,
137
140
  callbacks: IFunctionContextCallbacks,
141
+ paramIndex: number,
138
142
  ): void {
139
143
  const name = param.IDENTIFIER().getText();
140
144
  // Check both C-Next style (u8[8] param) and legacy style (u8 param[8])
@@ -149,23 +153,38 @@ class FunctionContextManager {
149
153
  callbacks,
150
154
  );
151
155
 
152
- // For callback-compatible functions, struct params use by-value semantics
153
- // (no pointer dereference, dot access instead of arrow in C mode)
154
- const isCallbackCompat =
155
- CodeGenState.currentFunctionName !== null &&
156
- CodeGenState.callbackCompatibleFunctions.has(
157
- CodeGenState.currentFunctionName,
158
- );
156
+ // Issue #895: For callback-compatible functions, check the typedef signature
157
+ // to determine if the param should be a pointer or value
158
+ const callbackTypedefInfo =
159
+ FunctionContextManager.getCallbackTypedefParamInfo(paramIndex);
160
+ const isCallbackPointerParam =
161
+ callbackTypedefInfo?.shouldBePointer ?? false;
162
+
163
+ // Determine isStruct: for callback-compatible params, both typedef AND type info matter
164
+ // - If typedef says pointer AND it's actually a struct, use -> access (isStruct=true)
165
+ // - If typedef says pointer BUT it's a primitive (like u8), don't treat as struct
166
+ // (primitives use forcePointerSemantics for dereference instead)
167
+ const isStruct = callbackTypedefInfo
168
+ ? isCallbackPointerParam && typeInfo.isStruct
169
+ : typeInfo.isStruct;
170
+
171
+ // Issue #895: Primitive types that become pointers need dereferencing when used as values
172
+ // e.g., "u8 buf" becoming "uint8_t* buf" requires "*buf" when accessing the value
173
+ const isCallbackPointerPrimitive =
174
+ isCallbackPointerParam && !typeInfo.isStruct && !isArray;
159
175
 
160
176
  // Register in currentParameters
161
177
  const paramInfo = {
162
178
  name,
163
179
  baseType: typeInfo.typeName,
164
180
  isArray,
165
- isStruct: isCallbackCompat ? false : typeInfo.isStruct,
181
+ isStruct,
166
182
  isConst,
167
183
  isCallback: typeInfo.isCallback,
168
184
  isString: typeInfo.isString,
185
+ isCallbackPointerPrimitive,
186
+ // Issue #895: Callback-compatible params need pointer semantics even in C++ mode
187
+ forcePointerSemantics: isCallbackPointerParam,
169
188
  };
170
189
  CodeGenState.currentParameters.set(name, paramInfo);
171
190
 
@@ -374,6 +393,40 @@ class FunctionContextManager {
374
393
  return dimensions;
375
394
  }
376
395
 
396
+ /**
397
+ * Issue #895: Get callback typedef parameter info from the C header.
398
+ * Returns null if not callback-compatible or index is invalid.
399
+ */
400
+ static getCallbackTypedefParamInfo(
401
+ paramIndex: number,
402
+ ): { shouldBePointer: boolean; shouldBeConst: boolean } | null {
403
+ if (CodeGenState.currentFunctionName === null) return null;
404
+
405
+ const typedefName = CodeGenState.callbackCompatibleFunctions.get(
406
+ CodeGenState.currentFunctionName,
407
+ );
408
+ if (!typedefName) return null;
409
+
410
+ const typedefType = CodeGenState.getTypedefType(typedefName);
411
+ if (!typedefType) return null;
412
+
413
+ const shouldBePointer = TypedefParamParser.shouldBePointer(
414
+ typedefType,
415
+ paramIndex,
416
+ );
417
+ const shouldBeConst = TypedefParamParser.shouldBeConst(
418
+ typedefType,
419
+ paramIndex,
420
+ );
421
+
422
+ if (shouldBePointer === null) return null;
423
+
424
+ return {
425
+ shouldBePointer,
426
+ shouldBeConst: shouldBeConst ?? false,
427
+ };
428
+ }
429
+
377
430
  /**
378
431
  * Extract string capacity from a string type context.
379
432
  */
@@ -15,6 +15,19 @@
15
15
  import ISeparatorContext from "../types/ISeparatorContext";
16
16
  import IMemberSeparatorDeps from "../types/IMemberSeparatorDeps";
17
17
 
18
+ /**
19
+ * Input parameters for building a separator context
20
+ */
21
+ interface IBuildContextInput {
22
+ firstId: string;
23
+ hasGlobal: boolean;
24
+ hasThis: boolean;
25
+ currentScope: string | null;
26
+ isStructParam: boolean;
27
+ isCppAccess: boolean;
28
+ forcePointerSemantics?: boolean;
29
+ }
30
+
18
31
  /**
19
32
  * Static utility for resolving member access separators
20
33
  */
@@ -23,14 +36,18 @@ class MemberSeparatorResolver {
23
36
  * Build the separator context for a member access chain
24
37
  */
25
38
  static buildContext(
26
- firstId: string,
27
- hasGlobal: boolean,
28
- hasThis: boolean,
29
- currentScope: string | null,
30
- isStructParam: boolean,
39
+ input: IBuildContextInput,
31
40
  deps: IMemberSeparatorDeps,
32
- isCppAccess: boolean,
33
41
  ): ISeparatorContext {
42
+ const {
43
+ firstId,
44
+ hasGlobal,
45
+ hasThis,
46
+ currentScope,
47
+ isStructParam,
48
+ isCppAccess,
49
+ forcePointerSemantics,
50
+ } = input;
34
51
  const isCrossScope =
35
52
  hasGlobal &&
36
53
  (deps.isKnownScope(firstId) || deps.isKnownRegister(firstId));
@@ -48,6 +65,7 @@ class MemberSeparatorResolver {
48
65
  isCppAccess,
49
66
  scopedRegName,
50
67
  isScopedRegister,
68
+ forcePointerSemantics,
51
69
  };
52
70
  }
53
71
 
@@ -66,7 +84,11 @@ class MemberSeparatorResolver {
66
84
  }
67
85
 
68
86
  // Struct parameter uses -> in C mode, . in C++ mode
87
+ // Issue #895: forcePointerSemantics overrides C++ mode to use ->
69
88
  if (ctx.isStructParam) {
89
+ if (ctx.forcePointerSemantics) {
90
+ return "->";
91
+ }
70
92
  return deps.getStructParamSeparator();
71
93
  }
72
94
 
@@ -28,6 +28,12 @@ class ParameterDereferenceResolver {
28
28
  paramInfo: TParameterInfo,
29
29
  deps: IParameterDereferenceDeps,
30
30
  ): boolean {
31
+ // Issue #895: Primitive params that became pointers due to callback typedef
32
+ // are NOT pass-by-value - they need dereferencing when used as values
33
+ if (paramInfo.isCallbackPointerPrimitive) {
34
+ return false; // Not pass-by-value → will be dereferenced
35
+ }
36
+
31
37
  // ADR-029: Callback parameters are function pointers
32
38
  if (paramInfo.isCallback) {
33
39
  return true;
@@ -92,6 +98,12 @@ class ParameterDereferenceResolver {
92
98
  return id;
93
99
  }
94
100
 
101
+ // Issue #895: Callback-compatible primitives always need dereferencing,
102
+ // even in C++ mode (because they use pointer semantics to match C typedef)
103
+ if (paramInfo.forcePointerSemantics) {
104
+ return `(*${id})`;
105
+ }
106
+
95
107
  // Known primitive that is pass-by-reference needs dereference
96
108
  // Issue #558/#644: In C++ mode, primitives become references
97
109
  return deps.maybeDereference(id);
@@ -42,6 +42,21 @@ interface IFromASTDeps {
42
42
 
43
43
  /** Whether the parameter should use pass-by-value (pre-computed) */
44
44
  isPassByValue: boolean;
45
+
46
+ /** Issue #895: Whether the current function is callback-compatible */
47
+ isCallbackCompatible: boolean;
48
+
49
+ /**
50
+ * Issue #895: Force pass-by-reference for callback-compatible functions
51
+ * When the typedef signature requires a pointer, this overrides normal logic.
52
+ */
53
+ forcePassByReference?: boolean;
54
+
55
+ /**
56
+ * Issue #895: Force const qualifier from callback typedef signature.
57
+ * When the C typedef has `const T*`, this preserves const on the generated param.
58
+ */
59
+ forceConst?: boolean;
45
60
  }
46
61
 
47
62
  /**
@@ -122,7 +137,15 @@ class ParameterInputAdapter {
122
137
  // Determine classification for non-array, non-string types
123
138
  const isKnownStruct = deps.isKnownStruct(typeName);
124
139
  const isKnownPrimitive = !!deps.typeMap[typeName];
125
- const isAutoConst = !deps.isModified && !isConst;
140
+ // Issue #895: Don't add auto-const for callback-compatible functions
141
+ // because it would change the signature and break typedef compatibility
142
+ const isAutoConst =
143
+ !deps.isCallbackCompatible && !deps.isModified && !isConst;
144
+
145
+ // Issue #895: For callback-compatible functions, force pass-by-reference
146
+ // when the typedef signature requires a pointer (e.g., opaque types)
147
+ const isPassByReference =
148
+ deps.forcePassByReference || isKnownStruct || isKnownPrimitive;
126
149
 
127
150
  return {
128
151
  name,
@@ -134,7 +157,12 @@ class ParameterInputAdapter {
134
157
  isCallback: false,
135
158
  isString: false,
136
159
  isPassByValue: deps.isPassByValue,
137
- isPassByReference: isKnownStruct || isKnownPrimitive,
160
+ isPassByReference,
161
+ // Issue #895: Force pointer syntax in C++ mode for callback-compatible functions
162
+ // because C callback typedefs expect pointers, not C++ references
163
+ forcePointerSyntax: deps.forcePassByReference,
164
+ // Issue #895: Preserve const from callback typedef signature
165
+ forceConst: deps.forceConst,
138
166
  };
139
167
  }
140
168
 
@@ -103,14 +103,19 @@ class ParameterSignatureBuilder {
103
103
  /**
104
104
  * Build pass-by-reference parameter signature.
105
105
  * C mode: const Point* p
106
- * C++ mode: const Point& p
106
+ * C++ mode: const Point& p (unless forcePointerSyntax)
107
+ *
108
+ * Issue #895: When forcePointerSyntax is set, always use pointer syntax
109
+ * because C callback typedefs expect pointers, not C++ references.
107
110
  */
108
111
  private static _buildRefParam(
109
112
  param: IParameterInput,
110
113
  refSuffix: string,
111
114
  ): string {
112
115
  const constPrefix = this._getConstPrefix(param);
113
- return `${constPrefix}${param.mappedType}${refSuffix} ${param.name}`;
116
+ // Issue #895: Override refSuffix for callback-compatible functions
117
+ const actualSuffix = param.forcePointerSyntax ? "*" : refSuffix;
118
+ return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
114
119
  }
115
120
 
116
121
  /**
@@ -122,13 +127,16 @@ class ParameterSignatureBuilder {
122
127
  }
123
128
 
124
129
  /**
125
- * Get const prefix combining explicit const and auto-const.
126
- * Auto-const is applied to unmodified parameters.
130
+ * Get const prefix combining explicit const, auto-const, and forced const.
131
+ * Priority: forceConst > isConst > isAutoConst
132
+ * Issue #895: forceConst preserves const from callback typedef signature.
127
133
  */
128
134
  private static _getConstPrefix(param: IParameterInput): string {
129
- const autoConst = param.isAutoConst && !param.isConst ? "const " : "";
130
- const explicitConst = param.isConst ? "const " : "";
131
- return `${autoConst}${explicitConst}`;
135
+ // Any source of const results in "const " prefix
136
+ if (param.forceConst || param.isConst || param.isAutoConst) {
137
+ return "const ";
138
+ }
139
+ return "";
132
140
  }
133
141
  }
134
142
 
@@ -303,7 +303,14 @@ class StringDeclHelper {
303
303
  initValue,
304
304
  arrayDims,
305
305
  );
306
- return { code: `${decl} = ${finalInitValue};`, handled: true };
306
+ // MISRA C:2012 Rules 9.3/9.4 - String literals don't fill all inner array bytes,
307
+ // but C standard guarantees zero-initialization of remaining elements
308
+ const suppression =
309
+ "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
310
+ return {
311
+ code: `${suppression}${decl} = ${finalInitValue};`,
312
+ handled: true,
313
+ };
307
314
  }
308
315
 
309
316
  /**
@@ -11,7 +11,7 @@
11
11
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
12
12
  import CodeGenState from "../../../state/CodeGenState.js";
13
13
  import StringUtils from "../../../../utils/StringUtils.js";
14
- import ExpressionUnwrapper from "../utils/ExpressionUnwrapper.js";
14
+ import ExpressionUnwrapper from "../../../../utils/ExpressionUnwrapper";
15
15
 
16
16
  /** Regex for identifying valid C/C++ identifiers */
17
17
  const IDENTIFIER_REGEX = /^[a-zA-Z_]\w*$/;