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
|
@@ -2798,7 +2798,8 @@ describe("CodeGenerator", () => {
|
|
|
2798
2798
|
|
|
2799
2799
|
expect(code).toContain("int8_t a = -128");
|
|
2800
2800
|
expect(code).toContain("int16_t b = -32768");
|
|
2801
|
-
|
|
2801
|
+
// MISRA 10.3: -2147483648 is replaced with (int32_t)INT32_MIN
|
|
2802
|
+
expect(code).toContain("int32_t c = (int32_t)INT32_MIN");
|
|
2802
2803
|
});
|
|
2803
2804
|
});
|
|
2804
2805
|
|
|
@@ -3031,7 +3032,8 @@ describe("CodeGenerator", () => {
|
|
|
3031
3032
|
});
|
|
3032
3033
|
|
|
3033
3034
|
// Wrap doesn't use clamp helpers
|
|
3034
|
-
|
|
3035
|
+
// Issue #845: MISRA 10.3 - narrow types expand with cast
|
|
3036
|
+
expect(code).toContain("(uint8_t)(counter + 1");
|
|
3035
3037
|
expect(code).not.toContain("cnx_clamp");
|
|
3036
3038
|
});
|
|
3037
3039
|
});
|
|
@@ -4880,7 +4882,9 @@ describe("CodeGenerator", () => {
|
|
|
4880
4882
|
});
|
|
4881
4883
|
|
|
4882
4884
|
describe("Compound assignment operators", () => {
|
|
4883
|
-
|
|
4885
|
+
// Issue #845: MISRA 10.3 - narrow type compound assignments expand to explicit cast
|
|
4886
|
+
// u8 val &= 0x0F => val = (uint8_t)(val & 0x0F)
|
|
4887
|
+
it("should generate bitwise AND compound assignment with MISRA cast", () => {
|
|
4884
4888
|
const source = `
|
|
4885
4889
|
wrap u8 mask <- 0xFF;
|
|
4886
4890
|
void main() {
|
|
@@ -4897,10 +4901,10 @@ describe("CodeGenerator", () => {
|
|
|
4897
4901
|
sourcePath: "test.cnx",
|
|
4898
4902
|
});
|
|
4899
4903
|
|
|
4900
|
-
expect(code).toContain("
|
|
4904
|
+
expect(code).toContain("(uint8_t)(mask &");
|
|
4901
4905
|
});
|
|
4902
4906
|
|
|
4903
|
-
it("should generate bitwise OR compound assignment", () => {
|
|
4907
|
+
it("should generate bitwise OR compound assignment with MISRA cast", () => {
|
|
4904
4908
|
const source = `
|
|
4905
4909
|
wrap u8 flags <- 0x00;
|
|
4906
4910
|
void main() {
|
|
@@ -4917,10 +4921,10 @@ describe("CodeGenerator", () => {
|
|
|
4917
4921
|
sourcePath: "test.cnx",
|
|
4918
4922
|
});
|
|
4919
4923
|
|
|
4920
|
-
expect(code).toContain("
|
|
4924
|
+
expect(code).toContain("(uint8_t)(flags |");
|
|
4921
4925
|
});
|
|
4922
4926
|
|
|
4923
|
-
it("should generate bitwise XOR compound assignment", () => {
|
|
4927
|
+
it("should generate bitwise XOR compound assignment with MISRA cast", () => {
|
|
4924
4928
|
const source = `
|
|
4925
4929
|
wrap u8 bits <- 0x55;
|
|
4926
4930
|
void main() {
|
|
@@ -4937,10 +4941,10 @@ describe("CodeGenerator", () => {
|
|
|
4937
4941
|
sourcePath: "test.cnx",
|
|
4938
4942
|
});
|
|
4939
4943
|
|
|
4940
|
-
expect(code).toContain("
|
|
4944
|
+
expect(code).toContain("(uint8_t)(bits ^");
|
|
4941
4945
|
});
|
|
4942
4946
|
|
|
4943
|
-
it("should generate left shift compound assignment", () => {
|
|
4947
|
+
it("should generate left shift compound assignment with MISRA cast", () => {
|
|
4944
4948
|
const source = `
|
|
4945
4949
|
wrap u8 val <- 1;
|
|
4946
4950
|
void main() {
|
|
@@ -4957,10 +4961,10 @@ describe("CodeGenerator", () => {
|
|
|
4957
4961
|
sourcePath: "test.cnx",
|
|
4958
4962
|
});
|
|
4959
4963
|
|
|
4960
|
-
expect(code).toContain("
|
|
4964
|
+
expect(code).toContain("(uint8_t)(val <<");
|
|
4961
4965
|
});
|
|
4962
4966
|
|
|
4963
|
-
it("should generate right shift compound assignment", () => {
|
|
4967
|
+
it("should generate right shift compound assignment with MISRA cast", () => {
|
|
4964
4968
|
const source = `
|
|
4965
4969
|
wrap u8 val <- 0x80;
|
|
4966
4970
|
void main() {
|
|
@@ -4977,7 +4981,7 @@ describe("CodeGenerator", () => {
|
|
|
4977
4981
|
sourcePath: "test.cnx",
|
|
4978
4982
|
});
|
|
4979
4983
|
|
|
4980
|
-
expect(code).toContain("
|
|
4984
|
+
expect(code).toContain("(uint8_t)(val >>");
|
|
4981
4985
|
});
|
|
4982
4986
|
|
|
4983
4987
|
it("should generate multiply compound assignment", () => {
|
|
@@ -7886,7 +7890,7 @@ describe("CodeGenerator", () => {
|
|
|
7886
7890
|
sourcePath: "test.cnx",
|
|
7887
7891
|
});
|
|
7888
7892
|
|
|
7889
|
-
expect(code).toContain("callee(arr[
|
|
7893
|
+
expect(code).toContain("callee(arr[0U])");
|
|
7890
7894
|
});
|
|
7891
7895
|
|
|
7892
7896
|
it("should handle scope member as argument", () => {
|
|
@@ -8035,7 +8039,7 @@ describe("CodeGenerator", () => {
|
|
|
8035
8039
|
sourcePath: "test.cnx",
|
|
8036
8040
|
});
|
|
8037
8041
|
|
|
8038
|
-
expect(code).toContain("callee(
|
|
8042
|
+
expect(code).toContain("callee(42U)");
|
|
8039
8043
|
});
|
|
8040
8044
|
|
|
8041
8045
|
it("should handle nested function call as argument", () => {
|
|
@@ -9138,8 +9142,8 @@ describe("CodeGenerator", () => {
|
|
|
9138
9142
|
sourcePath: "test.cnx",
|
|
9139
9143
|
});
|
|
9140
9144
|
|
|
9141
|
-
// Bitmap indexing generates bit extraction
|
|
9142
|
-
expect(code).toContain("((flags
|
|
9145
|
+
// Bitmap indexing generates bit extraction (no shift for position 0)
|
|
9146
|
+
expect(code).toContain("((flags) & 1)");
|
|
9143
9147
|
});
|
|
9144
9148
|
});
|
|
9145
9149
|
|
|
@@ -10707,8 +10711,8 @@ describe("CodeGenerator", () => {
|
|
|
10707
10711
|
sourcePath: "test.cnx",
|
|
10708
10712
|
});
|
|
10709
10713
|
|
|
10710
|
-
// Bit access extracts bit 0 with width 1
|
|
10711
|
-
expect(code).toContain("((flags) & ((1U <<
|
|
10714
|
+
// Bit access extracts bit 0 with width 1 (no shift for position 0)
|
|
10715
|
+
expect(code).toContain("((flags) & ((1U << 1U) - 1))");
|
|
10712
10716
|
});
|
|
10713
10717
|
|
|
10714
10718
|
it("should not dereference array parameter for index access", () => {
|
|
@@ -10728,8 +10732,8 @@ describe("CodeGenerator", () => {
|
|
|
10728
10732
|
});
|
|
10729
10733
|
|
|
10730
10734
|
// Array params don't need dereference
|
|
10731
|
-
// Array
|
|
10732
|
-
expect(code).toContain("arr[
|
|
10735
|
+
// Array index gets U suffix per MISRA 7.2
|
|
10736
|
+
expect(code).toContain("arr[5U]");
|
|
10733
10737
|
expect(code).not.toContain("(*arr)");
|
|
10734
10738
|
});
|
|
10735
10739
|
|
|
@@ -12990,11 +12994,11 @@ describe("CodeGenerator", () => {
|
|
|
12990
12994
|
sourcePath: "test.cnx",
|
|
12991
12995
|
});
|
|
12992
12996
|
|
|
12993
|
-
// Bit range access generates shift
|
|
12997
|
+
// Bit range access generates mask (no shift for start=0)
|
|
12994
12998
|
expect(code).toContain("flags");
|
|
12995
12999
|
expect(code).toContain("& 0xFF");
|
|
12996
|
-
//
|
|
12997
|
-
expect(code).toContain(">>
|
|
13000
|
+
// Optimization: no shift when start=0
|
|
13001
|
+
expect(code).not.toContain(">> 0");
|
|
12998
13002
|
});
|
|
12999
13003
|
|
|
13000
13004
|
it("should generate integer bit range with shift for non-zero start", () => {
|
|
@@ -13444,7 +13448,7 @@ describe("CodeGenerator", () => {
|
|
|
13444
13448
|
sourcePath: "test.cnx",
|
|
13445
13449
|
});
|
|
13446
13450
|
|
|
13447
|
-
expect(code).toContain("process(x +
|
|
13451
|
+
expect(code).toContain("process(x + 5U)");
|
|
13448
13452
|
});
|
|
13449
13453
|
});
|
|
13450
13454
|
|
|
@@ -13691,8 +13695,8 @@ describe("CodeGenerator", () => {
|
|
|
13691
13695
|
});
|
|
13692
13696
|
|
|
13693
13697
|
expect(code).toContain("__bits_val");
|
|
13694
|
-
//
|
|
13695
|
-
expect(code).toContain(">>
|
|
13698
|
+
// Optimization: no shift when start=0
|
|
13699
|
+
expect(code).not.toContain(">> 0");
|
|
13696
13700
|
});
|
|
13697
13701
|
|
|
13698
13702
|
it("should handle multiple reads from same float reusing union shadow", () => {
|
|
@@ -14673,8 +14677,8 @@ describe("CodeGenerator", () => {
|
|
|
14673
14677
|
sourcePath: "test.cnx",
|
|
14674
14678
|
});
|
|
14675
14679
|
|
|
14676
|
-
//
|
|
14677
|
-
expect(code).toContain(">>
|
|
14680
|
+
// Optimization: no shift when start=0
|
|
14681
|
+
expect(code).not.toContain(">> 0");
|
|
14678
14682
|
});
|
|
14679
14683
|
|
|
14680
14684
|
it("should generate 64-bit mask for u64 values", () => {
|
|
@@ -15324,8 +15328,9 @@ describe("CodeGenerator", () => {
|
|
|
15324
15328
|
sourcePath: "test.cnx",
|
|
15325
15329
|
});
|
|
15326
15330
|
|
|
15327
|
-
// Wrap types
|
|
15328
|
-
|
|
15331
|
+
// Wrap types don't use cnx_clamp helpers
|
|
15332
|
+
// Issue #845: MISRA 10.3 - narrow types expand with cast
|
|
15333
|
+
expect(code).toContain("(uint8_t)(val + 10");
|
|
15329
15334
|
expect(code).not.toContain("cnx_");
|
|
15330
15335
|
});
|
|
15331
15336
|
});
|
|
@@ -1130,17 +1130,17 @@ describe("TypeResolver", () => {
|
|
|
1130
1130
|
});
|
|
1131
1131
|
});
|
|
1132
1132
|
|
|
1133
|
-
describe("unsuffixed literals", () => {
|
|
1134
|
-
it("should return
|
|
1135
|
-
expect(TypeResolver.getLiteralType(mockLiteral("42"))).
|
|
1133
|
+
describe("unsuffixed literals (MISRA 10.3 compliance)", () => {
|
|
1134
|
+
it("should return int for unsuffixed integer", () => {
|
|
1135
|
+
expect(TypeResolver.getLiteralType(mockLiteral("42"))).toBe("int");
|
|
1136
1136
|
});
|
|
1137
1137
|
|
|
1138
|
-
it("should return
|
|
1139
|
-
expect(TypeResolver.getLiteralType(mockLiteral("0xFF"))).
|
|
1138
|
+
it("should return int for unsuffixed hex", () => {
|
|
1139
|
+
expect(TypeResolver.getLiteralType(mockLiteral("0xFF"))).toBe("int");
|
|
1140
1140
|
});
|
|
1141
1141
|
|
|
1142
|
-
it("should return
|
|
1143
|
-
expect(TypeResolver.getLiteralType(mockLiteral("3.14"))).
|
|
1142
|
+
it("should return f64 for unsuffixed float", () => {
|
|
1143
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14"))).toBe("f64");
|
|
1144
1144
|
});
|
|
1145
1145
|
});
|
|
1146
1146
|
});
|
|
@@ -460,19 +460,39 @@ class AssignmentClassifier {
|
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
/**
|
|
463
|
-
* Classify this.reg[bit] / this.arr[i] patterns with array access.
|
|
463
|
+
* Classify this.reg[bit] / this.arr[i] / this.flags[3] patterns with array access.
|
|
464
|
+
* Issue #954: Uses SubscriptClassifier to distinguish array vs bit access.
|
|
464
465
|
*/
|
|
465
466
|
private static classifyThisWithArrayAccess(
|
|
466
467
|
ctx: IAssignmentContext,
|
|
467
468
|
scopedRegName: string,
|
|
468
469
|
): AssignmentKind {
|
|
470
|
+
// Check for scoped register first
|
|
469
471
|
if (CodeGenState.symbols!.knownRegisters.has(scopedRegName)) {
|
|
470
472
|
const hasBitRange = ctx.postfixOps.some((op) => op.COMMA() !== null);
|
|
471
473
|
return hasBitRange
|
|
472
474
|
? AssignmentKind.SCOPED_REGISTER_BIT_RANGE
|
|
473
475
|
: AssignmentKind.SCOPED_REGISTER_BIT;
|
|
474
476
|
}
|
|
475
|
-
|
|
477
|
+
|
|
478
|
+
// Get type info using resolved scoped name (e.g., "Sensor_value")
|
|
479
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(scopedRegName);
|
|
480
|
+
|
|
481
|
+
// Use shared classifier to determine array vs bit access
|
|
482
|
+
const subscriptKind = SubscriptClassifier.classify({
|
|
483
|
+
typeInfo: typeInfo ?? null,
|
|
484
|
+
subscriptCount: ctx.lastSubscriptExprCount,
|
|
485
|
+
isRegisterAccess: false,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
switch (subscriptKind) {
|
|
489
|
+
case "bit_single":
|
|
490
|
+
return AssignmentKind.THIS_BIT;
|
|
491
|
+
case "bit_range":
|
|
492
|
+
return AssignmentKind.THIS_BIT_RANGE;
|
|
493
|
+
default:
|
|
494
|
+
return AssignmentKind.THIS_ARRAY;
|
|
495
|
+
}
|
|
476
496
|
}
|
|
477
497
|
|
|
478
498
|
/**
|
|
@@ -586,10 +606,13 @@ class AssignmentClassifier {
|
|
|
586
606
|
}
|
|
587
607
|
|
|
588
608
|
// Overflow clamp (integers only, not floats)
|
|
589
|
-
//
|
|
609
|
+
// Only applies to arithmetic compound ops (+= -= *=) which can overflow
|
|
610
|
+
// Bitwise ops (&= |= ^= <<= >>=) don't overflow, so they go to SIMPLE
|
|
611
|
+
const ARITHMETIC_COMPOUND_OPS = new Set(["+=", "-=", "*="]);
|
|
590
612
|
if (
|
|
591
613
|
typeInfo.overflowBehavior === "clamp" &&
|
|
592
|
-
TypeCheckUtils.isInteger(typeInfo.baseType)
|
|
614
|
+
TypeCheckUtils.isInteger(typeInfo.baseType) &&
|
|
615
|
+
ARITHMETIC_COMPOUND_OPS.has(ctx.cOp)
|
|
593
616
|
) {
|
|
594
617
|
return AssignmentKind.OVERFLOW_CLAMP;
|
|
595
618
|
}
|
|
@@ -113,6 +113,12 @@ enum AssignmentKind {
|
|
|
113
113
|
/** this.arr[i] <- value (scoped array element) */
|
|
114
114
|
THIS_ARRAY,
|
|
115
115
|
|
|
116
|
+
/** this.flags[3] <- true (scoped integer bit access) */
|
|
117
|
+
THIS_BIT,
|
|
118
|
+
|
|
119
|
+
/** this.value[0, 8] <- byte (scoped integer bit range) */
|
|
120
|
+
THIS_BIT_RANGE,
|
|
121
|
+
|
|
116
122
|
// === Complex access patterns ===
|
|
117
123
|
|
|
118
124
|
/** struct.field.subfield <- value (member chain) */
|
|
@@ -120,6 +120,7 @@ function setupSymbols(
|
|
|
120
120
|
scopePrivateConstValues: new Map(),
|
|
121
121
|
functionReturnTypes: new Map(),
|
|
122
122
|
getSingleFunctionForVariable: () => null,
|
|
123
|
+
opaqueTypes: new Set(),
|
|
123
124
|
hasPublicSymbols: () => false,
|
|
124
125
|
};
|
|
125
126
|
}
|
|
@@ -543,6 +544,78 @@ describe("AssignmentClassifier - Prefix Patterns", () => {
|
|
|
543
544
|
|
|
544
545
|
expect(AssignmentClassifier.classify(ctx)).toBe(AssignmentKind.THIS_ARRAY);
|
|
545
546
|
});
|
|
547
|
+
|
|
548
|
+
// Issue #954: Scope variable bit access should be classified as THIS_BIT/THIS_BIT_RANGE
|
|
549
|
+
it("classifies this.flags[3] as THIS_BIT for integer type", () => {
|
|
550
|
+
setupSymbols();
|
|
551
|
+
CodeGenState.currentScope = "Sensor";
|
|
552
|
+
// Register Sensor_flags as a non-array integer type
|
|
553
|
+
CodeGenState.setVariableTypeInfo(
|
|
554
|
+
"Sensor_flags",
|
|
555
|
+
createTypeInfo({ baseType: "u8", isArray: false }),
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
const ctx = createMockContext({
|
|
559
|
+
identifiers: ["flags"],
|
|
560
|
+
subscripts: [{} as IAssignmentContext["subscripts"][0]],
|
|
561
|
+
hasThis: true,
|
|
562
|
+
hasArrayAccess: true,
|
|
563
|
+
postfixOpsCount: 1,
|
|
564
|
+
isSimpleIdentifier: false,
|
|
565
|
+
lastSubscriptExprCount: 1, // single bit
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
expect(AssignmentClassifier.classify(ctx)).toBe(AssignmentKind.THIS_BIT);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it("classifies this.value[0, 8] as THIS_BIT_RANGE for integer type", () => {
|
|
572
|
+
setupSymbols();
|
|
573
|
+
CodeGenState.currentScope = "Sensor";
|
|
574
|
+
// Register Sensor_value as a non-array integer type
|
|
575
|
+
CodeGenState.setVariableTypeInfo(
|
|
576
|
+
"Sensor_value",
|
|
577
|
+
createTypeInfo({ baseType: "u16", isArray: false }),
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
const ctx = createMockContext({
|
|
581
|
+
identifiers: ["value"],
|
|
582
|
+
subscripts: [
|
|
583
|
+
{} as IAssignmentContext["subscripts"][0],
|
|
584
|
+
{} as IAssignmentContext["subscripts"][0],
|
|
585
|
+
],
|
|
586
|
+
hasThis: true,
|
|
587
|
+
hasArrayAccess: true,
|
|
588
|
+
postfixOpsCount: 1,
|
|
589
|
+
isSimpleIdentifier: false,
|
|
590
|
+
lastSubscriptExprCount: 2, // bit range
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
expect(AssignmentClassifier.classify(ctx)).toBe(
|
|
594
|
+
AssignmentKind.THIS_BIT_RANGE,
|
|
595
|
+
);
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it("classifies this.data[i] as THIS_ARRAY for array type", () => {
|
|
599
|
+
setupSymbols();
|
|
600
|
+
CodeGenState.currentScope = "Buffer";
|
|
601
|
+
// Register Buffer_data as an array type
|
|
602
|
+
CodeGenState.setVariableTypeInfo(
|
|
603
|
+
"Buffer_data",
|
|
604
|
+
createTypeInfo({ baseType: "u8", isArray: true, arrayDimensions: [10] }),
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
const ctx = createMockContext({
|
|
608
|
+
identifiers: ["data"],
|
|
609
|
+
subscripts: [{} as IAssignmentContext["subscripts"][0]],
|
|
610
|
+
hasThis: true,
|
|
611
|
+
hasArrayAccess: true,
|
|
612
|
+
postfixOpsCount: 1,
|
|
613
|
+
isSimpleIdentifier: false,
|
|
614
|
+
lastSubscriptExprCount: 1,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
expect(AssignmentClassifier.classify(ctx)).toBe(AssignmentKind.THIS_ARRAY);
|
|
618
|
+
});
|
|
546
619
|
});
|
|
547
620
|
|
|
548
621
|
// ========================================================================
|
|
@@ -206,10 +206,14 @@ function handleStructChainBitRange(ctx: IAssignmentContext): string {
|
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
208
|
* All bit access handlers for registration.
|
|
209
|
+
* Issue #954: THIS_BIT and THIS_BIT_RANGE reuse the integer bit handlers
|
|
210
|
+
* since they use resolvedBaseIdentifier which includes the scope prefix.
|
|
209
211
|
*/
|
|
210
212
|
const bitAccessHandlers: ReadonlyArray<[AssignmentKind, TAssignmentHandler]> = [
|
|
211
213
|
[AssignmentKind.INTEGER_BIT, handleIntegerBit],
|
|
212
214
|
[AssignmentKind.INTEGER_BIT_RANGE, handleIntegerBitRange],
|
|
215
|
+
[AssignmentKind.THIS_BIT, handleIntegerBit],
|
|
216
|
+
[AssignmentKind.THIS_BIT_RANGE, handleIntegerBitRange],
|
|
213
217
|
[AssignmentKind.STRUCT_MEMBER_BIT, handleStructMemberBit],
|
|
214
218
|
[AssignmentKind.ARRAY_ELEMENT_BIT, handleArrayElementBit],
|
|
215
219
|
[AssignmentKind.STRUCT_CHAIN_BIT_RANGE, handleStructChainBitRange],
|
|
@@ -3,25 +3,117 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The fallback case: generates `target = value;` or `target op= value;`
|
|
5
5
|
* Used when no special handling is needed.
|
|
6
|
+
*
|
|
7
|
+
* Issue #845: MISRA 10.3 - For compound assignments on narrower types (i8, i16,
|
|
8
|
+
* u8, u16), expands to explicit cast: `target = (type)(target OP value);`
|
|
9
|
+
* Also handles int-to-float conversions with explicit casts.
|
|
6
10
|
*/
|
|
7
11
|
import IAssignmentContext from "../IAssignmentContext";
|
|
8
12
|
import CodeGenState from "../../../../state/CodeGenState";
|
|
9
13
|
import type ICodeGenApi from "../../types/ICodeGenApi";
|
|
14
|
+
import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
|
|
15
|
+
import TypeResolver from "../../TypeResolver.js";
|
|
16
|
+
import TYPE_MAP from "../../types/TYPE_MAP.js";
|
|
17
|
+
import CppModeHelper from "../../helpers/CppModeHelper.js";
|
|
18
|
+
import COMPOUND_TO_BINARY from "../../types/COMPOUND_TO_BINARY.js";
|
|
10
19
|
|
|
11
20
|
/** Get typed generator reference */
|
|
12
21
|
function gen(): ICodeGenApi {
|
|
13
22
|
return CodeGenState.generator as ICodeGenApi;
|
|
14
23
|
}
|
|
15
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Try to handle compound assignment on narrow types (MISRA 10.3).
|
|
27
|
+
* Returns the generated code if handled, null otherwise.
|
|
28
|
+
*/
|
|
29
|
+
function tryHandleCompoundNarrowingCast(
|
|
30
|
+
ctx: IAssignmentContext,
|
|
31
|
+
target: string,
|
|
32
|
+
): string | null {
|
|
33
|
+
if (!ctx.isCompound || !ctx.firstIdTypeInfo) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const baseType = ctx.firstIdTypeInfo.baseType;
|
|
38
|
+
const promotedType = NarrowingCastHelper.getPromotedType(baseType);
|
|
39
|
+
|
|
40
|
+
if (promotedType !== "int" || baseType === "int") {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const binaryOp = COMPOUND_TO_BINARY[ctx.cOp];
|
|
45
|
+
if (!binaryOp) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const cType = TYPE_MAP[baseType] ?? baseType;
|
|
50
|
+
const expr = `(${target} ${binaryOp} ${ctx.generatedValue})`;
|
|
51
|
+
const castExpr = CppModeHelper.cast(cType, expr);
|
|
52
|
+
return `${target} = ${castExpr};`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Try to handle cross-type-category conversion (int <-> float).
|
|
57
|
+
* Returns the generated code if handled, null otherwise.
|
|
58
|
+
*/
|
|
59
|
+
function tryHandleIntToFloatConversion(
|
|
60
|
+
ctx: IAssignmentContext,
|
|
61
|
+
target: string,
|
|
62
|
+
): string | null {
|
|
63
|
+
if (ctx.isCompound || !ctx.firstIdTypeInfo || !ctx.valueCtx) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const targetType = ctx.firstIdTypeInfo.baseType;
|
|
68
|
+
const valueType = TypeResolver.getExpressionType(ctx.valueCtx);
|
|
69
|
+
|
|
70
|
+
if (!valueType) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
!NarrowingCastHelper.isCrossTypeCategoryConversion(valueType, targetType)
|
|
76
|
+
) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
!NarrowingCastHelper.isIntegerType(valueType) ||
|
|
82
|
+
!NarrowingCastHelper.isFloatType(targetType)
|
|
83
|
+
) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const castedValue = NarrowingCastHelper.wrapIntToFloat(
|
|
88
|
+
ctx.generatedValue,
|
|
89
|
+
targetType,
|
|
90
|
+
);
|
|
91
|
+
return `${target} ${ctx.cOp} ${castedValue};`;
|
|
92
|
+
}
|
|
93
|
+
|
|
16
94
|
/**
|
|
17
95
|
* Handle simple variable assignment.
|
|
18
96
|
*
|
|
19
97
|
* @example
|
|
20
98
|
* x <- 5 => x = 5;
|
|
21
99
|
* counter +<- 1 => counter += 1;
|
|
100
|
+
* i16_val &<- 0xFF => i16_val = (int16_t)(i16_val & 0xFF); // MISRA 10.3
|
|
22
101
|
*/
|
|
23
102
|
function handleSimpleAssignment(ctx: IAssignmentContext): string {
|
|
24
103
|
const target = gen().generateAssignmentTarget(ctx.targetCtx);
|
|
104
|
+
|
|
105
|
+
// Try compound assignment narrowing cast (MISRA 10.3)
|
|
106
|
+
const compoundResult = tryHandleCompoundNarrowingCast(ctx, target);
|
|
107
|
+
if (compoundResult) {
|
|
108
|
+
return compoundResult;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Try int-to-float conversion
|
|
112
|
+
const conversionResult = tryHandleIntToFloatConversion(ctx, target);
|
|
113
|
+
if (conversionResult) {
|
|
114
|
+
return conversionResult;
|
|
115
|
+
}
|
|
116
|
+
|
|
25
117
|
return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
|
|
26
118
|
}
|
|
27
119
|
|
package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts
CHANGED
|
@@ -59,13 +59,16 @@ describe("BitAccessHandlers", () => {
|
|
|
59
59
|
|
|
60
60
|
expect(kinds).toContain(AssignmentKind.INTEGER_BIT);
|
|
61
61
|
expect(kinds).toContain(AssignmentKind.INTEGER_BIT_RANGE);
|
|
62
|
+
expect(kinds).toContain(AssignmentKind.THIS_BIT);
|
|
63
|
+
expect(kinds).toContain(AssignmentKind.THIS_BIT_RANGE);
|
|
62
64
|
expect(kinds).toContain(AssignmentKind.STRUCT_MEMBER_BIT);
|
|
63
65
|
expect(kinds).toContain(AssignmentKind.ARRAY_ELEMENT_BIT);
|
|
64
66
|
expect(kinds).toContain(AssignmentKind.STRUCT_CHAIN_BIT_RANGE);
|
|
65
67
|
});
|
|
66
68
|
|
|
67
|
-
it("exports exactly
|
|
68
|
-
|
|
69
|
+
it("exports exactly 7 handlers", () => {
|
|
70
|
+
// Issue #954: Added THIS_BIT and THIS_BIT_RANGE for scope variable bit access
|
|
71
|
+
expect(bitAccessHandlers.length).toBe(7);
|
|
69
72
|
});
|
|
70
73
|
});
|
|
71
74
|
|
|
@@ -390,6 +390,20 @@ interface IOrchestrator {
|
|
|
390
390
|
|
|
391
391
|
/** Check if a float shadow has current value */
|
|
392
392
|
isFloatShadowCurrent(shadowName: string): boolean;
|
|
393
|
+
|
|
394
|
+
// === Issue #948: Opaque Type Helpers ===
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Check if a type is an opaque (forward-declared) struct type.
|
|
398
|
+
* Opaque types can only be used as pointers (cannot be instantiated).
|
|
399
|
+
*/
|
|
400
|
+
isOpaqueType(typeName: string): boolean;
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Mark a scope variable as having an opaque type.
|
|
404
|
+
* These variables are generated as pointers with NULL initialization.
|
|
405
|
+
*/
|
|
406
|
+
markOpaqueScopeVariable(qualifiedName: string): void;
|
|
393
407
|
}
|
|
394
408
|
|
|
395
409
|
export default IOrchestrator;
|
|
@@ -24,9 +24,11 @@ import generateScopedRegister from "./ScopedRegisterGenerator";
|
|
|
24
24
|
import BitmapCommentUtils from "./BitmapCommentUtils";
|
|
25
25
|
import ArrayDimensionUtils from "./ArrayDimensionUtils";
|
|
26
26
|
import QualifiedNameGenerator from "../../utils/QualifiedNameGenerator";
|
|
27
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Generate initializer expression for a variable declaration.
|
|
31
|
+
* Issue #872: Sets expectedType for MISRA 7.2 U suffix on unsigned literals.
|
|
30
32
|
*/
|
|
31
33
|
function generateInitializer(
|
|
32
34
|
varDecl: Parser.VariableDeclarationContext,
|
|
@@ -34,7 +36,12 @@ function generateInitializer(
|
|
|
34
36
|
orchestrator: IOrchestrator,
|
|
35
37
|
): string {
|
|
36
38
|
if (varDecl.expression()) {
|
|
37
|
-
|
|
39
|
+
// Issue #872: Set expectedType for MISRA 7.2 U suffix compliance
|
|
40
|
+
const typeName = orchestrator.generateType(varDecl.type());
|
|
41
|
+
return CodeGenState.withExpectedType(
|
|
42
|
+
typeName,
|
|
43
|
+
() => ` = ${orchestrator.generateExpression(varDecl.expression()!)}`,
|
|
44
|
+
);
|
|
38
45
|
}
|
|
39
46
|
// ADR-015: Zero initialization for uninitialized scope variables
|
|
40
47
|
return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
|
|
@@ -180,8 +187,17 @@ function generateRegularVariable(
|
|
|
180
187
|
const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
|
|
181
188
|
|
|
182
189
|
// ADR-016: All scope variables are emitted at file scope (static-like persistence)
|
|
183
|
-
|
|
190
|
+
let type = orchestrator.generateType(varDecl.type());
|
|
184
191
|
const fullName = QualifiedNameGenerator.forMember(scopeName, varName);
|
|
192
|
+
|
|
193
|
+
// Issue #948: Check if this is an opaque (forward-declared) struct type
|
|
194
|
+
// Opaque types must be declared as pointers with NULL initialization
|
|
195
|
+
const isOpaque = orchestrator.isOpaqueType(type);
|
|
196
|
+
if (isOpaque) {
|
|
197
|
+
type = `${type}*`;
|
|
198
|
+
orchestrator.markOpaqueScopeVariable(fullName);
|
|
199
|
+
}
|
|
200
|
+
|
|
185
201
|
// Issue #282: Add 'const' modifier for const variables
|
|
186
202
|
const constPrefix = isConst ? "const " : "";
|
|
187
203
|
const prefix = isPrivate ? "static " : "";
|
|
@@ -200,7 +216,13 @@ function generateRegularVariable(
|
|
|
200
216
|
|
|
201
217
|
// ADR-045: Add string capacity dimension for string arrays
|
|
202
218
|
decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
|
|
203
|
-
|
|
219
|
+
|
|
220
|
+
// Issue #948: Opaque types use NULL initialization instead of {0}
|
|
221
|
+
if (isOpaque) {
|
|
222
|
+
decl += " = NULL";
|
|
223
|
+
} else {
|
|
224
|
+
decl += generateInitializer(varDecl, isArray, orchestrator);
|
|
225
|
+
}
|
|
204
226
|
|
|
205
227
|
return decl + ";";
|
|
206
228
|
}
|