c-next 0.2.2 → 0.2.4

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.
Files changed (48) hide show
  1. package/README.md +59 -57
  2. package/dist/index.js +641 -191
  3. package/dist/index.js.map +4 -4
  4. package/package.json +1 -1
  5. package/src/cli/Runner.ts +1 -1
  6. package/src/cli/__tests__/Runner.test.ts +8 -8
  7. package/src/cli/serve/ServeCommand.ts +29 -9
  8. package/src/transpiler/Transpiler.ts +105 -200
  9. package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
  10. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
  11. package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
  12. package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
  13. package/src/transpiler/data/IncludeResolver.ts +11 -3
  14. package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
  15. package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
  16. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +170 -14
  17. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
  18. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +327 -0
  19. package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
  20. package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
  21. package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
  22. package/src/transpiler/logic/symbols/c/index.ts +50 -1
  23. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +99 -2
  24. package/src/transpiler/logic/symbols/c/utils/__tests__/DeclaratorUtils.test.ts +128 -0
  25. package/src/transpiler/output/codegen/CodeGenerator.ts +31 -5
  26. package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
  27. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +49 -36
  28. package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
  29. package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +90 -25
  30. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
  31. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
  32. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
  33. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
  34. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +10 -7
  35. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
  36. package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
  37. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
  38. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +6 -3
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +36 -22
  40. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +9 -1
  41. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +8 -6
  42. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
  43. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
  44. package/src/transpiler/state/CodeGenState.ts +6 -0
  45. package/src/transpiler/types/IPipelineFile.ts +2 -2
  46. package/src/transpiler/types/IPipelineInput.ts +1 -1
  47. package/src/transpiler/types/TTranspileInput.ts +18 -0
  48. package/src/utils/constants/TypeConstants.ts +22 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Unit tests for DeclaratorUtils
