c-next 0.2.8 → 0.2.10
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 +1019 -267
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +101 -8
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +170 -0
- package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +246 -0
- package/src/transpiler/logic/IncludeExtractor.ts +12 -6
- package/src/transpiler/logic/__tests__/IncludeExtractor.test.ts +24 -0
- package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +12 -1
- package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +172 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +84 -0
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +43 -0
- package/src/transpiler/logic/symbols/__tests__/TransitiveEnumCollector.test.ts +1 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +98 -1
- package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +23 -5
- package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +69 -2
- package/src/transpiler/logic/symbols/c/index.ts +85 -30
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +18 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +90 -0
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +40 -39
- package/src/transpiler/output/codegen/CodeGenerator.ts +55 -25
- package/src/transpiler/output/codegen/TypeResolver.ts +14 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +35 -30
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +7 -7
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +1 -0
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +27 -4
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +6 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +73 -0
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +4 -0
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +92 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +5 -2
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +1 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +14 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +25 -3
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +49 -0
- package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +17 -5
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +26 -6
- package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +31 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +72 -13
- package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +25 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +2 -1
- package/src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts +2 -17
- package/src/transpiler/output/codegen/generators/support/IncludeGenerator.ts +19 -7
- package/src/transpiler/output/codegen/generators/support/__tests__/IncludeGenerator.test.ts +68 -0
- package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +14 -2
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +3 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +3 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +56 -8
- package/src/transpiler/output/codegen/helpers/BitRangeHelper.ts +19 -3
- package/src/transpiler/output/codegen/helpers/NarrowingCastHelper.ts +191 -0
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +35 -5
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +131 -1
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +61 -2
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/BitRangeHelper.test.ts +53 -2
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/NarrowingCastHelper.test.ts +159 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +1 -0
- package/src/transpiler/output/codegen/types/COMPOUND_TO_BINARY.ts +21 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +2 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +14 -16
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +25 -0
- package/src/transpiler/state/CodeGenState.ts +89 -0
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +53 -0
- package/src/transpiler/state/__tests__/TranspilerState.test.ts +1 -0
- package/src/transpiler/types/ICachedFileEntry.ts +2 -0
- package/src/transpiler/types/ICodeGenSymbols.ts +8 -0
- package/src/utils/BitUtils.ts +33 -4
- package/src/utils/cache/CacheManager.ts +9 -1
- package/src/utils/cache/__tests__/CacheManager.test.ts +1 -1
|
@@ -466,6 +466,9 @@ function createMockOrchestrator(
|
|
|
466
466
|
generateCallbackTypedef: vi.fn(() => null),
|
|
467
467
|
isConstValue: vi.fn(() => true),
|
|
468
468
|
tryEvaluateConstant: vi.fn(() => undefined),
|
|
469
|
+
// Issue #948: Opaque type helpers
|
|
470
|
+
isOpaqueType: vi.fn(() => false),
|
|
471
|
+
markOpaqueScopeVariable: vi.fn(),
|
|
469
472
|
...overrides,
|
|
470
473
|
} as unknown as IOrchestrator;
|
|
471
474
|
}
|
|
@@ -721,6 +724,52 @@ describe("ScopeGenerator", () => {
|
|
|
721
724
|
expect(result.code).toContain("static uint32_t Device_status = 0;");
|
|
722
725
|
expect(orchestrator.getZeroInitializer).toHaveBeenCalled();
|
|
723
726
|
});
|
|
727
|
+
|
|
728
|
+
it("generates opaque type variable as pointer with NULL (Issue #948)", () => {
|
|
729
|
+
const varDecl = createMockVariableDecl({
|
|
730
|
+
name: "widget",
|
|
731
|
+
type: "widget_t",
|
|
732
|
+
});
|
|
733
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
734
|
+
const ctx = createMockScopeContext("GUI", [member]);
|
|
735
|
+
const input = createMockInput();
|
|
736
|
+
const state = createMockState();
|
|
737
|
+
const orchestrator = createMockOrchestrator({
|
|
738
|
+
...createMockOrchestrator(),
|
|
739
|
+
isOpaqueType: vi.fn(() => true),
|
|
740
|
+
markOpaqueScopeVariable: vi.fn(),
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
744
|
+
|
|
745
|
+
// Should generate as pointer with NULL initialization
|
|
746
|
+
expect(result.code).toContain("static widget_t* GUI_widget = NULL;");
|
|
747
|
+
// Should call markOpaqueScopeVariable
|
|
748
|
+
expect(orchestrator.markOpaqueScopeVariable).toHaveBeenCalledWith(
|
|
749
|
+
"GUI_widget",
|
|
750
|
+
);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it("does not generate pointer for non-opaque struct types", () => {
|
|
754
|
+
const varDecl = createMockVariableDecl({
|
|
755
|
+
name: "point",
|
|
756
|
+
type: "Point",
|
|
757
|
+
});
|
|
758
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
759
|
+
const ctx = createMockScopeContext("Canvas", [member]);
|
|
760
|
+
const input = createMockInput();
|
|
761
|
+
const state = createMockState();
|
|
762
|
+
const orchestrator = createMockOrchestrator({
|
|
763
|
+
...createMockOrchestrator(),
|
|
764
|
+
isOpaqueType: vi.fn(() => false),
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
768
|
+
|
|
769
|
+
// Should generate as value with {0} initialization
|
|
770
|
+
expect(result.code).toContain("static Point Canvas_point = 0;");
|
|
771
|
+
expect(result.code).not.toContain("Point*");
|
|
772
|
+
});
|
|
724
773
|
});
|
|
725
774
|
|
|
726
775
|
// ========================================================================
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import IGeneratorOutput from "../IGeneratorOutput";
|
|
15
15
|
import TTypeInfo from "../../types/TTypeInfo";
|
|
16
|
+
import CodeGenState from "../../../../state/CodeGenState.js";
|
|
17
|
+
import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Generate code for .capacity property access.
|
|
@@ -56,22 +58,32 @@ interface BitmapFieldInfo {
|
|
|
56
58
|
*
|
|
57
59
|
* Single bit fields generate: ((value >> offset) & 1)
|
|
58
60
|
* Multi-bit fields generate: ((value >> offset) & mask)
|
|
61
|
+
*
|
|
62
|
+
* MISRA C:2012 Rule 10.3: When target type is known (via CodeGenState.expectedType),
|
|
63
|
+
* wraps expression with appropriate cast. Bool targets use != 0U comparison.
|
|
59
64
|
*/
|
|
60
65
|
const generateBitmapFieldAccess = (
|
|
61
66
|
result: string,
|
|
62
67
|
fieldInfo: BitmapFieldInfo,
|
|
63
68
|
): IGeneratorOutput => {
|
|
69
|
+
let expr: string;
|
|
64
70
|
if (fieldInfo.width === 1) {
|
|
65
71
|
// Single bit: ((value >> offset) & 1)
|
|
66
|
-
|
|
72
|
+
expr = `((${result} >> ${fieldInfo.offset}) & 1)`;
|
|
67
73
|
} else {
|
|
68
74
|
// Multi-bit: ((value >> offset) & mask)
|
|
69
75
|
const mask = (1 << fieldInfo.width) - 1;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
expr = `((${result} >> ${fieldInfo.offset}) & 0x${mask.toString(16).toUpperCase()})`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// MISRA 10.3: Add narrowing cast if target type is known
|
|
80
|
+
const targetType = CodeGenState.expectedType;
|
|
81
|
+
if (targetType) {
|
|
82
|
+
// Bitmap operations on small types produce int in C
|
|
83
|
+
expr = NarrowingCastHelper.wrap(expr, "int", targetType);
|
|
74
84
|
}
|
|
85
|
+
|
|
86
|
+
return { code: expr, effects: [] };
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
// Export all generators
|
|
@@ -107,6 +107,7 @@ const _parameterExpectsAddressOf = (
|
|
|
107
107
|
/**
|
|
108
108
|
* Generate argument code for a C/C++ function call.
|
|
109
109
|
* Handles automatic address-of (&) for struct arguments passed to pointer params.
|
|
110
|
+
* Issue #872: Sets expectedType for MISRA 7.2 U suffix on unsigned literals.
|
|
110
111
|
*/
|
|
111
112
|
const _generateCFunctionArg = (
|
|
112
113
|
e: ExpressionContext,
|
|
@@ -134,7 +135,13 @@ const _generateCFunctionArg = (
|
|
|
134
135
|
);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
|
|
138
|
+
// Issue #872: Set expectedType for MISRA 7.2 compliance, but suppress bare enum resolution
|
|
139
|
+
// (bare enums in function args was never allowed - changing that requires ADR approval)
|
|
140
|
+
const argCode = CodeGenState.withExpectedType(
|
|
141
|
+
targetParam?.baseType,
|
|
142
|
+
() => orchestrator.generateExpression(e),
|
|
143
|
+
true, // suppressEnumResolution
|
|
144
|
+
);
|
|
138
145
|
|
|
139
146
|
// Issue #322: Check if parameter expects a pointer and argument is a struct
|
|
140
147
|
if (!targetParam?.baseType?.endsWith("*")) {
|
|
@@ -158,24 +165,32 @@ const _generateCFunctionArg = (
|
|
|
158
165
|
isPointerVariable = true;
|
|
159
166
|
}
|
|
160
167
|
|
|
168
|
+
// Issue #948: Check if argument is an opaque scope variable (already a pointer)
|
|
169
|
+
const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariable(argCode);
|
|
170
|
+
|
|
161
171
|
// Add & if argument needs address-of to match parameter type.
|
|
162
172
|
// Issue #322: struct types passed to pointer params.
|
|
163
173
|
// Issue #832: typedef'd pointer types (e.g., handle_t passed to handle_t*).
|
|
164
174
|
// Issue #895 Bug B: Skip address-of for variables that are already pointers
|
|
175
|
+
// Issue #948: Skip address-of for opaque scope variables (already pointers)
|
|
165
176
|
const needsAddressOf =
|
|
166
177
|
argType &&
|
|
167
178
|
!argType.endsWith("*") &&
|
|
168
179
|
!argCode.startsWith("&") &&
|
|
169
180
|
!targetParam.isArray &&
|
|
170
181
|
!isPointerVariable &&
|
|
182
|
+
!isOpaqueScopeVar &&
|
|
171
183
|
(orchestrator.isStructType(argType) ||
|
|
172
184
|
_parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
|
|
173
185
|
|
|
174
|
-
|
|
175
|
-
argCode = `&${argCode}`;
|
|
176
|
-
}
|
|
186
|
+
const finalArgCode = needsAddressOf ? `&${argCode}` : argCode;
|
|
177
187
|
|
|
178
|
-
return wrapWithCppEnumCast(
|
|
188
|
+
return wrapWithCppEnumCast(
|
|
189
|
+
finalArgCode,
|
|
190
|
+
e,
|
|
191
|
+
targetParam?.baseType,
|
|
192
|
+
orchestrator,
|
|
193
|
+
);
|
|
179
194
|
};
|
|
180
195
|
|
|
181
196
|
/**
|
|
@@ -295,7 +310,12 @@ const generateFunctionCall = (
|
|
|
295
310
|
orchestrator,
|
|
296
311
|
)
|
|
297
312
|
) {
|
|
298
|
-
|
|
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
|
+
);
|
|
299
319
|
return wrapWithCppEnumCast(
|
|
300
320
|
argCode,
|
|
301
321
|
e,
|
|
@@ -14,6 +14,8 @@ import TGeneratorEffect from "../TGeneratorEffect";
|
|
|
14
14
|
import IGeneratorInput from "../IGeneratorInput";
|
|
15
15
|
import IGeneratorState from "../IGeneratorState";
|
|
16
16
|
import IOrchestrator from "../IOrchestrator";
|
|
17
|
+
import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
|
|
18
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Unsigned type patterns for MISRA Rule 7.2 compliance.
|
|
@@ -27,8 +29,18 @@ const UNSIGNED_TYPES = new Set([
|
|
|
27
29
|
"uint8_t",
|
|
28
30
|
"uint16_t",
|
|
29
31
|
"uint32_t",
|
|
32
|
+
"size_t", // Array indices (MISRA 7.2)
|
|
30
33
|
]);
|
|
31
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Resolve typedef aliases to their underlying type.
|
|
37
|
+
* For C typedef'd types (e.g., "byte_t" -> "uint8_t"), look up the symbol table.
|
|
38
|
+
*/
|
|
39
|
+
function resolveTypedef(typeName: string): string {
|
|
40
|
+
const underlyingType = CodeGenState.getTypedefType(typeName);
|
|
41
|
+
return underlyingType ?? typeName;
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
/**
|
|
33
45
|
* Check if a literal is a numeric integer (decimal, hex, octal, or binary).
|
|
34
46
|
* Excludes strings, floats, and booleans.
|
|
@@ -83,6 +95,21 @@ const generateLiteral = (
|
|
|
83
95
|
return { code: literalText, effects };
|
|
84
96
|
}
|
|
85
97
|
|
|
98
|
+
// MISRA 10.3: Character literals have type int in C
|
|
99
|
+
// When assigning to narrower types (u8, i8, u16, i16), add explicit cast
|
|
100
|
+
if (literalText.startsWith("'")) {
|
|
101
|
+
const expectedType = state?.expectedType;
|
|
102
|
+
if (expectedType) {
|
|
103
|
+
const wrappedCode = NarrowingCastHelper.wrap(
|
|
104
|
+
literalText,
|
|
105
|
+
"int",
|
|
106
|
+
expectedType,
|
|
107
|
+
);
|
|
108
|
+
return { code: wrappedCode, effects };
|
|
109
|
+
}
|
|
110
|
+
return { code: literalText, effects };
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
// ADR-024: Transform C-Next float suffixes to standard C syntax
|
|
87
114
|
// 3.14f32 -> 3.14f (C float)
|
|
88
115
|
// 3.14f64 -> 3.14 (C double, no suffix needed)
|
|
@@ -120,9 +147,11 @@ const generateLiteral = (
|
|
|
120
147
|
isNumericIntegerLiteral(literalText) &&
|
|
121
148
|
!hasUnsignedSuffix(literalText)
|
|
122
149
|
) {
|
|
123
|
-
|
|
150
|
+
// Resolve typedef aliases (e.g., "byte_t" -> "uint8_t")
|
|
151
|
+
const resolvedType = resolveTypedef(expectedType);
|
|
152
|
+
if (UNSIGNED_64_TYPES.has(resolvedType)) {
|
|
124
153
|
literalText = literalText + "ULL";
|
|
125
|
-
} else if (UNSIGNED_TYPES.has(
|
|
154
|
+
} else if (UNSIGNED_TYPES.has(resolvedType)) {
|
|
126
155
|
literalText = literalText + "U";
|
|
127
156
|
}
|
|
128
157
|
}
|
|
@@ -22,6 +22,7 @@ import generateFunctionCall from "./CallExprGenerator";
|
|
|
22
22
|
import memberAccessChain from "../../memberAccessChain";
|
|
23
23
|
import MemberAccessValidator from "../../helpers/MemberAccessValidator";
|
|
24
24
|
import BitmapAccessHelper from "./BitmapAccessHelper";
|
|
25
|
+
import NarrowingCastHelper from "../../helpers/NarrowingCastHelper";
|
|
25
26
|
import TypeCheckUtils from "../../../../../utils/TypeCheckUtils";
|
|
26
27
|
import SubscriptClassifier from "../../subscript/SubscriptClassifier";
|
|
27
28
|
import TYPE_WIDTH from "../../types/TYPE_WIDTH";
|
|
@@ -1593,7 +1594,11 @@ const handleSingleSubscript = (
|
|
|
1593
1594
|
orchestrator: IOrchestrator,
|
|
1594
1595
|
output: SubscriptAccessResult,
|
|
1595
1596
|
): SubscriptAccessResult => {
|
|
1596
|
-
|
|
1597
|
+
// Set expectedType to size_t (unsigned) for array indices per MISRA 7.2
|
|
1598
|
+
// This ensures index literals get U suffix regardless of element type
|
|
1599
|
+
const index = CodeGenState.withExpectedType("size_t", () =>
|
|
1600
|
+
orchestrator.generateExpression(expr),
|
|
1601
|
+
);
|
|
1597
1602
|
|
|
1598
1603
|
// Check if result is a register member with bitmap type (throws)
|
|
1599
1604
|
validateNotBitmapMember(ctx, input);
|
|
@@ -1603,7 +1608,17 @@ const handleSingleSubscript = (
|
|
|
1603
1608
|
|
|
1604
1609
|
// Register access: bit extraction
|
|
1605
1610
|
if (isRegisterAccess) {
|
|
1606
|
-
|
|
1611
|
+
// Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
|
|
1612
|
+
let expr =
|
|
1613
|
+
index === "0" || index === "0U"
|
|
1614
|
+
? `((${ctx.result}) & 1)`
|
|
1615
|
+
: `((${ctx.result} >> ${index}) & 1)`;
|
|
1616
|
+
// MISRA 10.3: Add narrowing cast if expected type is narrower than int
|
|
1617
|
+
const targetType = CodeGenState.expectedType;
|
|
1618
|
+
if (targetType) {
|
|
1619
|
+
expr = NarrowingCastHelper.wrap(expr, "int", targetType);
|
|
1620
|
+
}
|
|
1621
|
+
output.result = expr;
|
|
1607
1622
|
return output;
|
|
1608
1623
|
}
|
|
1609
1624
|
|
|
@@ -1624,7 +1639,17 @@ const handleSingleSubscript = (
|
|
|
1624
1639
|
const isPrimitiveIntMember =
|
|
1625
1640
|
ctx.currentStructType && TypeCheckUtils.isInteger(ctx.currentStructType);
|
|
1626
1641
|
if (isPrimitiveIntMember) {
|
|
1627
|
-
|
|
1642
|
+
// Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
|
|
1643
|
+
let expr =
|
|
1644
|
+
index === "0" || index === "0U"
|
|
1645
|
+
? `((${ctx.result}) & 1)`
|
|
1646
|
+
: `((${ctx.result} >> ${index}) & 1)`;
|
|
1647
|
+
// MISRA 10.3: Add narrowing cast if expected type is narrower than int
|
|
1648
|
+
const targetType = CodeGenState.expectedType;
|
|
1649
|
+
if (targetType) {
|
|
1650
|
+
expr = NarrowingCastHelper.wrap(expr, "int", targetType);
|
|
1651
|
+
}
|
|
1652
|
+
output.result = expr;
|
|
1628
1653
|
output.currentStructType = undefined;
|
|
1629
1654
|
return output;
|
|
1630
1655
|
}
|
|
@@ -1742,10 +1767,21 @@ const handleDefaultSubscript = (
|
|
|
1742
1767
|
isRegisterAccess: false,
|
|
1743
1768
|
});
|
|
1744
1769
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1770
|
+
if (subscriptKind === "bit_single") {
|
|
1771
|
+
// Skip shift when index is 0 (either "0" or "0U" with MISRA suffix)
|
|
1772
|
+
let expr =
|
|
1773
|
+
index === "0" || index === "0U"
|
|
1774
|
+
? `((${ctx.result}) & 1)`
|
|
1775
|
+
: `((${ctx.result} >> ${index}) & 1)`;
|
|
1776
|
+
// MISRA 10.3: Add narrowing cast if expected type is narrower than int
|
|
1777
|
+
const targetType = CodeGenState.expectedType;
|
|
1778
|
+
if (targetType) {
|
|
1779
|
+
expr = NarrowingCastHelper.wrap(expr, "int", targetType);
|
|
1780
|
+
}
|
|
1781
|
+
output.result = expr;
|
|
1782
|
+
} else {
|
|
1783
|
+
output.result = `${ctx.result}[${index}]`;
|
|
1784
|
+
}
|
|
1749
1785
|
|
|
1750
1786
|
return output;
|
|
1751
1787
|
};
|
|
@@ -1762,8 +1798,12 @@ const handleBitRangeSubscript = (
|
|
|
1762
1798
|
effects: TGeneratorEffect[],
|
|
1763
1799
|
output: SubscriptAccessResult,
|
|
1764
1800
|
): SubscriptAccessResult => {
|
|
1765
|
-
|
|
1766
|
-
|
|
1801
|
+
// Set expectedType to size_t (unsigned) for bit indices per MISRA 7.2
|
|
1802
|
+
// Bit positions are inherently unsigned values
|
|
1803
|
+
const [start, width] = CodeGenState.withExpectedType("size_t", () => [
|
|
1804
|
+
orchestrator.generateExpression(exprs[0]),
|
|
1805
|
+
orchestrator.generateExpression(exprs[1]),
|
|
1806
|
+
]);
|
|
1767
1807
|
|
|
1768
1808
|
const isFloatType =
|
|
1769
1809
|
ctx.primaryTypeInfo?.baseType === "f32" ||
|
|
@@ -1784,10 +1824,28 @@ const handleBitRangeSubscript = (
|
|
|
1784
1824
|
);
|
|
1785
1825
|
} else {
|
|
1786
1826
|
const mask = orchestrator.generateBitMask(width);
|
|
1787
|
-
|
|
1788
|
-
|
|
1827
|
+
// Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
|
|
1828
|
+
let expr: string;
|
|
1829
|
+
if (start === "0" || start === "0U") {
|
|
1830
|
+
expr = `((${ctx.result}) & ${mask})`;
|
|
1831
|
+
} else {
|
|
1832
|
+
expr = `((${ctx.result} >> ${start}) & ${mask})`;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
// MISRA 10.3: Add narrowing cast if expected type is known
|
|
1836
|
+
// Bit operations promote to int, so wrap with cast when assigning to narrower types
|
|
1837
|
+
const targetType = CodeGenState.expectedType;
|
|
1838
|
+
if (targetType && ctx.primaryTypeInfo?.baseType) {
|
|
1839
|
+
const promotedSourceType = NarrowingCastHelper.getPromotedType(
|
|
1840
|
+
ctx.primaryTypeInfo.baseType,
|
|
1841
|
+
);
|
|
1842
|
+
output.result = NarrowingCastHelper.wrap(
|
|
1843
|
+
expr,
|
|
1844
|
+
promotedSourceType,
|
|
1845
|
+
targetType,
|
|
1846
|
+
);
|
|
1789
1847
|
} else {
|
|
1790
|
-
output.result =
|
|
1848
|
+
output.result = expr;
|
|
1791
1849
|
}
|
|
1792
1850
|
}
|
|
1793
1851
|
|
|
@@ -1854,7 +1912,8 @@ const handleFloatBitRange = (
|
|
|
1854
1912
|
}
|
|
1855
1913
|
|
|
1856
1914
|
// Return just the bit read expression using union member .u
|
|
1857
|
-
|
|
1915
|
+
// Skip shift when start is 0 (either "0" or "0U" with MISRA suffix)
|
|
1916
|
+
if (ctx.start === "0" || ctx.start === "0U") {
|
|
1858
1917
|
return `(${shadowName}.u & ${mask})`;
|
|
1859
1918
|
}
|
|
1860
1919
|
return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { UnaryExpressionContext } from "../../../../logic/parser/grammar/CNextParser";
|
|
10
10
|
import IGeneratorOutput from "../IGeneratorOutput";
|
|
11
|
+
import TGeneratorEffect from "../TGeneratorEffect";
|
|
11
12
|
import IGeneratorInput from "../IGeneratorInput";
|
|
12
13
|
import IGeneratorState from "../IGeneratorState";
|
|
13
14
|
import IOrchestrator from "../IOrchestrator";
|
|
@@ -15,6 +16,14 @@ import TypeResolver from "../../TypeResolver";
|
|
|
15
16
|
import TYPE_MAP from "../../types/TYPE_MAP";
|
|
16
17
|
import CppModeHelper from "../../helpers/CppModeHelper";
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Problematic negative literals that overflow their signed types in C.
|
|
21
|
+
* -2147483648 is parsed as -(2147483648) where 2147483648 > INT32_MAX.
|
|
22
|
+
* These need to be rewritten to avoid the overflow issue.
|
|
23
|
+
*/
|
|
24
|
+
const INT32_MIN_LITERAL = "2147483648";
|
|
25
|
+
const INT64_MIN_LITERAL = "9223372036854775808";
|
|
26
|
+
|
|
18
27
|
/**
|
|
19
28
|
* Generate C code for a unary expression.
|
|
20
29
|
*
|
|
@@ -45,7 +54,22 @@ const generateUnaryExpr = (
|
|
|
45
54
|
|
|
46
55
|
// Determine the operator and generate output
|
|
47
56
|
if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
|
|
48
|
-
if (text.startsWith("-"))
|
|
57
|
+
if (text.startsWith("-")) {
|
|
58
|
+
// MISRA 10.3: Handle problematic negative literals that overflow in C
|
|
59
|
+
// -2147483648 is parsed as -(2147483648) where 2147483648 > INT32_MAX
|
|
60
|
+
// Must use INT32_MIN or INT64_MIN to avoid the overflow
|
|
61
|
+
// Cast is needed because INT32_MIN has type 'int', not 'int32_t'
|
|
62
|
+
const effects: TGeneratorEffect[] = [];
|
|
63
|
+
if (inner === INT32_MIN_LITERAL) {
|
|
64
|
+
effects.push({ type: "include", header: "limits" });
|
|
65
|
+
return { code: "(int32_t)INT32_MIN", effects };
|
|
66
|
+
}
|
|
67
|
+
if (inner === INT64_MIN_LITERAL || inner === INT64_MIN_LITERAL + "LL") {
|
|
68
|
+
effects.push({ type: "include", header: "limits" });
|
|
69
|
+
return { code: "(int64_t)INT64_MIN", effects };
|
|
70
|
+
}
|
|
71
|
+
return { code: `-${inner}`, effects };
|
|
72
|
+
}
|
|
49
73
|
if (text.startsWith("~")) {
|
|
50
74
|
const innerType = TypeResolver.getUnaryExpressionType(
|
|
51
75
|
node.unaryExpression()!,
|
|
@@ -1179,7 +1179,8 @@ describe("PostfixExpressionGenerator", () => {
|
|
|
1179
1179
|
});
|
|
1180
1180
|
|
|
1181
1181
|
const result = generatePostfixExpression(ctx, input, state, orchestrator);
|
|
1182
|
-
|
|
1182
|
+
// Optimization: no shift when index is 0
|
|
1183
|
+
expect(result.code).toBe("((GPIO) & 1)");
|
|
1183
1184
|
});
|
|
1184
1185
|
|
|
1185
1186
|
it("throws for bracket indexing on bitmap type", () => {
|
|
@@ -13,6 +13,7 @@ import IGeneratorOutput from "../IGeneratorOutput";
|
|
|
13
13
|
import TGeneratorEffect from "../TGeneratorEffect";
|
|
14
14
|
import ITargetCapabilities from "../../types/ITargetCapabilities";
|
|
15
15
|
import TYPE_WIDTH from "../../types/TYPE_WIDTH";
|
|
16
|
+
import COMPOUND_TO_BINARY from "../../types/COMPOUND_TO_BINARY";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Maps C-Next types to C types (for atomic operations)
|
|
@@ -54,22 +55,6 @@ const STREX_MAP: Record<string, string> = {
|
|
|
54
55
|
i32: "__STREXW",
|
|
55
56
|
};
|
|
56
57
|
|
|
57
|
-
/**
|
|
58
|
-
* Map compound operators to simple operators
|
|
59
|
-
*/
|
|
60
|
-
const SIMPLE_OP_MAP: Record<string, string> = {
|
|
61
|
-
"+=": "+",
|
|
62
|
-
"-=": "-",
|
|
63
|
-
"*=": "*",
|
|
64
|
-
"/=": "/",
|
|
65
|
-
"%=": "%",
|
|
66
|
-
"&=": "&",
|
|
67
|
-
"|=": "|",
|
|
68
|
-
"^=": "^",
|
|
69
|
-
"<<=": "<<",
|
|
70
|
-
">>=": ">>",
|
|
71
|
-
};
|
|
72
|
-
|
|
73
58
|
/**
|
|
74
59
|
* Map compound operators to clamp helper operation names
|
|
75
60
|
*/
|
|
@@ -106,7 +91,7 @@ function generateInnerAtomicOp(
|
|
|
106
91
|
typeInfo: TTypeInfo,
|
|
107
92
|
): IGeneratorOutput {
|
|
108
93
|
const effects: TGeneratorEffect[] = [];
|
|
109
|
-
const simpleOp =
|
|
94
|
+
const simpleOp = COMPOUND_TO_BINARY[cOp] || "+";
|
|
110
95
|
|
|
111
96
|
// Handle clamp behavior for arithmetic operations (integers only)
|
|
112
97
|
const helperOp = getClampHelperOp(cOp, typeInfo);
|
|
@@ -13,6 +13,7 @@ interface IIncludeTransformOptions {
|
|
|
13
13
|
sourcePath: string | null;
|
|
14
14
|
includeDirs?: string[];
|
|
15
15
|
inputs?: string[];
|
|
16
|
+
cppMode?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -24,6 +25,7 @@ const resolveAngleIncludePath = (
|
|
|
24
25
|
sourcePath: string,
|
|
25
26
|
includeDirs: string[],
|
|
26
27
|
inputs: string[],
|
|
28
|
+
cppMode: boolean,
|
|
27
29
|
): string | null => {
|
|
28
30
|
if (inputs.length === 0) {
|
|
29
31
|
return null;
|
|
@@ -42,7 +44,8 @@ const resolveAngleIncludePath = (
|
|
|
42
44
|
inputs,
|
|
43
45
|
);
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
const ext = cppMode ? ".hpp" : ".h";
|
|
48
|
+
return relativePath ? relativePath.replace(/\.cnx$/, ext) : null;
|
|
46
49
|
};
|
|
47
50
|
|
|
48
51
|
/**
|
|
@@ -54,7 +57,12 @@ const transformAngleInclude = (
|
|
|
54
57
|
filename: string,
|
|
55
58
|
options: IIncludeTransformOptions,
|
|
56
59
|
): string => {
|
|
57
|
-
const {
|
|
60
|
+
const {
|
|
61
|
+
sourcePath,
|
|
62
|
+
includeDirs = [],
|
|
63
|
+
inputs = [],
|
|
64
|
+
cppMode = false,
|
|
65
|
+
} = options;
|
|
58
66
|
|
|
59
67
|
// Try to resolve the correct output path
|
|
60
68
|
if (sourcePath) {
|
|
@@ -63,6 +71,7 @@ const transformAngleInclude = (
|
|
|
63
71
|
sourcePath,
|
|
64
72
|
includeDirs,
|
|
65
73
|
inputs,
|
|
74
|
+
cppMode,
|
|
66
75
|
);
|
|
67
76
|
if (resolvedPath) {
|
|
68
77
|
return includeText.replace(`<${filename}.cnx>`, `<${resolvedPath}>`);
|
|
@@ -70,7 +79,8 @@ const transformAngleInclude = (
|
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
// Fallback: simple replacement
|
|
73
|
-
|
|
82
|
+
const ext = cppMode ? ".hpp" : ".h";
|
|
83
|
+
return includeText.replace(`<${filename}.cnx>`, `<${filename}${ext}>`);
|
|
74
84
|
};
|
|
75
85
|
|
|
76
86
|
/**
|
|
@@ -82,7 +92,7 @@ const transformQuoteInclude = (
|
|
|
82
92
|
filepath: string,
|
|
83
93
|
options: IIncludeTransformOptions,
|
|
84
94
|
): string => {
|
|
85
|
-
const { sourcePath } = options;
|
|
95
|
+
const { sourcePath, cppMode = false } = options;
|
|
86
96
|
|
|
87
97
|
// Validate .cnx file exists if we have source path
|
|
88
98
|
if (sourcePath) {
|
|
@@ -98,12 +108,14 @@ const transformQuoteInclude = (
|
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
// Transform to .h
|
|
102
|
-
|
|
111
|
+
// Transform to .h or .hpp
|
|
112
|
+
const ext = cppMode ? ".hpp" : ".h";
|
|
113
|
+
return includeText.replace(`"${filepath}.cnx"`, `"${filepath}${ext}"`);
|
|
103
114
|
};
|
|
104
115
|
|
|
105
116
|
/**
|
|
106
|
-
* ADR-010: Transform #include directives, converting .cnx to .h
|
|
117
|
+
* ADR-010: Transform #include directives, converting .cnx to .h or .hpp
|
|
118
|
+
* Issue #941: Uses .hpp extension when cppMode is true
|
|
107
119
|
* Validates that .cnx files exist if sourcePath is available
|
|
108
120
|
* Supports both <file.cnx> and "file.cnx" forms
|
|
109
121
|
*
|
|
@@ -156,6 +156,44 @@ describe("IncludeGenerator", () => {
|
|
|
156
156
|
|
|
157
157
|
expect(result).toBe("#include <lib.h>");
|
|
158
158
|
});
|
|
159
|
+
|
|
160
|
+
it("transforms angle bracket .cnx include to .hpp in C++ mode", () => {
|
|
161
|
+
const result = transformIncludeDirective("#include <utils.cnx>", {
|
|
162
|
+
sourcePath: null,
|
|
163
|
+
cppMode: true,
|
|
164
|
+
});
|
|
165
|
+
expect(result).toBe("#include <utils.hpp>");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("resolves path from inputs with .hpp in C++ mode", () => {
|
|
169
|
+
vi.mocked(CnxFileResolver.findCnxFile).mockReturnValue(
|
|
170
|
+
"/project/src/Display/utils.cnx",
|
|
171
|
+
);
|
|
172
|
+
vi.mocked(CnxFileResolver.getRelativePathFromInputs).mockReturnValue(
|
|
173
|
+
"Display/utils.cnx",
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const result = transformIncludeDirective("#include <utils.cnx>", {
|
|
177
|
+
sourcePath: "/project/src/main.cnx",
|
|
178
|
+
includeDirs: ["/project/src/Display"],
|
|
179
|
+
inputs: ["/project/src"],
|
|
180
|
+
cppMode: true,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(result).toBe("#include <Display/utils.hpp>");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("falls back to .hpp in C++ mode when file not found", () => {
|
|
187
|
+
vi.mocked(CnxFileResolver.findCnxFile).mockReturnValue(null);
|
|
188
|
+
|
|
189
|
+
const result = transformIncludeDirective("#include <missing.cnx>", {
|
|
190
|
+
sourcePath: "/project/src/main.cnx",
|
|
191
|
+
inputs: ["/project/src"],
|
|
192
|
+
cppMode: true,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result).toBe("#include <missing.hpp>");
|
|
196
|
+
});
|
|
159
197
|
});
|
|
160
198
|
|
|
161
199
|
// ==========================================================================
|
|
@@ -221,6 +259,28 @@ describe("IncludeGenerator", () => {
|
|
|
221
259
|
}),
|
|
222
260
|
).toThrow(/Referenced in:.*main\.cnx/);
|
|
223
261
|
});
|
|
262
|
+
|
|
263
|
+
it("transforms quoted .cnx include to .hpp in C++ mode", () => {
|
|
264
|
+
vi.mocked(CnxFileResolver.cnxFileExists).mockReturnValue(true);
|
|
265
|
+
|
|
266
|
+
const result = transformIncludeDirective('#include "helper.cnx"', {
|
|
267
|
+
sourcePath: "/project/src/main.cnx",
|
|
268
|
+
cppMode: true,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
expect(result).toBe('#include "helper.hpp"');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("transforms quoted include with relative path to .hpp in C++ mode", () => {
|
|
275
|
+
vi.mocked(CnxFileResolver.cnxFileExists).mockReturnValue(true);
|
|
276
|
+
|
|
277
|
+
const result = transformIncludeDirective('#include "../lib/utils.cnx"', {
|
|
278
|
+
sourcePath: "/project/src/main.cnx",
|
|
279
|
+
cppMode: true,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(result).toBe('#include "../lib/utils.hpp"');
|
|
283
|
+
});
|
|
224
284
|
});
|
|
225
285
|
|
|
226
286
|
// ==========================================================================
|
|
@@ -262,6 +322,14 @@ describe("IncludeGenerator", () => {
|
|
|
262
322
|
});
|
|
263
323
|
expect(result).toBe("int x = 5;");
|
|
264
324
|
});
|
|
325
|
+
|
|
326
|
+
it("passes through .h includes unchanged even in C++ mode", () => {
|
|
327
|
+
const result = transformIncludeDirective('#include "myheader.h"', {
|
|
328
|
+
sourcePath: "/project/main.cnx",
|
|
329
|
+
cppMode: true,
|
|
330
|
+
});
|
|
331
|
+
expect(result).toBe('#include "myheader.h"');
|
|
332
|
+
});
|
|
265
333
|
});
|
|
266
334
|
|
|
267
335
|
// ==========================================================================
|