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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypedefParamParser - Parses C function pointer typedef signatures
|
|
3
|
+
*
|
|
4
|
+
* Extracts parameter types from typedef strings like:
|
|
5
|
+
* "void (*)(widget_t *, const rect_t *, uint8_t *)"
|
|
6
|
+
* "void (*)(Point p)"
|
|
7
|
+
*
|
|
8
|
+
* Used by Issue #895 to determine if callback params should be pointers or values.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parsed parameter info from a typedef.
|
|
13
|
+
*/
|
|
14
|
+
interface ITypedefParam {
|
|
15
|
+
/** Full type string (e.g., "widget_t *", "const rect_t *", "uint8_t *") */
|
|
16
|
+
type: string;
|
|
17
|
+
/** Whether this is a pointer type */
|
|
18
|
+
isPointer: boolean;
|
|
19
|
+
/** Whether this has const qualifier */
|
|
20
|
+
isConst: boolean;
|
|
21
|
+
/** Base type without pointer/const (e.g., "widget_t", "rect_t", "uint8_t") */
|
|
22
|
+
baseType: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parse result for a typedef signature.
|
|
27
|
+
*/
|
|
28
|
+
interface ITypedefParseResult {
|
|
29
|
+
/** Return type */
|
|
30
|
+
returnType: string;
|
|
31
|
+
/** Parsed parameters */
|
|
32
|
+
params: ITypedefParam[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class TypedefParamParser {
|
|
36
|
+
/**
|
|
37
|
+
* Parse a function pointer typedef type string.
|
|
38
|
+
*
|
|
39
|
+
* @param typedefType - The type string, e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)"
|
|
40
|
+
* @returns Parsed result with return type and parameters, or null if parsing fails
|
|
41
|
+
*/
|
|
42
|
+
static parse(typedefType: string): ITypedefParseResult | null {
|
|
43
|
+
// Expected format: "return_type (*)(param1, param2, ...)"
|
|
44
|
+
// Find the (*) marker first
|
|
45
|
+
const funcPtrIndex = typedefType.indexOf("(*)");
|
|
46
|
+
if (funcPtrIndex === -1) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const returnType = typedefType.substring(0, funcPtrIndex).trim();
|
|
51
|
+
|
|
52
|
+
// Find the opening paren after (*)
|
|
53
|
+
const afterFuncPtr = typedefType.substring(funcPtrIndex + 3).trim();
|
|
54
|
+
if (!afterFuncPtr.startsWith("(")) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Extract params by finding the matching closing paren
|
|
59
|
+
const paramsStr = TypedefParamParser.extractParenContent(afterFuncPtr);
|
|
60
|
+
if (paramsStr === null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Handle void or empty params
|
|
65
|
+
if (!paramsStr || paramsStr === "void") {
|
|
66
|
+
return { returnType, params: [] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Split by comma, handling nested parentheses
|
|
70
|
+
const paramStrings = TypedefParamParser.splitParams(paramsStr);
|
|
71
|
+
const params = paramStrings.map((p) => TypedefParamParser.parseParam(p));
|
|
72
|
+
|
|
73
|
+
return { returnType, params };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract content between matching parentheses, handling arbitrary nesting.
|
|
78
|
+
* @param str - String starting with '('
|
|
79
|
+
* @returns Content between outer parens, or null if no match
|
|
80
|
+
*/
|
|
81
|
+
private static extractParenContent(str: string): string | null {
|
|
82
|
+
if (!str.startsWith("(")) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let depth = 0;
|
|
87
|
+
for (let i = 0; i < str.length; i++) {
|
|
88
|
+
if (str[i] === "(") {
|
|
89
|
+
depth++;
|
|
90
|
+
} else if (str[i] === ")") {
|
|
91
|
+
depth--;
|
|
92
|
+
if (depth === 0) {
|
|
93
|
+
// Found matching close paren - return content between
|
|
94
|
+
return str.substring(1, i);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// No matching close paren found
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Split parameter string by commas, respecting nested parentheses.
|
|
105
|
+
*/
|
|
106
|
+
private static splitParams(paramsStr: string): string[] {
|
|
107
|
+
const params: string[] = [];
|
|
108
|
+
let current = "";
|
|
109
|
+
let depth = 0;
|
|
110
|
+
|
|
111
|
+
for (const char of paramsStr) {
|
|
112
|
+
if (char === "(") {
|
|
113
|
+
depth++;
|
|
114
|
+
current += char;
|
|
115
|
+
} else if (char === ")") {
|
|
116
|
+
depth--;
|
|
117
|
+
current += char;
|
|
118
|
+
} else if (char === "," && depth === 0) {
|
|
119
|
+
params.push(current.trim());
|
|
120
|
+
current = "";
|
|
121
|
+
} else {
|
|
122
|
+
current += char;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (current.trim()) {
|
|
127
|
+
params.push(current.trim());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return params;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse a single parameter type string.
|
|
135
|
+
*/
|
|
136
|
+
private static parseParam(paramStr: string): ITypedefParam {
|
|
137
|
+
const trimmed = paramStr.trim();
|
|
138
|
+
|
|
139
|
+
// Check for pointer
|
|
140
|
+
const isPointer = trimmed.includes("*");
|
|
141
|
+
|
|
142
|
+
// Check for const - handles both "const " (with space) and merged forms
|
|
143
|
+
// C grammar getText() strips spaces, so "const rect_t*" may appear merged
|
|
144
|
+
const isConst = /\bconst\b/.test(trimmed) || trimmed.startsWith("const");
|
|
145
|
+
|
|
146
|
+
// Extract base type (remove const, *, and param name if present)
|
|
147
|
+
let baseType = trimmed
|
|
148
|
+
.replaceAll(/\bconst\b/g, "") // Remove const (with word boundary)
|
|
149
|
+
.replace(/^const/, "") // Remove const at start (no space case) - only once
|
|
150
|
+
.replaceAll("*", "") // Remove pointers
|
|
151
|
+
.replaceAll(/\s+/g, " ") // Normalize whitespace
|
|
152
|
+
.trim();
|
|
153
|
+
|
|
154
|
+
// Remove trailing param name if present (e.g., "rect_t area" -> "rect_t")
|
|
155
|
+
// Only remove if there are multiple words (space-separated)
|
|
156
|
+
if (baseType.includes(" ")) {
|
|
157
|
+
baseType = baseType.replace(/\s+\w+$/, "");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Handle struct keyword
|
|
161
|
+
if (baseType.startsWith("struct ")) {
|
|
162
|
+
baseType = baseType.substring(7);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
type: trimmed,
|
|
167
|
+
isPointer,
|
|
168
|
+
isConst,
|
|
169
|
+
baseType,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the parameter info at a given index, or null if not found.
|
|
175
|
+
*/
|
|
176
|
+
private static getParamAt(
|
|
177
|
+
typedefType: string,
|
|
178
|
+
paramIndex: number,
|
|
179
|
+
): ITypedefParam | null {
|
|
180
|
+
const parsed = TypedefParamParser.parse(typedefType);
|
|
181
|
+
if (!parsed || paramIndex >= parsed.params.length) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return parsed.params[paramIndex];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check if a parameter at a given index should be a pointer based on the typedef.
|
|
189
|
+
*
|
|
190
|
+
* @param typedefType - The typedef type string
|
|
191
|
+
* @param paramIndex - The parameter index (0-based)
|
|
192
|
+
* @returns true if the param should be a pointer, false for value, null if unknown
|
|
193
|
+
*/
|
|
194
|
+
static shouldBePointer(
|
|
195
|
+
typedefType: string,
|
|
196
|
+
paramIndex: number,
|
|
197
|
+
): boolean | null {
|
|
198
|
+
return (
|
|
199
|
+
TypedefParamParser.getParamAt(typedefType, paramIndex)?.isPointer ?? null
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Check if a parameter at a given index should be const based on the typedef.
|
|
205
|
+
*
|
|
206
|
+
* @param typedefType - The typedef type string
|
|
207
|
+
* @param paramIndex - The parameter index (0-based)
|
|
208
|
+
* @returns true if the param should be const, false otherwise, null if unknown
|
|
209
|
+
*/
|
|
210
|
+
static shouldBeConst(
|
|
211
|
+
typedefType: string,
|
|
212
|
+
paramIndex: number,
|
|
213
|
+
): boolean | null {
|
|
214
|
+
return (
|
|
215
|
+
TypedefParamParser.getParamAt(typedefType, paramIndex)?.isConst ?? null
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export default TypedefParamParser;
|
|
@@ -139,6 +139,8 @@ interface IVariableDeclCallbacks {
|
|
|
139
139
|
ctx: Parser.VariableDeclarationContext,
|
|
140
140
|
name: string,
|
|
141
141
|
) => void;
|
|
142
|
+
/** Issue #895 Bug B: Mark variable as pointer in type registry */
|
|
143
|
+
markVariableAsPointer: (name: string) => void;
|
|
142
144
|
/** Get string concatenation operands */
|
|
143
145
|
getStringConcatOperands: (
|
|
144
146
|
ctx: Parser.ExpressionContext,
|
|
@@ -571,9 +573,13 @@ class VariableDeclHelper {
|
|
|
571
573
|
}
|
|
572
574
|
|
|
573
575
|
// Issue #696: Use helper for modifier extraction and validation
|
|
576
|
+
// Issue #852 (MISRA Rule 8.5): Pass hasInitializer and cppMode for correct extern behavior
|
|
577
|
+
const hasInitializer = ctx.expression() !== null;
|
|
574
578
|
const modifiers = VariableModifierBuilder.build(
|
|
575
579
|
ctx,
|
|
576
580
|
CodeGenState.inFunctionBody,
|
|
581
|
+
hasInitializer,
|
|
582
|
+
CodeGenState.cppMode,
|
|
577
583
|
);
|
|
578
584
|
|
|
579
585
|
const name = ctx.IDENTIFIER().getText();
|
|
@@ -587,6 +593,11 @@ class VariableDeclHelper {
|
|
|
587
593
|
// Track local variable metadata
|
|
588
594
|
callbacks.trackLocalVariable(ctx, name);
|
|
589
595
|
|
|
596
|
+
// Issue #895 Bug B: If type was inferred as pointer, mark it in the registry
|
|
597
|
+
if (type.endsWith("*")) {
|
|
598
|
+
callbacks.markVariableAsPointer(name);
|
|
599
|
+
}
|
|
600
|
+
|
|
590
601
|
// ADR-045: Handle bounded string type specially - early return
|
|
591
602
|
const stringResult = StringDeclHelper.generateStringDecl(
|
|
592
603
|
typeCtx,
|
|
@@ -46,12 +46,16 @@ class VariableModifierBuilder {
|
|
|
46
46
|
*
|
|
47
47
|
* @param ctx - Parser context with modifier methods
|
|
48
48
|
* @param inFunctionBody - Whether we're inside a function body (affects extern)
|
|
49
|
+
* @param hasInitializer - Whether the variable has an initializer (affects extern in C mode)
|
|
50
|
+
* @param cppMode - Whether we're generating C++ code (affects extern behavior)
|
|
49
51
|
* @returns Modifier strings ready for use in generated code
|
|
50
52
|
* @throws Error if both atomic and volatile are specified
|
|
51
53
|
*/
|
|
52
54
|
static build(
|
|
53
55
|
ctx: IModifierContext,
|
|
54
56
|
inFunctionBody: boolean,
|
|
57
|
+
hasInitializer: boolean = false,
|
|
58
|
+
cppMode: boolean = false,
|
|
55
59
|
): IVariableModifiers {
|
|
56
60
|
const hasConst = ctx.constModifier?.() ?? false;
|
|
57
61
|
const constMod = hasConst ? "const " : "";
|
|
@@ -59,7 +63,18 @@ class VariableModifierBuilder {
|
|
|
59
63
|
const volatileMod = ctx.volatileModifier() ? "volatile " : "";
|
|
60
64
|
|
|
61
65
|
// Issue #525: Add extern for top-level const in C++ for external linkage
|
|
62
|
-
const
|
|
66
|
+
// In C++, const at file scope has internal linkage by default, so extern is needed.
|
|
67
|
+
//
|
|
68
|
+
// Issue #852 (MISRA Rule 8.5): In C mode, do NOT add extern to definitions
|
|
69
|
+
// (variables with initializers). The extern declaration comes from the header.
|
|
70
|
+
//
|
|
71
|
+
// Summary:
|
|
72
|
+
// - C mode + no initializer: extern (declaration)
|
|
73
|
+
// - C mode + initializer: NO extern (definition - MISRA 8.5)
|
|
74
|
+
// - C++ mode: ALWAYS extern for external linkage (both declarations and definitions)
|
|
75
|
+
const needsExtern =
|
|
76
|
+
hasConst && !inFunctionBody && (cppMode || !hasInitializer);
|
|
77
|
+
const externMod = needsExtern ? "extern " : "";
|
|
63
78
|
|
|
64
79
|
// Validate: cannot use both atomic and volatile
|
|
65
80
|
if (ctx.atomicModifier() && ctx.volatileModifier()) {
|
|
@@ -347,7 +347,7 @@ describe("FunctionContextManager", () => {
|
|
|
347
347
|
const callbacks = createMockCallbacks();
|
|
348
348
|
const param = createMockParam("x", "u32", { isPrimitive: true });
|
|
349
349
|
|
|
350
|
-
FunctionContextManager.processParameter(param, callbacks);
|
|
350
|
+
FunctionContextManager.processParameter(param, callbacks, 0);
|
|
351
351
|
|
|
352
352
|
const paramInfo = CodeGenState.currentParameters.get("x");
|
|
353
353
|
expect(paramInfo).toBeDefined();
|
|
@@ -363,7 +363,7 @@ describe("FunctionContextManager", () => {
|
|
|
363
363
|
isArray: true,
|
|
364
364
|
});
|
|
365
365
|
|
|
366
|
-
FunctionContextManager.processParameter(param, callbacks);
|
|
366
|
+
FunctionContextManager.processParameter(param, callbacks, 0);
|
|
367
367
|
|
|
368
368
|
const paramInfo = CodeGenState.currentParameters.get("arr");
|
|
369
369
|
expect(paramInfo).toBeDefined();
|
|
@@ -377,7 +377,7 @@ describe("FunctionContextManager", () => {
|
|
|
377
377
|
isConst: true,
|
|
378
378
|
});
|
|
379
379
|
|
|
380
|
-
FunctionContextManager.processParameter(param, callbacks);
|
|
380
|
+
FunctionContextManager.processParameter(param, callbacks, 0);
|
|
381
381
|
|
|
382
382
|
const paramInfo = CodeGenState.currentParameters.get("x");
|
|
383
383
|
expect(paramInfo).toBeDefined();
|
|
@@ -391,7 +391,7 @@ describe("FunctionContextManager", () => {
|
|
|
391
391
|
);
|
|
392
392
|
const param = createMockParam("point", "Point", { isUserType: true });
|
|
393
393
|
|
|
394
|
-
FunctionContextManager.processParameter(param, callbacks);
|
|
394
|
+
FunctionContextManager.processParameter(param, callbacks, 0);
|
|
395
395
|
|
|
396
396
|
const paramInfo = CodeGenState.currentParameters.get("point");
|
|
397
397
|
expect(paramInfo).toBeDefined();
|
|
@@ -406,7 +406,7 @@ describe("FunctionContextManager", () => {
|
|
|
406
406
|
stringCapacity: 32,
|
|
407
407
|
});
|
|
408
408
|
|
|
409
|
-
FunctionContextManager.processParameter(param, callbacks);
|
|
409
|
+
FunctionContextManager.processParameter(param, callbacks, 0);
|
|
410
410
|
|
|
411
411
|
const paramInfo = CodeGenState.currentParameters.get("name");
|
|
412
412
|
expect(paramInfo).toBeDefined();
|
|
@@ -46,13 +46,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
{
|
|
50
|
+
firstId: "Motor",
|
|
51
|
+
hasGlobal: true,
|
|
52
|
+
hasThis: false,
|
|
53
|
+
currentScope: null,
|
|
54
|
+
isStructParam: false,
|
|
55
|
+
isCppAccess: false,
|
|
56
|
+
},
|
|
54
57
|
deps,
|
|
55
|
-
false, // isCppAccess
|
|
56
58
|
);
|
|
57
59
|
|
|
58
60
|
expect(ctx.isCrossScope).toBe(true);
|
|
@@ -65,13 +67,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
65
67
|
});
|
|
66
68
|
|
|
67
69
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
{
|
|
71
|
+
firstId: "GPIO7",
|
|
72
|
+
hasGlobal: true,
|
|
73
|
+
hasThis: false,
|
|
74
|
+
currentScope: null,
|
|
75
|
+
isStructParam: false,
|
|
76
|
+
isCppAccess: false,
|
|
77
|
+
},
|
|
73
78
|
deps,
|
|
74
|
-
false,
|
|
75
79
|
);
|
|
76
80
|
|
|
77
81
|
expect(ctx.isCrossScope).toBe(true);
|
|
@@ -83,13 +87,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
83
87
|
});
|
|
84
88
|
|
|
85
89
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
{
|
|
91
|
+
firstId: "CONTROL_REG",
|
|
92
|
+
hasGlobal: false,
|
|
93
|
+
hasThis: true,
|
|
94
|
+
currentScope: "Motor",
|
|
95
|
+
isStructParam: false,
|
|
96
|
+
isCppAccess: false,
|
|
97
|
+
},
|
|
91
98
|
deps,
|
|
92
|
-
false,
|
|
93
99
|
);
|
|
94
100
|
|
|
95
101
|
expect(ctx.scopedRegName).toBe("Motor_CONTROL_REG");
|
|
@@ -100,13 +106,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
100
106
|
const deps = createMockDeps();
|
|
101
107
|
|
|
102
108
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
{
|
|
110
|
+
firstId: "CONTROL_REG",
|
|
111
|
+
hasGlobal: false,
|
|
112
|
+
hasThis: false,
|
|
113
|
+
currentScope: "Motor",
|
|
114
|
+
isStructParam: false,
|
|
115
|
+
isCppAccess: false,
|
|
116
|
+
},
|
|
108
117
|
deps,
|
|
109
|
-
false,
|
|
110
118
|
);
|
|
111
119
|
|
|
112
120
|
expect(ctx.scopedRegName).toBe(null);
|
|
@@ -117,13 +125,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
117
125
|
const deps = createMockDeps();
|
|
118
126
|
|
|
119
127
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
{
|
|
129
|
+
firstId: "point",
|
|
130
|
+
hasGlobal: false,
|
|
131
|
+
hasThis: false,
|
|
132
|
+
currentScope: null,
|
|
133
|
+
isStructParam: true,
|
|
134
|
+
isCppAccess: false,
|
|
135
|
+
},
|
|
125
136
|
deps,
|
|
126
|
-
false,
|
|
127
137
|
);
|
|
128
138
|
|
|
129
139
|
expect(ctx.isStructParam).toBe(true);
|
|
@@ -133,13 +143,15 @@ describe("MemberSeparatorResolver", () => {
|
|
|
133
143
|
const deps = createMockDeps();
|
|
134
144
|
|
|
135
145
|
const ctx = MemberSeparatorResolver.buildContext(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
{
|
|
147
|
+
firstId: "SeaDash",
|
|
148
|
+
hasGlobal: true,
|
|
149
|
+
hasThis: false,
|
|
150
|
+
currentScope: null,
|
|
151
|
+
isStructParam: false,
|
|
152
|
+
isCppAccess: true,
|
|
153
|
+
},
|
|
141
154
|
deps,
|
|
142
|
-
true, // isCppAccess
|
|
143
155
|
);
|
|
144
156
|
|
|
145
157
|
expect(ctx.isCppAccess).toBe(true);
|
|
@@ -61,6 +61,7 @@ function createDefaultASTDeps(overrides?: {
|
|
|
61
61
|
typeMap,
|
|
62
62
|
isModified: overrides?.isModified ?? false,
|
|
63
63
|
isPassByValue: overrides?.isPassByValue ?? false,
|
|
64
|
+
isCallbackCompatible: false,
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -422,5 +423,41 @@ describe("ParameterInputAdapter", () => {
|
|
|
422
423
|
expect(result.arrayDimensions).toEqual(["5", "33"]);
|
|
423
424
|
expect(result.isPassByReference).toBe(false);
|
|
424
425
|
});
|
|
426
|
+
|
|
427
|
+
it("passes through forceConst from deps (Issue #895)", () => {
|
|
428
|
+
const ctx = getParameterContext("void foo(Point area) {}");
|
|
429
|
+
const deps = {
|
|
430
|
+
...createDefaultASTDeps({ isKnownStruct: true }),
|
|
431
|
+
forceConst: true,
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const result = ParameterInputAdapter.fromAST(ctx, deps);
|
|
435
|
+
|
|
436
|
+
expect(result.forceConst).toBe(true);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("passes through forcePassByReference and forceConst together", () => {
|
|
440
|
+
const ctx = getParameterContext("void foo(Point area) {}");
|
|
441
|
+
const deps = {
|
|
442
|
+
...createDefaultASTDeps({ isKnownStruct: true }),
|
|
443
|
+
forcePassByReference: true,
|
|
444
|
+
forceConst: true,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const result = ParameterInputAdapter.fromAST(ctx, deps);
|
|
448
|
+
|
|
449
|
+
expect(result.forcePointerSyntax).toBe(true);
|
|
450
|
+
expect(result.forceConst).toBe(true);
|
|
451
|
+
expect(result.isPassByReference).toBe(true);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("forceConst defaults to undefined when not provided", () => {
|
|
455
|
+
const ctx = getParameterContext("void foo(Point area) {}");
|
|
456
|
+
const deps = createDefaultASTDeps({ isKnownStruct: true });
|
|
457
|
+
|
|
458
|
+
const result = ParameterInputAdapter.fromAST(ctx, deps);
|
|
459
|
+
|
|
460
|
+
expect(result.forceConst).toBeUndefined();
|
|
461
|
+
});
|
|
425
462
|
});
|
|
426
463
|
});
|
|
@@ -312,4 +312,67 @@ describe("ParameterSignatureBuilder", () => {
|
|
|
312
312
|
expect(result).toBe("const UnknownType data");
|
|
313
313
|
});
|
|
314
314
|
});
|
|
315
|
+
|
|
316
|
+
describe("callback-compatible parameters (Issue #895)", () => {
|
|
317
|
+
it("forceConst adds const from typedef signature", () => {
|
|
318
|
+
const input = createInput({
|
|
319
|
+
name: "area",
|
|
320
|
+
baseType: "rect_t",
|
|
321
|
+
mappedType: "rect_t",
|
|
322
|
+
isPassByReference: true,
|
|
323
|
+
forcePointerSyntax: true,
|
|
324
|
+
forceConst: true,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const result = ParameterSignatureBuilder.build(input, "&");
|
|
328
|
+
|
|
329
|
+
// Should use pointer (not reference) and add const from typedef
|
|
330
|
+
expect(result).toBe("const rect_t* area");
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("forcePointerSyntax uses pointer in C++ mode", () => {
|
|
334
|
+
const input = createInput({
|
|
335
|
+
name: "w",
|
|
336
|
+
baseType: "widget_t",
|
|
337
|
+
mappedType: "widget_t",
|
|
338
|
+
isPassByReference: true,
|
|
339
|
+
forcePointerSyntax: true,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const result = ParameterSignatureBuilder.build(input, "&");
|
|
343
|
+
|
|
344
|
+
// Should use pointer even in C++ mode (& suffix)
|
|
345
|
+
expect(result).toBe("widget_t* w");
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("forceConst without forcePointerSyntax still adds const", () => {
|
|
349
|
+
const input = createInput({
|
|
350
|
+
name: "data",
|
|
351
|
+
baseType: "Data",
|
|
352
|
+
mappedType: "Data",
|
|
353
|
+
isPassByReference: true,
|
|
354
|
+
forceConst: true,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const result = ParameterSignatureBuilder.build(input, "*");
|
|
358
|
+
|
|
359
|
+
expect(result).toBe("const Data* data");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("forceConst combines with forcePointerSyntax in C++ mode", () => {
|
|
363
|
+
const input = createInput({
|
|
364
|
+
name: "buf",
|
|
365
|
+
baseType: "uint8_t",
|
|
366
|
+
mappedType: "uint8_t",
|
|
367
|
+
isPassByReference: true,
|
|
368
|
+
forcePointerSyntax: true,
|
|
369
|
+
forceConst: false, // typedef doesn't have const
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const result = ParameterSignatureBuilder.build(input, "&");
|
|
373
|
+
|
|
374
|
+
// No const, but still uses pointer
|
|
375
|
+
expect(result).toBe("uint8_t* buf");
|
|
376
|
+
});
|
|
377
|
+
});
|
|
315
378
|
});
|