3
+ */
4
+
5
+ import { describe, it, expect } from "vitest";
6
+ import DeclaratorUtils from "../DeclaratorUtils";
7
+ import HeaderParser from "../../../../parser/HeaderParser";
8
+
9
+ describe("DeclaratorUtils", () => {
10
+ describe("extractTypeFromSpecQualList", () => {
11
+ it("should extract simple type", () => {
12
+ // Parse a struct with a simple int field
13
+ const source = `struct Test { int value; };`;
14
+ const { tree } = HeaderParser.parseC(source);
15
+
16
+ // Navigate to the struct's field's specifierQualifierList
17
+ const structSpec = tree
18
+ ?.translationUnit()
19
+ ?.externalDeclaration(0)
20
+ ?.declaration()
21
+ ?.declarationSpecifiers()
22
+ ?.declarationSpecifier(0)
23
+ ?.typeSpecifier()
24
+ ?.structOrUnionSpecifier();
25
+
26
+ const fieldDecl = structSpec
27
+ ?.structDeclarationList()
28
+ ?.structDeclaration(0);
29
+ const specQualList = fieldDecl?.specifierQualifierList();
30
+
31
+ const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
32
+ expect(result).toBe("int");
33
+ });
34
+
35
+ it("should extract unsigned int type", () => {
36
+ const source = `struct Test { unsigned int count; };`;
37
+ const { tree } = HeaderParser.parseC(source);
38
+
39
+ const structSpec = tree
40
+ ?.translationUnit()
41
+ ?.externalDeclaration(0)
42
+ ?.declaration()
43
+ ?.declarationSpecifiers()
44
+ ?.declarationSpecifier(0)
45
+ ?.typeSpecifier()
46
+ ?.structOrUnionSpecifier();
47
+
48
+ const fieldDecl = structSpec
49
+ ?.structDeclarationList()
50
+ ?.structDeclaration(0);
51
+ const specQualList = fieldDecl?.specifierQualifierList();
52
+
53
+ const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
54
+ // For simple types without struct specifier, getText() is used
55
+ // The specifierQualifierList has "unsigned" and "int" as separate tokens
56
+ // which are joined with spaces
57
+ expect(result).toBe("unsigned int");
58
+ });
59
+
60
+ it("should extract anonymous struct type with proper spacing", () => {
61
+ // This is the bug case from issue #875
62
+ const source = `typedef struct {
63
+ int value;
64
+ struct {
65
+ unsigned int flag_a: 1;
66
+ unsigned int flag_b: 1;
67
+ } flags;
68
+ } config_t;`;
69
+ const { tree } = HeaderParser.parseC(source);
70
+
71
+ const structSpec = tree
72
+ ?.translationUnit()
73
+ ?.externalDeclaration(0)
74
+ ?.declaration()
75
+ ?.declarationSpecifiers()
76
+ ?.declarationSpecifier(1)
77
+ ?.typeSpecifier()
78
+ ?.structOrUnionSpecifier();
79
+
80
+ // Get the second field (flags) which is an anonymous struct
81
+ const flagsDecl = structSpec
82
+ ?.structDeclarationList()
83
+ ?.structDeclaration(1);
84
+ const specQualList = flagsDecl?.specifierQualifierList();
85
+
86
+ const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
87
+
88
+ // Should have proper spacing, not concatenated tokens like "struct{...}"
89
+ expect(result).toContain("struct {");
90
+ // Note: our reconstruction adds spaces around the colon, which is valid C
91
+ expect(result).toContain("unsigned int flag_a : 1;");
92
+ expect(result).toContain("unsigned int flag_b : 1;");
93
+ });
94
+
95
+ it("should extract anonymous union type with proper spacing", () => {
96
+ // Test union handling (code path: structOrUnion.Struct() returns false)
97
+ const source = `typedef struct {
98
+ union {
99
+ int as_int;
100
+ float as_float;
101
+ } value;
102
+ } variant_t;`;
103
+ const { tree } = HeaderParser.parseC(source);
104
+
105
+ const structSpec = tree
106
+ ?.translationUnit()
107
+ ?.externalDeclaration(0)
108
+ ?.declaration()
109
+ ?.declarationSpecifiers()
110
+ ?.declarationSpecifier(1)
111
+ ?.typeSpecifier()
112
+ ?.structOrUnionSpecifier();
113
+
114
+ // Get the first field (value) which is an anonymous union
115
+ const valueDecl = structSpec
116
+ ?.structDeclarationList()
117
+ ?.structDeclaration(0);
118
+ const specQualList = valueDecl?.specifierQualifierList();
119
+
120
+ const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
121
+
122
+ // Should use "union" keyword and have proper spacing
123
+ expect(result).toContain("union {");
124
+ expect(result).toContain("int as_int;");
125
+ expect(result).toContain("float as_float;");
126
+ });
127
+ });
128
+ });
@@ -1042,11 +1042,14 @@ export default class CodeGenerator implements IOrchestrator {
1042
1042
  }
1043
1043
 
1044
1044
  /**
1045
- * Validate do-while condition.
1045
+ * Validate condition is a boolean expression (ADR-027, Issue #884).
1046
1046
  * Part of IOrchestrator interface (ADR-053 A3).
1047
1047
  */
1048
- validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
1049
- TypeValidator.validateDoWhileCondition(ctx);
1048
+ validateConditionIsBoolean(
1049
+ ctx: Parser.ExpressionContext,
1050
+ conditionType: string,
1051
+ ): void {
1052
+ TypeValidator.validateConditionIsBoolean(ctx, conditionType);
1050
1053
  }
1051
1054
 
