c-next 0.2.15 → 0.2.17
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/README.md +16 -0
- package/dist/index.js +1403 -427
- package/dist/index.js.map +3 -3
- package/grammar/CNext.g4 +4 -0
- package/package.json +5 -1
- package/src/transpiler/Transpiler.ts +90 -22
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
- package/src/transpiler/logic/symbols/SymbolTable.ts +54 -12
- package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
- package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +32 -8
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +71 -6
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
- package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/ScopeUtils.ts +19 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
/**
|
|
@@ -166,7 +166,8 @@ const _generateCFunctionArg = (
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Issue #948: Check if argument is an opaque scope variable (already a pointer)
|
|
169
|
-
|
|
169
|
+
// Issue #996: ...including an element of an opaque-handle array (arr[i])
|
|
170
|
+
const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariableAccess(argCode);
|
|
170
171
|
|
|
171
172
|
// Add & if argument needs address-of to match parameter type.
|
|
172
173
|
// Issue #322: struct types passed to pointer params.
|
|
@@ -284,50 +285,54 @@ const generateFunctionCall = (
|
|
|
284
285
|
// Get function signature once for all arguments
|
|
285
286
|
const sig = input.functionSignatures.get(funcExpr);
|
|
286
287
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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,
|
|
288
|
+
// Issue #992: Clear inDeclarationInit for function call arguments — struct
|
|
289
|
+
// initializers inside function args need compound literals, not plain designated initializers.
|
|
290
|
+
const args = CodeGenState.withoutDeclarationInit(() =>
|
|
291
|
+
argExprs
|
|
292
|
+
.map((e, idx) => {
|
|
293
|
+
// Get parameter type info from local signature or cross-file SymbolTable
|
|
294
|
+
const resolved = CallExprUtils.resolveTargetParam(
|
|
295
|
+
sig,
|
|
307
296
|
idx,
|
|
308
|
-
|
|
309
|
-
|
|
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,
|
|
297
|
+
funcExpr,
|
|
298
|
+
input.symbolTable,
|
|
324
299
|
);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
300
|
+
const targetParam = resolved.param;
|
|
301
|
+
|
|
302
|
+
// C/C++ function: use pass-by-value semantics
|
|
303
|
+
if (!isCNextFunc) {
|
|
304
|
+
return _generateCFunctionArg(e, targetParam, input, orchestrator);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// C-Next function: check if target parameter should be passed by value
|
|
308
|
+
if (
|
|
309
|
+
_shouldPassByValue(
|
|
310
|
+
funcExpr,
|
|
311
|
+
idx,
|
|
312
|
+
targetParam,
|
|
313
|
+
resolved.isCrossFile,
|
|
314
|
+
orchestrator,
|
|
315
|
+
)
|
|
316
|
+
) {
|
|
317
|
+
// Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
|
|
318
|
+
const argCode = CodeGenState.withExpectedType(
|
|
319
|
+
targetParam?.baseType,
|
|
320
|
+
() => orchestrator.generateExpression(e),
|
|
321
|
+
true, // suppressEnumResolution
|
|
322
|
+
);
|
|
323
|
+
return wrapWithCppEnumCast(
|
|
324
|
+
argCode,
|
|
325
|
+
e,
|
|
326
|
+
targetParam?.baseType,
|
|
327
|
+
orchestrator,
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Target parameter is pass-by-reference: use & logic
|
|
332
|
+
return orchestrator.generateFunctionArg(e, targetParam?.baseType);
|
|
333
|
+
})
|
|
334
|
+
.join(", "),
|
|
335
|
+
);
|
|
331
336
|
|
|
332
337
|
return { code: `${funcExpr}(${args})`, effects };
|
|
333
338
|
};
|
|
@@ -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 =
|
|
79
|
-
|
|
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
|
};
|
package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts
CHANGED
|
@@ -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
|
});
|
package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts
CHANGED
|
@@ -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");
|
|
@@ -60,6 +60,13 @@ interface IFromASTDeps {
|
|
|
60
60
|
* When the C typedef has `const T*`, this preserves const on the generated param.
|
|
61
61
|
*/
|
|
62
62
|
forceConst?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Issue #995: Check if a type is an opaque handle (incomplete struct typedef).
|
|
66
|
+
* Opaque handles should not get auto-const because they must be passed to
|
|
67
|
+
* C APIs that expect non-const pointers.
|
|
68
|
+
*/
|
|
69
|
+
isOpaqueType?: (typeName: string) => boolean;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
/**
|
|
@@ -142,6 +149,8 @@ class ParameterInputAdapter {
|
|
|
142
149
|
const isKnownPrimitive = !!deps.typeMap[typeName];
|
|
143
150
|
// Issue #958: C-header typedef struct types need pointer semantics
|
|
144
151
|
const isTypedefStruct = deps.isTypedefStructType(typeName);
|
|
152
|
+
// Issue #995: Detect opaque handles — rule applied in ParameterSignatureBuilder
|
|
153
|
+
const isOpaque = deps.isOpaqueType?.(typeName) ?? false;
|
|
145
154
|
// Issue #895: Don't add auto-const for callback-compatible functions
|
|
146
155
|
// because it would change the signature and break typedef compatibility
|
|
147
156
|
const isAutoConst =
|
|
@@ -171,6 +180,8 @@ class ParameterInputAdapter {
|
|
|
171
180
|
deps.forcePassByReference || isTypedefStruct || undefined,
|
|
172
181
|
// Issue #895: Preserve const from callback typedef signature
|
|
173
182
|
forceConst: deps.forceConst,
|
|
183
|
+
// Issue #995: Pass through opaque handle detection — rule applied in builder
|
|
184
|
+
isOpaqueHandle: isOpaque || undefined,
|
|
174
185
|
};
|
|
175
186
|
}
|
|
176
187
|
|
|
@@ -236,6 +247,8 @@ class ParameterInputAdapter {
|
|
|
236
247
|
isPassByReference: isCallbackPointer ? true : !deps.isPassByValue,
|
|
237
248
|
forcePointerSyntax: isCallbackPointer || undefined,
|
|
238
249
|
forceConst: param.isCallbackConst || undefined,
|
|
250
|
+
// Issue #995: Pass through opaque handle detection — rule applied in builder
|
|
251
|
+
isOpaqueHandle: param.isOpaqueHandle || undefined,
|
|
239
252
|
};
|
|
240
253
|
}
|
|
241
254
|
|
|
@@ -296,14 +309,15 @@ class ParameterInputAdapter {
|
|
|
296
309
|
}
|
|
297
310
|
}
|
|
298
311
|
|
|
299
|
-
|
|
300
|
-
|
|
312
|
+
// ADR-006: Arrays are pass-by-reference and mutable by default.
|
|
313
|
+
// Never apply auto-const to arrays - only explicit const from source code.
|
|
314
|
+
// Auto-const would break compatibility with C APIs expecting mutable pointers.
|
|
301
315
|
return {
|
|
302
316
|
name,
|
|
303
317
|
baseType: typeName,
|
|
304
318
|
mappedType,
|
|
305
319
|
isConst,
|
|
306
|
-
isAutoConst,
|
|
320
|
+
isAutoConst: false,
|
|
307
321
|
isArray: true,
|
|
308
322
|
arrayDimensions: dims,
|
|
309
323
|
isCallback: false,
|
|
@@ -47,6 +47,11 @@ class ParameterSignatureBuilder {
|
|
|
47
47
|
return this._buildStringParam(param);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// Issue #995: Opaque handles are always pass-by-reference with pointer syntax
|
|
51
|
+
if (param.isOpaqueHandle) {
|
|
52
|
+
return this._buildRefParam(param, refSuffix);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
// Known struct or known primitive: pass by reference
|
|
51
56
|
if (param.isPassByReference) {
|
|
52
57
|
return this._buildRefParam(param, refSuffix);
|
|
@@ -103,18 +108,21 @@ class ParameterSignatureBuilder {
|
|
|
103
108
|
/**
|
|
104
109
|
* Build pass-by-reference parameter signature.
|
|
105
110
|
* C mode: const Point* p
|
|
106
|
-
* C++ mode: const Point& p (unless forcePointerSyntax)
|
|
111
|
+
* C++ mode: const Point& p (unless forcePointerSyntax or isOpaqueHandle)
|
|
107
112
|
*
|
|
108
113
|
* Issue #895: When forcePointerSyntax is set, always use pointer syntax
|
|
109
114
|
* because C callback typedefs expect pointers, not C++ references.
|
|
115
|
+
* Issue #995: Opaque handles must use pointer syntax because C APIs
|
|
116
|
+
* expect pointers to incomplete struct types (e.g., widget_t*).
|
|
110
117
|
*/
|
|
111
118
|
private static _buildRefParam(
|
|
112
119
|
param: IParameterInput,
|
|
113
120
|
refSuffix: string,
|
|
114
121
|
): string {
|
|
115
122
|
const constPrefix = this._getConstPrefix(param);
|
|
116
|
-
// Issue #895: Override refSuffix for callback-compatible
|
|
117
|
-
const actualSuffix =
|
|
123
|
+
// Issue #895/#995: Override refSuffix for callback-compatible or opaque handle params
|
|
124
|
+
const actualSuffix =
|
|
125
|
+
param.forcePointerSyntax || param.isOpaqueHandle ? "*" : refSuffix;
|
|
118
126
|
return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
|
|
119
127
|
}
|
|
120
128
|
|
|
@@ -130,10 +138,15 @@ class ParameterSignatureBuilder {
|
|
|
130
138
|
* Get const prefix combining explicit const, auto-const, and forced const.
|
|
131
139
|
* Priority: forceConst > isConst > isAutoConst
|
|
132
140
|
* Issue #895: forceConst preserves const from callback typedef signature.
|
|
141
|
+
* Issue #995: Opaque handles suppress auto-const (they must be passed to
|
|
142
|
+
* C APIs that expect non-const pointers).
|
|
133
143
|
*/
|
|
134
144
|
private static _getConstPrefix(param: IParameterInput): string {
|
|
145
|
+
// Issue #995: Opaque handles never get auto-const — they're passed to C APIs
|
|
146
|
+
// expecting mutable pointers (e.g., LVGL's lv_obj_t)
|
|
147
|
+
const effectiveAutoConst = param.isOpaqueHandle ? false : param.isAutoConst;
|
|
135
148
|
// Any source of const results in "const " prefix
|
|
136
|
-
if (param.forceConst || param.isConst ||
|
|
149
|
+
if (param.forceConst || param.isConst || effectiveAutoConst) {
|
|
137
150
|
return "const ";
|
|
138
151
|
}
|
|
139
152
|
return "";
|