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.
- package/dist/index.js +561 -78
- package/dist/index.js.map +3 -3
- package/package.json +3 -1
- package/src/transpiler/Transpiler.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +194 -8
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +140 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +41 -0
- package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
- package/src/transpiler/output/codegen/CodeGenerator.ts +195 -17
- package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
- package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
- package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
- package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
- package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
- package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
- package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
- package/src/transpiler/state/CodeGenState.ts +21 -2
- package/src/utils/BitUtils.ts +17 -13
- package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
- package/src/utils/__tests__/BitUtils.test.ts +56 -56
- 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("& ~(
|
|
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("& ~(
|
|
208
|
-
expect(result).toContain("(3 &
|
|
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("& ~(
|
|
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("& ~(
|
|
312
|
+
expect(result).toContain("& ~(1U << 0)");
|
|
313
313
|
});
|
|
314
314
|
});
|
|
315
315
|
|
package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts
CHANGED
|
@@ -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("& ~(
|
|
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 = (
|
|
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("& ~(
|
|
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 = (
|
|
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
|
-
|
|
1661
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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,
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
126
|
-
*
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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 "
|
|
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*$/;
|