1052
1055
  /**
@@ -1563,8 +1566,8 @@ export default class CodeGenerator implements IOrchestrator {
1563
1566
 
1564
1567
  /**
1565
1568
  * Issue #561: Analyze modifications in a parse tree without full code generation.
1566
- * Used by Pipeline.transpileSource() to collect modification info from includes
1567
- * for cross-file const inference (unified with Pipeline.run() behavior).
1569
+ * Used by the transpile() pipeline to collect modification info from includes
1570
+ * for cross-file const inference.
1568
1571
  *
1569
1572
  * Issue #565: Now accepts optional cross-file data for transitive propagation.
1570
1573
  * When a file calls a function from an included file that modifies its param,
@@ -3496,6 +3499,17 @@ export default class CodeGenerator implements IOrchestrator {
3496
3499
 
3497
3500
  // For C-Next/C structs, generate designated initializer
3498
3501
  const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
3502
+
3503
+ // Issue #882: In C++ mode, anonymous structs/unions must use plain brace init.
3504
+ // Compound literals like (struct { ... }){ ... } create incompatible types in C++
3505
+ // because each struct { ... } definition creates a distinct nominal type.
3506
+ if (
3507
+ CodeGenState.cppMode &&
3508
+ (typeName.startsWith("struct {") || typeName.startsWith("union {"))
3509
+ ) {
3510
+ return `{ ${fieldInits.join(", ")} }`;
3511
+ }
3512
+
3499
3513
  return `(${castType}){ ${fieldInits.join(", ")} }`;
3500
3514
  }
3501
3515
 
@@ -3763,6 +3777,18 @@ export default class CodeGenerator implements IOrchestrator {
3763
3777
  return true;
3764
3778
  }
3765
3779
 
3780
+ // Callback-compatible functions: struct params become pass-by-value
3781
+ // to match C function pointer typedef signatures
3782
+ if (
3783
+ CodeGenState.currentFunctionName &&
3784
+ CodeGenState.callbackCompatibleFunctions.has(
3785
+ CodeGenState.currentFunctionName,
3786
+ ) &&
3787
+ this.isKnownStruct(typeName)
3788
+ ) {
3789
+ return true;
3790
+ }
3791
+
3766
3792
  return false;
3767
3793
  }
3768
3794
 
@@ -714,16 +714,19 @@ class TypeValidator {
714
714
  }
715
715
 
716
716
  // ========================================================================
717
- // Do-While Validation (ADR-027)
717
+ // Condition Boolean Validation (ADR-027, Issue #884)
718
718
  // ========================================================================
719
719
 
720
- static validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
720
+ static validateConditionIsBoolean(
721
+ ctx: Parser.ExpressionContext,
722
+ conditionType: string,
723
+ ): void {
721
724
  const ternaryExpr = ctx.ternaryExpression();
722
725
  const orExprs = ternaryExpr.orExpression();
723
726
 
724
727
  if (orExprs.length !== 1) {
725
728
  throw new Error(
726
- `Error E0701: do-while condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
729
+ `Error E0701: ${conditionType} condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
727
730
  );
728
731
  }
729
732
 
@@ -737,7 +740,7 @@ class TypeValidator {
737
740
  const andExpr = orExpr.andExpression(0);
738
741
  if (!andExpr) {
739
742
  throw new Error(
740
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
743
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
741
744
  );
742
745
  }
743
746
 
@@ -748,7 +751,7 @@ class TypeValidator {
748
751
  const equalityExpr = andExpr.equalityExpression(0);
749
752
  if (!equalityExpr) {
750
753
  throw new Error(
751
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
754
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
752
755
  );
753
756
  }
754
757
 
@@ -759,7 +762,7 @@ class TypeValidator {
759
762
  const relationalExpr = equalityExpr.relationalExpression(0);
760
763
  if (!relationalExpr) {
761
764
  throw new Error(
762
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
765
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
763
766
  );
764
767
  }
765
768
 
@@ -773,7 +776,7 @@ class TypeValidator {
773
776
  }
774
777
 
775
778
  throw new Error(
776
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
779
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
777
780
  );
778
781
  }
779
782
 
@@ -8776,7 +8776,7 @@ describe("CodeGenerator", () => {
8776
8776
  expect(code).toContain(">> 4");
8777
8777
  });
8778
8778
 
8779
- it("should generate float bit range read with shadow variable", () => {
8779
+ it("should generate float bit range read with union shadow variable", () => {
8780
8780
  const source = `
8781
8781
  void test() {
8782
8782
  f32 value <- 3.14;
@@ -8793,8 +8793,9 @@ describe("CodeGenerator", () => {
8793
8793
  sourcePath: "test.cnx",
8794
8794
  });
8795
8795
 
8796
- // Float bit access uses shadow variable and memcpy
8797
- expect(code).toContain("memcpy");
8796
+ // Float bit access uses union-based type punning (MISRA 21.15 compliant)
8797
+ expect(code).toContain("union { float f; uint32_t u; }");
8798
+ expect(code).not.toContain("memcpy");
8798
8799
  });
8799
8800
  });
8800
8801
 
@@ -10750,7 +10751,7 @@ describe("CodeGenerator", () => {
10750
10751
  });
10751
10752
 
10752
10753
  describe("float bit range access helpers", () => {
10753
- it("should generate float bit range read with memcpy", () => {
10754
+ it("should generate float bit range read with union", () => {
10754
10755
  const source = `
10755
10756
  void test() {
10756
10757
  f32 value <- 3.14;
@@ -10767,11 +10768,12 @@ describe("CodeGenerator", () => {
10767
10768
  sourcePath: "test.cnx",
10768
10769
  });
10769
10770
 
10770
- // Float bit access uses memcpy for type punning
10771
- expect(code).toContain("memcpy");
10771
+ // Float bit access uses union-based type punning (MISRA 21.15 compliant)
10772
+ expect(code).toContain("union { float f; uint32_t u; }");
10773
+ expect(code).not.toContain("memcpy");
10772
10774
  });
10773
10775
 
10774
- it("should generate f64 bit range read", () => {
10776
+ it("should generate f64 bit range read with union", () => {
10775
10777
  const source = `
10776
10778
  void test() {
10777
10779
  f64 value <- 3.14159;
@@ -10788,7 +10790,9 @@ describe("CodeGenerator", () => {
10788
10790
  sourcePath: "test.cnx",
10789
10791
  });
10790
10792
 
10791
- expect(code).toContain("memcpy");
10793
+ // f64 uses double/uint64_t union
10794
+ expect(code).toContain("union { double f; uint64_t u; }");
10795
+ expect(code).not.toContain("memcpy");
10792
10796
  });
10793
10797
  });
10794
10798
 
@@ -12264,7 +12268,7 @@ describe("CodeGenerator", () => {
12264
12268
  expect(code).toContain("flags");
12265
12269
  });
12266
12270
 
12267
- it("should handle float bit punning", () => {
12271
+ it("should handle float bit punning with union", () => {
12268
12272
  const source = `
12269
12273
  void test() {
12270
12274
  f32 val <- 3.14;
@@ -12281,8 +12285,9 @@ describe("CodeGenerator", () => {
12281
12285
  sourcePath: "test.cnx",
12282
12286
  });
12283
12287
 
12284
- // Float bit access uses memcpy for type punning
12285
- expect(code).toContain("memcpy");
12288
+ // Float bit access uses union-based type punning (MISRA 21.15 compliant)
12289
+ expect(code).toContain("union { float f; uint32_t u; }");
12290
+ expect(code).not.toContain("memcpy");
12286
12291
  });
12287
12292
  });
12288
12293
 
@@ -12870,7 +12875,7 @@ describe("CodeGenerator", () => {
12870
12875
 
12871
12876
  describe("refactored helper methods", () => {
12872
12877
  describe("float bit range access (PR #715 coverage)", () => {
12873
- it("should generate f32 bit range read with shadow and memcpy", () => {
12878
+ it("should generate f32 bit range read with union shadow", () => {
12874
12879
  const source = `
12875
12880
  void test() {
12876
12881
  f32 floatVal <- 1.5;
@@ -12887,13 +12892,13 @@ describe("CodeGenerator", () => {
12887
12892
  sourcePath: "test.cnx",
12888
12893
  });
12889
12894
 
12890
- // Should use shadow variable pattern for float bit access
12895
+ // Should use union-based type punning (MISRA 21.15 compliant)
12891
12896
  expect(code).toContain("__bits_floatVal");
12892
- expect(code).toContain("memcpy");
12893
- expect(code).toContain("uint32_t");
12897
+ expect(code).toContain("union { float f; uint32_t u; }");
12898
+ expect(code).not.toContain("memcpy");
12894
12899
  });
12895
12900
 
12896
- it("should generate f64 bit range read with 64-bit shadow", () => {
12901
+ it("should generate f64 bit range read with 64-bit union shadow", () => {
12897
12902
  const source = `
12898
12903
  void test() {
12899
12904
  f64 doubleVal <- 2.718;
@@ -12910,10 +12915,10 @@ describe("CodeGenerator", () => {
12910
12915
  sourcePath: "test.cnx",
12911
12916
  });
12912
12917
 
12913
- // Should use 64-bit shadow for f64
12918
+ // Should use 64-bit union for f64 (MISRA 21.15 compliant)
12914
12919
  expect(code).toContain("__bits_doubleVal");
12915
- expect(code).toContain("uint64_t");
12916
- expect(code).toContain("memcpy");
12920
+ expect(code).toContain("union { double f; uint64_t u; }");
12921
+ expect(code).not.toContain("memcpy");
12917
12922
  });
12918
12923
 
12919
12924
  it("should reuse shadow variable for repeated float bit reads", () => {
@@ -12934,11 +12939,13 @@ describe("CodeGenerator", () => {
12934
12939
  sourcePath: "test.cnx",
12935
12940
  });
12936
12941
 
12937
- // Shadow variable should only be declared once
12938
- const shadowDecls = (code.match(/uint32_t __bits_val;/g) || []).length;
12942
+ // Union shadow should only be declared once
12943
+ const shadowDecls = (
12944
+ code.match(/union { float f; uint32_t u; } __bits_val;/g) || []
12945
+ ).length;
12939
12946
  expect(shadowDecls).toBe(1);
12940
- // But memcpy may be used only on first access
12941
- expect(code).toContain("__bits_val");
12947
+ // Uses union member .u for bit access
12948
+ expect(code).toContain("__bits_val.u");
12942
12949
  });
12943
12950
 
12944
12951
  it("should generate integer bit range with start=0 optimization", () => {
@@ -13617,7 +13624,7 @@ describe("CodeGenerator", () => {
13617
13624
  });
13618
13625
 
13619
13626
  describe("float bit range edge cases", () => {
13620
- it("should generate f32 bit range with non-zero start position", () => {
13627
+ it("should generate f32 bit range with non-zero start position using union", () => {
13621
13628
  const source = `
13622
13629
  void test() {
13623
13630
  f32 value <- 3.14;
@@ -13636,7 +13643,9 @@ describe("CodeGenerator", () => {
13636
13643
 
13637
13644
  expect(code).toContain("__bits_value");
13638
13645
  expect(code).toContain(">> 8");
13639
- expect(code).toContain("memcpy");
13646
+ // Uses union, not memcpy (MISRA 21.15 compliant)
13647
+ expect(code).toContain("union { float f; uint32_t u; }");
13648
+ expect(code).not.toContain("memcpy");
13640
13649
  });
13641
13650
 
13642
13651
  it("should generate f32 bit range with start=0", () => {
@@ -13661,7 +13670,7 @@ describe("CodeGenerator", () => {
13661
13670
  expect(code).toContain(">> 0U");
13662
13671
  });
13663
13672
 
13664
- it("should handle multiple reads from same float reusing shadow", () => {
13673
+ it("should handle multiple reads from same float reusing union shadow", () => {
13665
13674
  const source = `
13666
13675
  void test() {
13667
13676
  f32 data <- 2.5;
@@ -13680,10 +13689,10 @@ describe("CodeGenerator", () => {
13680
13689
  sourcePath: "test.cnx",
13681
13690
  });
13682
13691
 
13683
- // Shadow declaration should appear once
13684
- expect(code).toContain("uint32_t __bits_data");
13685
- // Multiple reads should work
13686
- expect(code).toContain("__bits_data");
13692
+ // Union shadow declaration should appear once
13693
+ expect(code).toContain("union { float f; uint32_t u; } __bits_data;");
13694
+ // Multiple reads should work via union member
13695
+ expect(code).toContain("__bits_data.u");
13687
13696
  });
13688
13697
  });
13689
13698
 
@@ -15406,7 +15415,7 @@ describe("CodeGenerator", () => {
15406
15415
  expect(code).toContain("& 0xFF");
15407
15416
  });
15408
15417
 
15409
- it("should generate float bit range access with memcpy", () => {
15418
+ it("should generate float bit range access with union", () => {
15410
15419
  const source = `
15411
15420
  void test() {
15412
15421
  f32 fval <- 1.5;
@@ -15423,11 +15432,13 @@ describe("CodeGenerator", () => {
15423
15432
  sourcePath: "test.cnx",
15424
15433
  });
15425
15434
 
15426
- expect(code).toContain("memcpy");
15427
- expect(code).toContain("__bits_fval");
15435
+ // Uses union, not memcpy (MISRA 21.15 compliant)
15436
+ expect(code).toContain("union { float f; uint32_t u; } __bits_fval;");
15437
+ expect(code).toContain("__bits_fval.u");
15438
+ expect(code).not.toContain("memcpy");
15428
15439
  });
15429
15440
 
15430
- it("should generate f64 bit range access with uint64_t shadow", () => {
15441
+ it("should generate f64 bit range access with uint64_t union shadow", () => {
15431
15442
  const source = `
15432
15443
  void test() {
15433
15444
  f64 dval <- 1.5;
@@ -15444,8 +15455,10 @@ describe("CodeGenerator", () => {
15444
15455
  sourcePath: "test.cnx",
15445
15456
  });
15446
15457
 
15447
- expect(code).toContain("uint64_t __bits_dval");
15448
- expect(code).toContain("memcpy");
15458
+ // Uses union, not memcpy (MISRA 21.15 compliant)
15459
+ expect(code).toContain("union { double f; uint64_t u; } __bits_dval;");
15460
+ expect(code).toContain("__bits_dval.u");
15461
+ expect(code).not.toContain("memcpy");
15449
15462
  });
15450
15463
  });
15451
15464
  });
@@ -312,7 +312,9 @@ describe("ExpressionWalker - const inference integration", () => {
312
312
  }
313
313
  `;
314
314
  const transpiler = new Transpiler({ inputs: [] });
315
- const transpileResult = await transpiler.transpileSource(source);
315
+ const transpileResult = (
316
+ await transpiler.transpile({ kind: "source", source: source })
317
+ ).files[0];
316
318
  expect(transpileResult.success).toBe(true);
317
319
  // The caller function should NOT have const param because x is passed to modifyParam
318
320
  // which modifies its parameter
@@ -340,7 +342,9 @@ describe("ExpressionWalker - const inference integration", () => {
340
342
  }
341
343
  `;
342
344
  const transpiler = new Transpiler({ inputs: [] });
343
- const transpileResult = await transpiler.transpileSource(source);
345
+ const transpileResult = (
346
+ await transpiler.transpile({ kind: "source", source: source })
347
+ ).files[0];
344
348
  expect(transpileResult.success).toBe(true);
345
349
  // x should not be const because it's passed through a modifying call chain
346
350
  expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");
@@ -367,7 +371,9 @@ describe("ExpressionWalker - const inference integration", () => {
367
371
  }
368
372
  `;
369
373
  const transpiler = new Transpiler({ inputs: [] });
370
- const transpileResult = await transpiler.transpileSource(source);
374
+ const transpileResult = (
375
+ await transpiler.transpile({ kind: "source", source: source })
376
+ ).files[0];
371
377
  expect(transpileResult.success).toBe(true);
372
378
  // x should not be const
373
379
  expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");