c-next 0.2.2 → 0.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "packageManager": "npm@11.9.0",
6
6
  "type": "module",
@@ -11,6 +11,9 @@ import type {
11
11
  DeclarationSpecifiersContext,
12
12
  StructOrUnionSpecifierContext,
13
13
  EnumSpecifierContext,
14
+ StructDeclarationListContext,
15
+ StructDeclarationContext,
16
+ StructDeclaratorContext,
14
17
  } from "../../../parser/c/grammar/CParser";
15
18
  import SymbolUtils from "../../SymbolUtils";
16
19
  import IExtractedParameter from "../../shared/IExtractedParameter";
@@ -227,8 +230,8 @@ class DeclaratorUtils {
227
230
  // Use just the struct/union name, not "structName" concatenated
228
231
  parts.push(identifier.getText());
229
232
  } else {
230
- // Anonymous struct - use full text
231
- parts.push(typeSpec.getText());
233
+ // Anonymous struct - reconstruct with proper spacing
234
+ parts.push(DeclaratorUtils.reconstructAnonymousStruct(structSpec));
232
235
  }
233
236
  } else {
234
237
  parts.push(typeSpec.getText());
@@ -246,6 +249,100 @@ class DeclaratorUtils {
246
249
  return parts.join(" ") || "int";
247
250
  }
248
251
 
252
+ /**
253
+ * Reconstruct an anonymous struct/union type with proper spacing.
254
+ * For `struct { unsigned int flag_a: 1; }`, returns the properly formatted string
255
+ * instead of the concatenated tokens from getText().
256
+ */
257
+ private static reconstructAnonymousStruct(
258
+ structSpec: StructOrUnionSpecifierContext,
259
+ ): string {
260
+ const structOrUnion = structSpec.structOrUnion();
261
+ const keyword = structOrUnion.Struct() ? "struct" : "union";
262
+
263
+ const declList = structSpec.structDeclarationList();
264
+ if (!declList) {
265
+ return `${keyword} { }`;
266
+ }
267
+
268
+ const fields = DeclaratorUtils.reconstructStructFields(declList);
269
+ return `${keyword} { ${fields} }`;
270
+ }
271
+
272
+ /**
273
+ * Reconstruct struct fields with proper spacing.
274
+ */
275
+ private static reconstructStructFields(
276
+ declList: StructDeclarationListContext,
277
+ ): string {
278
+ const fieldStrings: string[] = [];
279
+
280
+ for (const decl of declList.structDeclaration()) {
281
+ const fieldStr = DeclaratorUtils.reconstructStructField(decl);
282
+ if (fieldStr) {
283
+ fieldStrings.push(fieldStr);
284
+ }
285
+ }
286
+
287
+ return fieldStrings.join(" ");
288
+ }
289
+
290
+ /**
291
+ * Reconstruct a single struct field declaration.
292
+ */
293
+ private static reconstructStructField(
294
+ decl: StructDeclarationContext,
295
+ ): string | null {
296
+ const specQualList = decl.specifierQualifierList();
297
+ if (!specQualList) return null;
298
+
299
+ // Get the base type with proper spacing
300
+ const baseType = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
301
+
302
+ const declaratorList = decl.structDeclaratorList();
303
+ if (!declaratorList) {
304
+ return `${baseType};`;
305
+ }
306
+
307
+ // Process each declarator in the list
308
+ const declarators: string[] = [];
309
+ for (const structDecl of declaratorList.structDeclarator()) {
310
+ const declStr = DeclaratorUtils.reconstructStructDeclarator(structDecl);
311
+ if (declStr) {
312
+ declarators.push(declStr);
313
+ }
314
+ }
315
+
316
+ if (declarators.length === 0) {
317
+ return `${baseType};`;
318
+ }
319
+
320
+ return `${baseType} ${declarators.join(", ")};`;
321
+ }
322
+
323
+ /**
324
+ * Reconstruct a struct declarator (field name with optional bitfield width).
325
+ */
326
+ private static reconstructStructDeclarator(
327
+ structDecl: StructDeclaratorContext,
328
+ ): string | null {
329
+ const declarator = structDecl.declarator();
330
+ const hasColon = structDecl.Colon() !== null;
331
+ const constExpr = structDecl.constantExpression();
332
+
333
+ let name = "";
334
+ if (declarator) {
335
+ name = DeclaratorUtils.extractDeclaratorName(declarator) || "";
336
+ }
337
+
338
+ if (hasColon && constExpr) {
339
+ const width = constExpr.getText();
340
+ return `${name}: ${width}`;
341
+ }
342
+
343
+ return name || null;
344
+ }
345
+
249
346
  /**
250
347
  * Extract typedef name from declaration specifiers.
251
348
  * For "typedef struct { ... } AppConfig;", this returns "AppConfig".
@@ -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
+ });
@@ -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
  });
@@ -133,7 +133,7 @@ describe("CodeGenerator requireInclude", () => {
133
133
  expect(result.code).toContain("sizeof(float)");
134
134
  });
135
135
 
136
- it("generates static assert for float bit indexing read", async () => {
136
+ it("generates static assert for float bit indexing read (no string.h)", async () => {
137
137
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
138
138
 
139
139
  const result = await transpiler.transpileSource(`
@@ -145,7 +145,9 @@ describe("CodeGenerator requireInclude", () => {
145
145
 
146
146
  expect(result.success).toBe(true);
147
147
  expect(result.code).toContain("_Static_assert");
148
- expect(result.code).toContain("#include <string.h>"); // For memcpy
148
+ // Uses union-based type punning, no memcpy needed (MISRA 21.15 compliant)
149
+ expect(result.code).not.toContain("#include <string.h>");
150
+ expect(result.code).toContain("union { float f; uint32_t u; }");
149
151
  });
150
152
  });
151
153
 
@@ -2051,7 +2051,15 @@ interface IFloatBitRangeContext {
2051
2051
  }
2052
2052
 
2053
2053
  /**
2054
- * Handle float bit range access with memcpy shadow variable.
2054
+ * Get the C float type name for a C-Next float type.
2055
+ */
2056
+ const getFloatTypeName = (baseType: string): string => {
2057
+ return baseType === "f64" ? "double" : "float";
2058
+ };
2059
+
2060
+ /**
2061
+ * Handle float bit range access with union-based type punning.
2062
+ * Uses union { float f; uint32_t u; } for MISRA C:2012 Rule 21.15 compliance.
2055
2063
  */
2056
2064
  const handleFloatBitRange = (
2057
2065
  ctx: IFloatBitRangeContext,
@@ -2065,35 +2073,36 @@ const handleFloatBitRange = (
2065
2073
  );
2066
2074
  }
2067
2075
 
2068
- effects.push(
2069
- { type: "include", header: "string" },
2070
- { type: "include", header: "float_static_assert" },
2071
- );
2076
+ effects.push({ type: "include", header: "float_static_assert" });
2072
2077
 
2073
2078
  const isF64 = ctx.baseType === "f64";
2074
- const shadowType = isF64 ? "uint64_t" : "uint32_t";
2079
+ const floatType = getFloatTypeName(ctx.baseType);
2080
+ const intType = isF64 ? "uint64_t" : "uint32_t";
2075
2081
  const shadowName = `__bits_${ctx.rootIdentifier}`;
2076
2082
  const mask = orchestrator.generateBitMask(ctx.width, isF64);
2077
2083
 
2078
2084
  const needsDeclaration = !orchestrator.hasFloatBitShadow(shadowName);
2079
2085
  if (needsDeclaration) {
2080
2086
  orchestrator.registerFloatBitShadow(shadowName);
2081
- orchestrator.addPendingTempDeclaration(`${shadowType} ${shadowName};`);
2087
+ // Emit union declaration: union { float f; uint32_t u; } __bits_name;
2088
+ orchestrator.addPendingTempDeclaration(
2089
+ `union { ${floatType} f; ${intType} u; } ${shadowName};`,
2090
+ );
2082
2091
  }
2083
2092
 
2084
2093
  const shadowIsCurrent = orchestrator.isFloatShadowCurrent(shadowName);
2085
2094
  orchestrator.markFloatShadowCurrent(shadowName);
2086
2095
 
2087
- if (shadowIsCurrent) {
2088
- if (ctx.start === "0") {
2089
- return `(${shadowName} & ${mask})`;
2090
- }
2091
- return `((${shadowName} >> ${ctx.start}) & ${mask})`;
2096
+ // If shadow is not current, emit assignment: __bits_name.f = floatVar;
2097
+ if (!shadowIsCurrent) {
2098
+ orchestrator.addPendingTempDeclaration(`${shadowName}.f = ${ctx.result};`);
2092
2099
  }
2100
+
2101
+ // Return just the bit read expression using union member .u
2093
2102
  if (ctx.start === "0") {
2094
- return `(memcpy(&${shadowName}, &${ctx.result}, sizeof(${ctx.result})), (${shadowName} & ${mask}))`;
2103
+ return `(${shadowName}.u & ${mask})`;
2095
2104
  }
2096
- return `(memcpy(&${shadowName}, &${ctx.result}, sizeof(${ctx.result})), ((${shadowName} >> ${ctx.start}) & ${mask}))`;
2105
+ return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
2097
2106
  };
2098
2107
 
2099
2108
  // ========================================================================
@@ -1698,7 +1698,7 @@ describe("PostfixExpressionGenerator", () => {
1698
1698
  });
1699
1699
 
1700
1700
  describe("float bit indexing", () => {
1701
- it("generates float bit range with memcpy", () => {
1701
+ it("generates float bit range with union-based type punning", () => {
1702
1702
  const typeRegistry = new Map<string, TTypeInfo>([
1703
1703
  [
1704
1704
  "f",
@@ -1726,9 +1726,11 @@ describe("PostfixExpressionGenerator", () => {
1726
1726
  });
1727
1727
 
1728
1728
  const result = generatePostfixExpression(ctx, input, state, orchestrator);
1729
- expect(result.code).toContain("memcpy");
1730
- expect(result.code).toContain("__bits_f");
1731
- expect(result.effects).toContainEqual({
1729
+ // Uses union member .u for bit access, not memcpy
1730
+ expect(result.code).toContain("__bits_f.u");
1731
+ expect(result.code).not.toContain("memcpy");
1732
+ // No string.h needed for MISRA 21.15 compliance
1733
+ expect(result.effects).not.toContainEqual({
1732
1734
  type: "include",
1733
1735
  header: "string",
1734
1736
  });
@@ -1767,7 +1769,7 @@ describe("PostfixExpressionGenerator", () => {
1767
1769
  ).toThrow("cannot be used at global scope");
1768
1770
  });
1769
1771
 
1770
- it("skips memcpy when shadow is current", () => {
1772
+ it("uses union member when shadow is current (no re-assignment)", () => {
1771
1773
  const typeRegistry = new Map<string, TTypeInfo>([
1772
1774
  [
1773
1775
  "f",
@@ -1796,7 +1798,8 @@ describe("PostfixExpressionGenerator", () => {
1796
1798
 
1797
1799
  const result = generatePostfixExpression(ctx, input, state, orchestrator);
1798
1800
  expect(result.code).not.toContain("memcpy");
1799
- expect(result.code).toBe("(__bits_f & 0xFF)");
1801
+ // Uses union member .u for bit access
1802
+ expect(result.code).toBe("(__bits_f.u & 0xFF)");
1800
1803
  });
1801
1804
  });
1802
1805
 
@@ -324,9 +324,10 @@ const generateSwitch = (
324
324
  effects.push(...caseResult.effects);
325
325
  }
326
326
 
327
- // Generate default if present
327
+ // Generate default case
328
328
  const defaultCtx = node.defaultCase();
329
329
  if (defaultCtx) {
330
+ // Explicit default from source
330
331
  const defaultResult = generateDefaultCase(
331
332
  defaultCtx,
332
333
  input,
@@ -335,6 +336,14 @@ const generateSwitch = (
335
336
  );
336
337
  lines.push(defaultResult.code);
337
338
  effects.push(...defaultResult.effects);
339
+ } else {
340
+ // Issue #855: MISRA C:2012 Rule 16.4 - every switch shall have a default
341
+ // Generate empty default case for compliance
342
+ lines.push(
343
+ orchestrator.indent("default: {"),
344
+ orchestrator.indent(orchestrator.indent("break;")),
345
+ orchestrator.indent("}"),
346
+ );
338
347
  }
339
348
 
340
349
  lines.push("}");
@@ -70,8 +70,11 @@ class ArrayAccessHelper {
70
70
  }
71
71
 
72
72
  /**
73
- * Generate float bit range read with shadow variable.
74
- * Uses memcpy pattern to safely reinterpret float bits.
73
+ * Generate float bit range read with union-based type punning.
74
+ * Uses union { float f; uint32_t u; } for MISRA 21.15 compliance.
75
+ *
76
+ * NOTE: This helper is not used in production (PostfixExpressionGenerator
77
+ * handles float bit access directly). It exists for testing and completeness.
75
78
  */
76
79
  static generateFloatBitRange(
77
80
  info: IArrayAccessInfo,
@@ -85,7 +88,7 @@ class ArrayAccessHelper {
85
88
  );
86
89
  }
87
90
 
88
- deps.requireInclude("string");
91
+ // No string.h needed - uses union-based type punning
89
92
  deps.requireInclude("float_static_assert");
90
93
 
91
94
  const isF64 = info.typeInfo?.baseType === "f64";