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.
Files changed (71) hide show
  1. package/dist/index.js +1019 -267
  2. package/dist/index.js.map +4 -4
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +101 -8
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +170 -0
  6. package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +246 -0
  7. package/src/transpiler/logic/IncludeExtractor.ts +12 -6
  8. package/src/transpiler/logic/__tests__/IncludeExtractor.test.ts +24 -0
  9. package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +12 -1
  10. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +172 -0
  11. package/src/transpiler/logic/symbols/SymbolTable.ts +84 -0
  12. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +43 -0
  13. package/src/transpiler/logic/symbols/__tests__/TransitiveEnumCollector.test.ts +1 -0
  14. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +98 -1
  15. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +23 -5
  16. package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +69 -2
  17. package/src/transpiler/logic/symbols/c/index.ts +85 -30
  18. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +18 -0
  19. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +90 -0
  20. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +40 -39
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +55 -25
  22. package/src/transpiler/output/codegen/TypeResolver.ts +14 -0
  23. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
  24. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +35 -30
  25. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +7 -7
  26. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +1 -0
  27. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +27 -4
  28. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +6 -0
  29. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +73 -0
  30. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +4 -0
  31. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +92 -0
  32. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +5 -2
  33. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +1 -0
  34. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +14 -0
  35. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +25 -3
  36. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +49 -0
  37. package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +17 -5
  38. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +26 -6
  39. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +31 -2
  40. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +72 -13
  41. package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +25 -1
  42. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +2 -1
  43. package/src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts +2 -17
  44. package/src/transpiler/output/codegen/generators/support/IncludeGenerator.ts +19 -7
  45. package/src/transpiler/output/codegen/generators/support/__tests__/IncludeGenerator.test.ts +68 -0
  46. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +14 -2
  47. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +3 -0
  48. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +3 -5
  49. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +56 -8
  50. package/src/transpiler/output/codegen/helpers/BitRangeHelper.ts +19 -3
  51. package/src/transpiler/output/codegen/helpers/NarrowingCastHelper.ts +191 -0
  52. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +35 -5
  53. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +131 -1
  54. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +61 -2
  55. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +1 -0
  56. package/src/transpiler/output/codegen/helpers/__tests__/BitRangeHelper.test.ts +53 -2
  57. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +1 -0
  58. package/src/transpiler/output/codegen/helpers/__tests__/NarrowingCastHelper.test.ts +159 -0
  59. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +1 -0
  60. package/src/transpiler/output/codegen/types/COMPOUND_TO_BINARY.ts +21 -0
  61. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +2 -0
  62. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +14 -16
  63. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +25 -0
  64. package/src/transpiler/state/CodeGenState.ts +89 -0
  65. package/src/transpiler/state/__tests__/CodeGenState.test.ts +53 -0
  66. package/src/transpiler/state/__tests__/TranspilerState.test.ts +1 -0
  67. package/src/transpiler/types/ICachedFileEntry.ts +2 -0
  68. package/src/transpiler/types/ICodeGenSymbols.ts +8 -0
  69. package/src/utils/BitUtils.ts +33 -4
  70. package/src/utils/cache/CacheManager.ts +9 -1
  71. 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
- expect(code).toContain("int32_t c = -2147483648");
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
- expect(code).toContain("counter += 1");
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
- it("should generate bitwise AND compound assignment", () => {
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[0])");
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(42)");
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 with U suffix
9142
- expect(code).toContain("((flags >> 0U) & 1)");
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 << 1) - 1))");
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 element access doesn't get U suffix yet
10732
- expect(code).toContain("arr[5]");
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 and mask
12997
+ // Bit range access generates mask (no shift for start=0)
12994
12998
  expect(code).toContain("flags");
12995
12999
  expect(code).toContain("& 0xFF");
12996
- // MISRA Rule 7.2: shift amount gets U suffix
12997
- expect(code).toContain(">> 0U");
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 + 5)");
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
- // MISRA Rule 7.2: shift amount gets U suffix
13695
- expect(code).toContain(">> 0U");
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
- // MISRA Rule 7.2: shift amount gets U suffix
14677
- expect(code).toContain(">> 0U");
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 just use += without helper
15328
- expect(code).toContain("+=");
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 null for unsuffixed integer", () => {
1135
- expect(TypeResolver.getLiteralType(mockLiteral("42"))).toBeNull();
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 null for unsuffixed hex", () => {
1139
- expect(TypeResolver.getLiteralType(mockLiteral("0xFF"))).toBeNull();
1138
+ it("should return int for unsuffixed hex", () => {
1139
+ expect(TypeResolver.getLiteralType(mockLiteral("0xFF"))).toBe("int");
1140
1140
  });
1141
1141
 
1142
- it("should return null for unsuffixed float", () => {
1143
- expect(TypeResolver.getLiteralType(mockLiteral("3.14"))).toBeNull();
1142
+ it("should return f64 for unsuffixed float", () => {
1143
+ expect(TypeResolver.getLiteralType(mockLiteral("3.14"))).toBe("f64");
1144
1144
  });
1145
1145
  });
1146
1146
  });
@@ -122,6 +122,7 @@ describe("MemberChainAnalyzer", () => {
122
122
  scopePrivateConstValues: new Map(),
123
123
  functionReturnTypes: new Map(),
124
124
  getSingleFunctionForVariable: () => null,
125
+ opaqueTypes: new Set(),
125
126
  hasPublicSymbols: () => false,
126
127
  };
127
128
  }
@@ -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
- return AssignmentKind.THIS_ARRAY;
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
- // Also applies to scoped atomic variables with clamp behavior
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
 
@@ -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 5 handlers", () => {
68
- expect(bitAccessHandlers.length).toBe(5);
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
 
@@ -57,6 +57,7 @@ function createDefaultMockSymbols(): ICodeGenSymbols {
57
57
 
58
58
  // Methods
59
59
  getSingleFunctionForVariable: () => null,
60
+ opaqueTypes: new Set(),
60
61
  hasPublicSymbols: () => false,
61
62
  };
62
63
  }
@@ -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
- return ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
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
- const type = orchestrator.generateType(varDecl.type());
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
- decl += generateInitializer(varDecl, isArray, orchestrator);
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
  }