c-next 0.2.9 → 0.2.11

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 (61) hide show
  1. package/dist/index.js +941 -210
  2. package/dist/index.js.map +4 -4
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +102 -7
  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/analysis/ArrayIndexTypeAnalyzer.ts +12 -1
  8. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +172 -0
  9. package/src/transpiler/logic/symbols/SymbolTable.ts +150 -0
  10. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +98 -0
  11. package/src/transpiler/logic/symbols/__tests__/TransitiveEnumCollector.test.ts +1 -0
  12. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +98 -1
  13. package/src/transpiler/logic/symbols/c/__tests__/PointerTypedef.test.ts +47 -0
  14. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +23 -5
  15. package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +118 -8
  16. package/src/transpiler/logic/symbols/c/index.ts +107 -30
  17. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +35 -0
  18. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +90 -0
  19. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +40 -39
  20. package/src/transpiler/output/codegen/CodeGenerator.ts +32 -1
  21. package/src/transpiler/output/codegen/TypeResolver.ts +14 -0
  22. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +19 -14
  23. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +7 -7
  24. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +1 -0
  25. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +27 -4
  26. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +6 -0
  27. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +73 -0
  28. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +4 -0
  29. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +92 -0
  30. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +5 -2
  31. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +1 -0
  32. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +20 -0
  33. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +23 -2
  34. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +51 -0
  35. package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +17 -5
  36. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +5 -0
  37. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +16 -0
  38. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +41 -5
  39. package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +25 -1
  40. package/src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts +2 -17
  41. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +3 -0
  42. package/src/transpiler/output/codegen/helpers/BitRangeHelper.ts +19 -3
  43. package/src/transpiler/output/codegen/helpers/NarrowingCastHelper.ts +191 -0
  44. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +35 -4
  45. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +131 -1
  46. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +1 -0
  47. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +1 -0
  48. package/src/transpiler/output/codegen/helpers/__tests__/BitRangeHelper.test.ts +53 -2
  49. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +1 -0
  50. package/src/transpiler/output/codegen/helpers/__tests__/NarrowingCastHelper.test.ts +159 -0
  51. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +1 -0
  52. package/src/transpiler/output/codegen/types/COMPOUND_TO_BINARY.ts +21 -0
  53. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +2 -0
  54. package/src/transpiler/state/CodeGenState.ts +58 -0
  55. package/src/transpiler/state/__tests__/CodeGenState.test.ts +53 -0
  56. package/src/transpiler/state/__tests__/TranspilerState.test.ts +1 -0
  57. package/src/transpiler/types/ICachedFileEntry.ts +4 -0
  58. package/src/transpiler/types/ICodeGenSymbols.ts +8 -0
  59. package/src/utils/BitUtils.ts +33 -4
  60. package/src/utils/cache/CacheManager.ts +17 -1
  61. package/src/utils/cache/__tests__/CacheManager.test.ts +1 -1
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "c-next",
13
- version: "0.2.9",
13
+ version: "0.2.11",
14
14
  description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
15
15
  packageManager: "npm@11.9.0",
16
16
  type: "module",
@@ -109966,6 +109966,26 @@ var SymbolTable = class {
109966
109966
  * In C, they must be referred to as 'struct Name', not just 'Name'
109967
109967
  */
109968
109968
  needsStructKeyword = /* @__PURE__ */ new Set();
109969
+ /**
109970
+ * Issue #948: Track typedef names that alias incomplete (forward-declared) struct types.
109971
+ * These are "opaque" types that can only be used as pointers.
109972
+ */
109973
+ opaqueTypes = /* @__PURE__ */ new Set();
109974
+ /**
109975
+ * Issue #958: Track typedef struct type names with their source files.
109976
+ * Maps typeName -> sourceFile. Used for scope variables which should be pointers
109977
+ * when the struct definition comes from a different file than the typedef.
109978
+ * If definition is in the same file as typedef, the entry is removed (value type).
109979
+ * If definition is in a different file, the entry remains (pointer type).
109980
+ */
109981
+ typedefStructTypes = /* @__PURE__ */ new Map();
109982
+ /**
109983
+ * Issue #948: Track struct tag -> typedef name relationships.
109984
+ * When a typedef declares an alias for a struct tag (e.g., typedef struct _foo foo_t),
109985
+ * we record structTagAliases["_foo"] = "foo_t". This allows us to unmark the typedef
109986
+ * as opaque when the full struct definition is later found.
109987
+ */
109988
+ structTagAliases = /* @__PURE__ */ new Map();
109969
109989
  /**
109970
109990
  * Issue #208: Track enum backing type bit widths
109971
109991
  * C++14 typed enums: enum Name : uint8_t { ... } have explicit bit widths
@@ -110594,6 +110614,116 @@ Rename the C-Next symbol to resolve.`
110594
110614
  }
110595
110615
  }
110596
110616
  // ========================================================================
110617
+ // Opaque Type Tracking (Issue #948)
110618
+ // ========================================================================
110619
+ /**
110620
+ * Issue #948: Mark a typedef as aliasing an opaque (forward-declared) struct type.
110621
+ * @param typeName Typedef name (e.g., "widget_t")
110622
+ */
110623
+ markOpaqueType(typeName) {
110624
+ this.opaqueTypes.add(typeName);
110625
+ }
110626
+ /**
110627
+ * Issue #948: Unmark a typedef when full struct definition is found.
110628
+ * Handles edge case: typedef before definition.
110629
+ * @param typeName Typedef name
110630
+ */
110631
+ unmarkOpaqueType(typeName) {
110632
+ this.opaqueTypes.delete(typeName);
110633
+ }
110634
+ /**
110635
+ * Issue #948: Check if a typedef aliases an opaque struct type.
110636
+ * @param typeName Typedef name
110637
+ * @returns true if the type is opaque (forward-declared)
110638
+ */
110639
+ isOpaqueType(typeName) {
110640
+ return this.opaqueTypes.has(typeName);
110641
+ }
110642
+ /**
110643
+ * Issue #948: Get all opaque type names for cache serialization.
110644
+ * @returns Array of opaque typedef names
110645
+ */
110646
+ getAllOpaqueTypes() {
110647
+ return Array.from(this.opaqueTypes);
110648
+ }
110649
+ /**
110650
+ * Issue #948: Restore opaque types from cache.
110651
+ * @param typeNames Array of opaque typedef names
110652
+ */
110653
+ restoreOpaqueTypes(typeNames) {
110654
+ for (const name of typeNames) {
110655
+ this.opaqueTypes.add(name);
110656
+ }
110657
+ }
110658
+ /**
110659
+ * Issue #948: Register a struct tag -> typedef name relationship.
110660
+ * Called when processing: typedef struct _foo foo_t;
110661
+ * This allows unmarking foo_t when struct _foo { ... } is later defined.
110662
+ * @param structTag The struct tag name (e.g., "_foo")
110663
+ * @param typedefName The typedef alias name (e.g., "foo_t")
110664
+ */
110665
+ registerStructTagAlias(structTag, typedefName) {
110666
+ this.structTagAliases.set(structTag, typedefName);
110667
+ }
110668
+ /**
110669
+ * Issue #948: Get the typedef alias for a struct tag, if any.
110670
+ * @param structTag The struct tag name
110671
+ * @returns The typedef alias name, or undefined if none registered
110672
+ */
110673
+ getStructTagAlias(structTag) {
110674
+ return this.structTagAliases.get(structTag);
110675
+ }
110676
+ // ========================================================================
110677
+ // Issue #958: Typedef Struct Type Tracking
110678
+ // ========================================================================
110679
+ /**
110680
+ * Issue #958: Mark a typedef as aliasing a struct type.
110681
+ * Records the source file to enable same-file vs cross-file distinction.
110682
+ * @param typedefName The typedef name (e.g., "widget_t")
110683
+ * @param sourceFile The file where the typedef was declared
110684
+ */
110685
+ markTypedefStructType(typedefName, sourceFile) {
110686
+ this.typedefStructTypes.set(typedefName, sourceFile);
110687
+ }
110688
+ /**
110689
+ * Issue #958: Unmark a typedef struct type when full definition is found.
110690
+ * Only unmarks if the definition is in the SAME file as the typedef declaration.
110691
+ * Cross-file definitions keep the typedef marked (pointer semantics).
110692
+ * @param typeName The typedef name to unmark
110693
+ * @param sourceFile The file where the definition was found
110694
+ */
110695
+ unmarkTypedefStructType(typeName, sourceFile) {
110696
+ const typedefFile = this.typedefStructTypes.get(typeName);
110697
+ if (typedefFile === sourceFile) {
110698
+ this.typedefStructTypes.delete(typeName);
110699
+ }
110700
+ }
110701
+ /**
110702
+ * Issue #958: Check if a typedef aliases a struct type.
110703
+ * Used for scope variables which should be pointers for external struct types.
110704
+ * @param typeName The type name to check
110705
+ * @returns true if this is a typedef'd struct type from C headers
110706
+ */
110707
+ isTypedefStructType(typeName) {
110708
+ return this.typedefStructTypes.has(typeName);
110709
+ }
110710
+ /**
110711
+ * Issue #958: Get all typedef struct types for cache serialization.
110712
+ * @returns Map entries as [typeName, sourceFile] pairs
110713
+ */
110714
+ getAllTypedefStructTypes() {
110715
+ return Array.from(this.typedefStructTypes.entries());
110716
+ }
110717
+ /**
110718
+ * Issue #958: Restore typedef struct types from cache.
110719
+ * @param entries Array of [typeName, sourceFile] pairs
110720
+ */
110721
+ restoreTypedefStructTypes(entries) {
110722
+ for (const [name, sourceFile] of entries) {
110723
+ this.typedefStructTypes.set(name, sourceFile);
110724
+ }
110725
+ }
110726
+ // ========================================================================
110597
110727
  // Enum Bit Width Tracking
110598
110728
  // ========================================================================
110599
110729
  /**
@@ -110710,7 +110840,10 @@ Rename the C-Next symbol to resolve.`
110710
110840
  this.cppSymbolsByFile.clear();
110711
110841
  this.structFields.clear();
110712
110842
  this.needsStructKeyword.clear();
110843
+ this.opaqueTypes.clear();
110844
+ this.structTagAliases.clear();
110713
110845
  this.enumBitWidth.clear();
110846
+ this.typedefStructTypes.clear();
110714
110847
  }
110715
110848
  };
110716
110849
  var SymbolTable_default = SymbolTable;
@@ -110871,6 +111004,16 @@ var CodeGenState = class {
110871
111004
  /** Issue #473: IRQ wrappers for critical sections */
110872
111005
  static needsIrqWrappers = false;
110873
111006
  // ===========================================================================
111007
+ // OPAQUE TYPE SCOPE VARIABLES (Issue #948)
111008
+ // ===========================================================================
111009
+ /**
111010
+ * Tracks scope variables with opaque (forward-declared) struct types.
111011
+ * These are generated as pointers with NULL initialization and should
111012
+ * be passed directly (not with &) since they're already pointers.
111013
+ * Maps qualified name (e.g., "MyScope_widget") to true.
111014
+ */
111015
+ static opaqueScopeVariables = /* @__PURE__ */ new Set();
111016
+ // ===========================================================================
110874
111017
  // C++ MODE STATE (Issue #250)
110875
111018
  // ===========================================================================
110876
111019
  /** Use temp vars instead of compound literals */
@@ -110954,6 +111097,7 @@ var CodeGenState = class {
110954
111097
  this.tempVarCounter = 0;
110955
111098
  this.pendingCppClassAssignments = [];
110956
111099
  this.selfIncludeAdded = false;
111100
+ this.opaqueScopeVariables = /* @__PURE__ */ new Set();
110957
111101
  this.sourcePath = null;
110958
111102
  this.includeDirs = [];
110959
111103
  this.inputs = [];
@@ -111043,6 +111187,22 @@ var CodeGenState = class {
111043
111187
  static isKnownRegister(name) {
111044
111188
  return this.symbols?.knownRegisters.has(name) ?? false;
111045
111189
  }
111190
+ /**
111191
+ * Issue #948: Check if a type name is an opaque (forward-declared) struct type.
111192
+ * Opaque types are incomplete types that can only be used as pointers.
111193
+ * Example: `typedef struct _widget_t widget_t;` without a body makes `widget_t` opaque.
111194
+ */
111195
+ static isOpaqueType(typeName) {
111196
+ return this.symbols?.opaqueTypes.has(typeName) ?? false;
111197
+ }
111198
+ /**
111199
+ * Issue #958: Check if a type name is an external typedef struct type.
111200
+ * External typedef struct types should use pointer semantics for scope variables.
111201
+ * Unlike isOpaqueType, this returns true for both forward-declared and complete structs.
111202
+ */
111203
+ static isTypedefStructType(typeName) {
111204
+ return this.symbolTable?.isTypedefStructType(typeName) ?? false;
111205
+ }
111046
111206
  /**
111047
111207
  * Get type info for a variable.
111048
111208
  * Checks local typeRegistry first, then falls back to SymbolTable
@@ -111482,6 +111642,28 @@ var CodeGenState = class {
111482
111642
  return this.floatShadowCurrent.has(name);
111483
111643
  }
111484
111644
  // ===========================================================================
111645
+ // OPAQUE SCOPE VARIABLE HELPERS (Issue #948)
111646
+ // ===========================================================================
111647
+ /**
111648
+ * Mark a scope variable as having an opaque (forward-declared) struct type.
111649
+ * These are generated as pointers with NULL initialization.
111650
+ *
111651
+ * @param qualifiedName - The fully qualified variable name (e.g., "MyScope_widget")
111652
+ */
111653
+ static markOpaqueScopeVariable(qualifiedName) {
111654
+ this.opaqueScopeVariables.add(qualifiedName);
111655
+ }
111656
+ /**
111657
+ * Check if a scope variable has an opaque type (and is thus a pointer).
111658
+ * Used during access generation to determine if & prefix is needed.
111659
+ *
111660
+ * @param qualifiedName - The fully qualified variable name (e.g., "MyScope_widget")
111661
+ * @returns true if this is an opaque scope variable (already a pointer)
111662
+ */
111663
+ static isOpaqueScopeVariable(qualifiedName) {
111664
+ return this.opaqueScopeVariables.has(qualifiedName);
111665
+ }
111666
+ // ===========================================================================
111485
111667
  // C++ MODE HELPERS
111486
111668
  // ===========================================================================
111487
111669
  /**
@@ -113191,6 +113373,12 @@ var TypeResolver2 = class _TypeResolver {
113191
113373
  if (floatMatch) {
113192
113374
  return "f" + floatMatch[1];
113193
113375
  }
113376
+ if (/^\d+$/.test(text) || /^0[xXbBoO][\da-fA-F]+$/.test(text)) {
113377
+ return "int";
113378
+ }
113379
+ if (/^\d*\.\d+([eE][+-]?\d+)?$/.test(text) || /^\d+[eE][+-]?\d+$/.test(text)) {
113380
+ return "f64";
113381
+ }
113194
113382
  return null;
113195
113383
  }
113196
113384
  /**
@@ -114450,6 +114638,229 @@ var GeneratorRegistry = class {
114450
114638
  }
114451
114639
  };
114452
114640
 
114641
+ // src/transpiler/output/codegen/helpers/CppModeHelper.ts
114642
+ var CppModeHelper = class {
114643
+ /**
114644
+ * Get address-of expression for struct parameter passing.
114645
+ * C mode: `&expr` (pass pointer to struct)
114646
+ * C++ mode: `expr` (pass reference directly)
114647
+ *
114648
+ * @param expr - The expression to potentially wrap
114649
+ * @returns The expression with address-of operator in C mode
114650
+ */
114651
+ static maybeAddressOf(expr) {
114652
+ return CodeGenState.cppMode ? expr : `&${expr}`;
114653
+ }
114654
+ /**
114655
+ * Get dereference expression for struct parameter access.
114656
+ * C mode: `(*expr)` (dereference pointer)
114657
+ * C++ mode: `expr` (reference can be used directly)
114658
+ *
114659
+ * @param expr - The expression to potentially dereference
114660
+ * @returns The expression with dereference in C mode
114661
+ */
114662
+ static maybeDereference(expr) {
114663
+ return CodeGenState.cppMode ? expr : `(*${expr})`;
114664
+ }
114665
+ /**
114666
+ * Get the type modifier for struct parameter declarations.
114667
+ * C mode: `*` (pointer type)
114668
+ * C++ mode: `&` (reference type)
114669
+ *
114670
+ * @returns The type modifier character
114671
+ */
114672
+ static refOrPtr() {
114673
+ return CodeGenState.cppMode ? "&" : "*";
114674
+ }
114675
+ /**
114676
+ * Get the member access separator for struct parameters.
114677
+ * C mode: `->` (pointer member access)
114678
+ * C++ mode: `.` (reference member access)
114679
+ *
114680
+ * @returns The member access separator
114681
+ */
114682
+ static memberSeparator() {
114683
+ return CodeGenState.cppMode ? "." : "->";
114684
+ }
114685
+ /**
114686
+ * Get NULL literal for the current mode.
114687
+ * C mode: `NULL`
114688
+ * C++ mode: `nullptr`
114689
+ *
114690
+ * @returns The null pointer literal
114691
+ */
114692
+ static nullLiteral() {
114693
+ return CodeGenState.cppMode ? "nullptr" : "NULL";
114694
+ }
114695
+ /**
114696
+ * Generate a cast expression for the current mode.
114697
+ * C mode: `(type)expr`
114698
+ * C++ mode: `static_cast<type>(expr)`
114699
+ *
114700
+ * @param type - The target type
114701
+ * @param expr - The expression to cast
114702
+ * @returns The cast expression
114703
+ */
114704
+ static cast(type, expr) {
114705
+ return CodeGenState.cppMode ? `static_cast<${type}>(${expr})` : `(${type})${expr}`;
114706
+ }
114707
+ /**
114708
+ * Generate a reinterpret cast expression for the current mode.
114709
+ * C mode: `(type)expr`
114710
+ * C++ mode: `reinterpret_cast<type>(expr)`
114711
+ *
114712
+ * @param type - The target type
114713
+ * @param expr - The expression to cast
114714
+ * @returns The cast expression
114715
+ */
114716
+ static reinterpretCast(type, expr) {
114717
+ return CodeGenState.cppMode ? `reinterpret_cast<${type}>(${expr})` : `(${type})${expr}`;
114718
+ }
114719
+ /**
114720
+ * Check if we're in C++ mode.
114721
+ *
114722
+ * @returns True if generating C++ code
114723
+ */
114724
+ static isCppMode() {
114725
+ return CodeGenState.cppMode;
114726
+ }
114727
+ };
114728
+ var CppModeHelper_default = CppModeHelper;
114729
+
114730
+ // src/transpiler/output/codegen/helpers/NarrowingCastHelper.ts
114731
+ var EXTENDED_TYPE_WIDTH = {
114732
+ ...TYPE_WIDTH_default,
114733
+ int: 32
114734
+ // C's int after promotion
114735
+ };
114736
+ var PROMOTED_TO_INT = /* @__PURE__ */ new Set(["u8", "i8", "u16", "i16", "bool"]);
114737
+ var FLOAT_TYPES2 = /* @__PURE__ */ new Set(["f32", "f64", "float", "double"]);
114738
+ var INTEGER_TYPES2 = /* @__PURE__ */ new Set([
114739
+ "u8",
114740
+ "u16",
114741
+ "u32",
114742
+ "u64",
114743
+ "i8",
114744
+ "i16",
114745
+ "i32",
114746
+ "i64",
114747
+ "uint8_t",
114748
+ "uint16_t",
114749
+ "uint32_t",
114750
+ "uint64_t",
114751
+ "int8_t",
114752
+ "int16_t",
114753
+ "int32_t",
114754
+ "int64_t",
114755
+ "int"
114756
+ ]);
114757
+ var NarrowingCastHelper = class _NarrowingCastHelper {
114758
+ /**
114759
+ * Check if a cast is needed for MISRA 10.3 compliance.
114760
+ * Returns true if:
114761
+ * - Source is wider than target (narrowing)
114762
+ * - Source and target are different essential type categories
114763
+ *
114764
+ * @param sourceType - Type of the expression (C-Next type or "int" for promoted)
114765
+ * @param targetType - Type of the target variable (C-Next type)
114766
+ */
114767
+ static needsCast(sourceType, targetType) {
114768
+ if (sourceType === targetType) {
114769
+ return false;
114770
+ }
114771
+ if (targetType === "bool" && sourceType !== "bool") {
114772
+ return true;
114773
+ }
114774
+ const sourceWidth = EXTENDED_TYPE_WIDTH[sourceType];
114775
+ const targetWidth = EXTENDED_TYPE_WIDTH[targetType];
114776
+ if (sourceWidth === void 0 || targetWidth === void 0) {
114777
+ return false;
114778
+ }
114779
+ return sourceWidth > targetWidth;
114780
+ }
114781
+ /**
114782
+ * Wrap expression with cast if needed for MISRA 10.3 compliance.
114783
+ *
114784
+ * @param expr - The generated C expression
114785
+ * @param sourceType - Type of the expression (C-Next type or "int")
114786
+ * @param targetType - Type of the assignment target (C-Next type)
114787
+ * @returns Expression with cast wrapper if needed, or original expression
114788
+ */
114789
+ static wrap(expr, sourceType, targetType) {
114790
+ if (!_NarrowingCastHelper.needsCast(sourceType, targetType)) {
114791
+ return expr;
114792
+ }
114793
+ if (targetType === "bool") {
114794
+ return `((${expr}) != 0U)`;
114795
+ }
114796
+ const cType = TYPE_MAP_default[targetType] ?? targetType;
114797
+ return CppModeHelper_default.cast(cType, expr);
114798
+ }
114799
+ /**
114800
+ * Determine the result type of C integer promotion for a given type.
114801
+ *
114802
+ * In C, operations on types smaller than int are promoted:
114803
+ * - u8, i8, u16, i16, bool -> int (32-bit)
114804
+ * - u32, i32, u64, i64 -> no promotion (already >= int width)
114805
+ *
114806
+ * @param baseType - The C-Next type of the operand
114807
+ * @returns "int" for promoted types, or the original type
114808
+ */
114809
+ static getPromotedType(baseType) {
114810
+ if (PROMOTED_TO_INT.has(baseType)) {
114811
+ return "int";
114812
+ }
114813
+ return baseType;
114814
+ }
114815
+ /**
114816
+ * Check if a type is a floating-point type.
114817
+ */
114818
+ static isFloatType(typeName) {
114819
+ return FLOAT_TYPES2.has(typeName);
114820
+ }
114821
+ /**
114822
+ * Check if a type is an integer type.
114823
+ */
114824
+ static isIntegerType(typeName) {
114825
+ return INTEGER_TYPES2.has(typeName);
114826
+ }
114827
+ /**
114828
+ * Check if conversion between source and target is a cross-type-category conversion.
114829
+ * MISRA 10.3 requires explicit casts for different essential type categories.
114830
+ */
114831
+ static isCrossTypeCategoryConversion(sourceType, targetType) {
114832
+ const sourceIsFloat = _NarrowingCastHelper.isFloatType(sourceType);
114833
+ const targetIsFloat = _NarrowingCastHelper.isFloatType(targetType);
114834
+ const sourceIsInt = _NarrowingCastHelper.isIntegerType(sourceType);
114835
+ const targetIsInt = _NarrowingCastHelper.isIntegerType(targetType);
114836
+ return sourceIsFloat && targetIsInt || sourceIsInt && targetIsFloat;
114837
+ }
114838
+ /**
114839
+ * Get the appropriate C float type for a C-Next type.
114840
+ */
114841
+ static getCFloatType(typeName) {
114842
+ if (typeName === "f32" || typeName === "float") {
114843
+ return "float";
114844
+ }
114845
+ if (typeName === "f64" || typeName === "double") {
114846
+ return "double";
114847
+ }
114848
+ return "double";
114849
+ }
114850
+ /**
114851
+ * Wrap int-to-float conversion with explicit cast for MISRA 10.3.
114852
+ *
114853
+ * @param expr - The integer expression
114854
+ * @param targetType - The float target type (f32/f64)
114855
+ * @returns Expression with cast
114856
+ */
114857
+ static wrapIntToFloat(expr, targetType) {
114858
+ const floatType = _NarrowingCastHelper.getCFloatType(targetType);
114859
+ return CppModeHelper_default.cast(floatType, expr);
114860
+ }
114861
+ };
114862
+ var NarrowingCastHelper_default = NarrowingCastHelper;
114863
+
114453
114864
  // src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts
114454
114865
  var UNSIGNED_64_TYPES = /* @__PURE__ */ new Set(["u64", "uint64_t"]);
114455
114866
  var UNSIGNED_TYPES3 = /* @__PURE__ */ new Set([
@@ -114490,6 +114901,18 @@ var generateLiteral = (node, _input, state, _orchestrator) => {
114490
114901
  effects.push({ type: "include", header: "stdbool" });
114491
114902
  return { code: literalText, effects };
114492
114903
  }
114904
+ if (literalText.startsWith("'")) {
114905
+ const expectedType2 = state?.expectedType;
114906
+ if (expectedType2) {
114907
+ const wrappedCode = NarrowingCastHelper_default.wrap(
114908
+ literalText,
114909
+ "int",
114910
+ expectedType2
114911
+ );
114912
+ return { code: wrappedCode, effects };
114913
+ }
114914
+ return { code: literalText, effects };
114915
+ }
114493
114916
  if (/[fF]32$/.test(literalText)) {
114494
114917
  literalText = literalText.replace(/[fF]32$/, "f");
114495
114918
  return { code: literalText, effects };
@@ -114851,96 +115274,9 @@ var binaryExprGenerators = {
114851
115274
  };
114852
115275
  var BinaryExprGenerator_default = binaryExprGenerators;
114853
115276
 
114854
- // src/transpiler/output/codegen/helpers/CppModeHelper.ts
114855
- var CppModeHelper = class {
114856
- /**
114857
- * Get address-of expression for struct parameter passing.
114858
- * C mode: `&expr` (pass pointer to struct)
114859
- * C++ mode: `expr` (pass reference directly)
114860
- *
114861
- * @param expr - The expression to potentially wrap
114862
- * @returns The expression with address-of operator in C mode
114863
- */
114864
- static maybeAddressOf(expr) {
114865
- return CodeGenState.cppMode ? expr : `&${expr}`;
114866
- }
114867
- /**
114868
- * Get dereference expression for struct parameter access.
114869
- * C mode: `(*expr)` (dereference pointer)
114870
- * C++ mode: `expr` (reference can be used directly)
114871
- *
114872
- * @param expr - The expression to potentially dereference
114873
- * @returns The expression with dereference in C mode
114874
- */
114875
- static maybeDereference(expr) {
114876
- return CodeGenState.cppMode ? expr : `(*${expr})`;
114877
- }
114878
- /**
114879
- * Get the type modifier for struct parameter declarations.
114880
- * C mode: `*` (pointer type)
114881
- * C++ mode: `&` (reference type)
114882
- *
114883
- * @returns The type modifier character
114884
- */
114885
- static refOrPtr() {
114886
- return CodeGenState.cppMode ? "&" : "*";
114887
- }
114888
- /**
114889
- * Get the member access separator for struct parameters.
114890
- * C mode: `->` (pointer member access)
114891
- * C++ mode: `.` (reference member access)
114892
- *
114893
- * @returns The member access separator
114894
- */
114895
- static memberSeparator() {
114896
- return CodeGenState.cppMode ? "." : "->";
114897
- }
114898
- /**
114899
- * Get NULL literal for the current mode.
114900
- * C mode: `NULL`
114901
- * C++ mode: `nullptr`
114902
- *
114903
- * @returns The null pointer literal
114904
- */
114905
- static nullLiteral() {
114906
- return CodeGenState.cppMode ? "nullptr" : "NULL";
114907
- }
114908
- /**
114909
- * Generate a cast expression for the current mode.
114910
- * C mode: `(type)expr`
114911
- * C++ mode: `static_cast<type>(expr)`
114912
- *
114913
- * @param type - The target type
114914
- * @param expr - The expression to cast
114915
- * @returns The cast expression
114916
- */
114917
- static cast(type, expr) {
114918
- return CodeGenState.cppMode ? `static_cast<${type}>(${expr})` : `(${type})${expr}`;
114919
- }
114920
- /**
114921
- * Generate a reinterpret cast expression for the current mode.
114922
- * C mode: `(type)expr`
114923
- * C++ mode: `reinterpret_cast<type>(expr)`
114924
- *
114925
- * @param type - The target type
114926
- * @param expr - The expression to cast
114927
- * @returns The cast expression
114928
- */
114929
- static reinterpretCast(type, expr) {
114930
- return CodeGenState.cppMode ? `reinterpret_cast<${type}>(${expr})` : `(${type})${expr}`;
114931
- }
114932
- /**
114933
- * Check if we're in C++ mode.
114934
- *
114935
- * @returns True if generating C++ code
114936
- */
114937
- static isCppMode() {
114938
- return CodeGenState.cppMode;
114939
- }
114940
- };
114941
- var CppModeHelper_default = CppModeHelper;
114942
-
114943
115277
  // src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts
115278
+ var INT32_MIN_LITERAL = "2147483648";
115279
+ var INT64_MIN_LITERAL = "9223372036854775808";
114944
115280
  var generateUnaryExpr = (node, _input, _state, orchestrator) => {
114945
115281
  if (node.postfixExpression()) {
114946
115282
  return {
@@ -114951,7 +115287,18 @@ var generateUnaryExpr = (node, _input, _state, orchestrator) => {
114951
115287
  const inner = orchestrator.generateUnaryExpr(node.unaryExpression());
114952
115288
  const text = node.getText();
114953
115289
  if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
114954
- if (text.startsWith("-")) return { code: `-${inner}`, effects: [] };
115290
+ if (text.startsWith("-")) {
115291
+ const effects = [];
115292
+ if (inner === INT32_MIN_LITERAL) {
115293
+ effects.push({ type: "include", header: "limits" });
115294
+ return { code: "(int32_t)INT32_MIN", effects };
115295
+ }
115296
+ if (inner === INT64_MIN_LITERAL || inner === INT64_MIN_LITERAL + "LL") {
115297
+ effects.push({ type: "include", header: "limits" });
115298
+ return { code: "(int64_t)INT64_MIN", effects };
115299
+ }
115300
+ return { code: `-${inner}`, effects };
115301
+ }
114955
115302
  if (text.startsWith("~")) {
114956
115303
  const innerType = TypeResolver_default2.getUnaryExpressionType(
114957
115304
  node.unaryExpression()
@@ -115014,15 +115361,18 @@ var generateSizeProperty = (typeInfo) => {
115014
115361
  throw new Error(`Error: .size is only available on string types`);
115015
115362
  };
115016
115363
  var generateBitmapFieldAccess = (result, fieldInfo) => {
115364
+ let expr;
115017
115365
  if (fieldInfo.width === 1) {
115018
- return { code: `((${result} >> ${fieldInfo.offset}) & 1)`, effects: [] };
115366
+ expr = `((${result} >> ${fieldInfo.offset}) & 1)`;
115019
115367
  } else {
115020
115368
  const mask = (1 << fieldInfo.width) - 1;
115021
- return {
115022
- code: `((${result} >> ${fieldInfo.offset}) & 0x${mask.toString(16).toUpperCase()})`,
115023
- effects: []
115024
- };
115369
+ expr = `((${result} >> ${fieldInfo.offset}) & 0x${mask.toString(16).toUpperCase()})`;
115370
+ }
115371
+ const targetType = CodeGenState.expectedType;
115372
+ if (targetType) {
115373
+ expr = NarrowingCastHelper_default.wrap(expr, "int", targetType);
115025
115374
  }
115375
+ return { code: expr, effects: [] };
115026
115376
  };
115027
115377
  var accessGenerators = {
115028
115378
  generateCapacityProperty,
@@ -115199,7 +115549,8 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115199
115549
  if (typeInfo?.isPointer) {
115200
115550
  isPointerVariable = true;
115201
115551
  }
115202
- const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115552
+ const isOpaqueScopeVar = CodeGenState.isOpaqueScopeVariable(argCode);
115553
+ const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && !isOpaqueScopeVar && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115203
115554
  const finalArgCode = needsAddressOf ? `&${argCode}` : argCode;
115204
115555
  return wrapWithCppEnumCast(
115205
115556
  finalArgCode,
@@ -115337,7 +115688,7 @@ var trackPassThroughModifications = (funcName, argExprs, orchestrator) => {
115337
115688
  var CallExprGenerator_default = generateFunctionCall;
115338
115689
 
115339
115690
  // src/utils/TypeCheckUtils.ts
115340
- var INTEGER_TYPES2 = [
115691
+ var INTEGER_TYPES3 = [
115341
115692
  "u8",
115342
115693
  "u16",
115343
115694
  "u32",
@@ -115349,7 +115700,7 @@ var INTEGER_TYPES2 = [
115349
115700
  ];
115350
115701
  var UNSIGNED_TYPES4 = ["u8", "u16", "u32", "u64"];
115351
115702
  var SIGNED_TYPES3 = ["i8", "i16", "i32", "i64"];
115352
- var FLOAT_TYPES2 = ["f32", "f64"];
115703
+ var FLOAT_TYPES3 = ["f32", "f64"];
115353
115704
  var STANDARD_WIDTHS = [8, 16, 32];
115354
115705
  var TypeCheckUtils = class {
115355
115706
  /**
@@ -115359,7 +115710,7 @@ var TypeCheckUtils = class {
115359
115710
  * @returns true if it's u8, u16, u32, u64, i8, i16, i32, or i64
115360
115711
  */
115361
115712
  static isInteger(typeName) {
115362
- return INTEGER_TYPES2.includes(typeName);
115713
+ return INTEGER_TYPES3.includes(typeName);
115363
115714
  }
115364
115715
  /**
115365
115716
  * Check if a type name is an unsigned integer type.
@@ -115386,7 +115737,7 @@ var TypeCheckUtils = class {
115386
115737
  * @returns true if it's f32 or f64
115387
115738
  */
115388
115739
  static isFloat(typeName) {
115389
- return FLOAT_TYPES2.includes(typeName);
115740
+ return FLOAT_TYPES3.includes(typeName);
115390
115741
  }
115391
115742
  /**
115392
115743
  * Check if a type name is a string type (string<N>).
@@ -116618,7 +116969,12 @@ var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116618
116969
  const isRegisterAccess = checkRegisterAccess(ctx, input);
116619
116970
  const identifierTypeInfo = getIdentifierTypeInfo(ctx, input);
116620
116971
  if (isRegisterAccess) {
116621
- output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116972
+ let expr2 = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116973
+ const targetType = CodeGenState.expectedType;
116974
+ if (targetType) {
116975
+ expr2 = NarrowingCastHelper_default.wrap(expr2, "int", targetType);
116976
+ }
116977
+ output.result = expr2;
116622
116978
  return output;
116623
116979
  }
116624
116980
  if (ctx.currentMemberIsArray) {
@@ -116632,7 +116988,12 @@ var handleSingleSubscript = (ctx, expr, input, orchestrator, output) => {
116632
116988
  }
116633
116989
  const isPrimitiveIntMember = ctx.currentStructType && TypeCheckUtils_default.isInteger(ctx.currentStructType);
116634
116990
  if (isPrimitiveIntMember) {
116635
- output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116991
+ let expr2 = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
116992
+ const targetType = CodeGenState.expectedType;
116993
+ if (targetType) {
116994
+ expr2 = NarrowingCastHelper_default.wrap(expr2, "int", targetType);
116995
+ }
116996
+ output.result = expr2;
116636
116997
  output.currentStructType = void 0;
116637
116998
  return output;
116638
116999
  }
@@ -116691,7 +117052,12 @@ var handleDefaultSubscript = (ctx, index, typeInfo, output) => {
116691
117052
  isRegisterAccess: false
116692
117053
  });
116693
117054
  if (subscriptKind === "bit_single") {
116694
- output.result = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
117055
+ let expr = index === "0" || index === "0U" ? `((${ctx.result}) & 1)` : `((${ctx.result} >> ${index}) & 1)`;
117056
+ const targetType = CodeGenState.expectedType;
117057
+ if (targetType) {
117058
+ expr = NarrowingCastHelper_default.wrap(expr, "int", targetType);
117059
+ }
117060
+ output.result = expr;
116695
117061
  } else {
116696
117062
  output.result = `${ctx.result}[${index}]`;
116697
117063
  }
@@ -116718,10 +117084,24 @@ var handleBitRangeSubscript = (ctx, exprs, input, state, orchestrator, effects,
116718
117084
  );
116719
117085
  } else {
116720
117086
  const mask = orchestrator.generateBitMask(width);
117087
+ let expr;
116721
117088
  if (start === "0" || start === "0U") {
116722
- output.result = `((${ctx.result}) & ${mask})`;
117089
+ expr = `((${ctx.result}) & ${mask})`;
117090
+ } else {
117091
+ expr = `((${ctx.result} >> ${start}) & ${mask})`;
117092
+ }
117093
+ const targetType = CodeGenState.expectedType;
117094
+ if (targetType && ctx.primaryTypeInfo?.baseType) {
117095
+ const promotedSourceType = NarrowingCastHelper_default.getPromotedType(
117096
+ ctx.primaryTypeInfo.baseType
117097
+ );
117098
+ output.result = NarrowingCastHelper_default.wrap(
117099
+ expr,
117100
+ promotedSourceType,
117101
+ targetType
117102
+ );
116723
117103
  } else {
116724
- output.result = `((${ctx.result} >> ${start}) & ${mask})`;
117104
+ output.result = expr;
116725
117105
  }
116726
117106
  }
116727
117107
  return output;
@@ -117031,6 +117411,21 @@ var generateCriticalStatement = (node, _input, _state, orchestrator) => {
117031
117411
  };
117032
117412
  var CriticalGenerator_default = generateCriticalStatement;
117033
117413
 
117414
+ // src/transpiler/output/codegen/types/COMPOUND_TO_BINARY.ts
117415
+ var COMPOUND_TO_BINARY = {
117416
+ "+=": "+",
117417
+ "-=": "-",
117418
+ "*=": "*",
117419
+ "/=": "/",
117420
+ "%=": "%",
117421
+ "&=": "&",
117422
+ "|=": "|",
117423
+ "^=": "^",
117424
+ "<<=": "<<",
117425
+ ">>=": ">>"
117426
+ };
117427
+ var COMPOUND_TO_BINARY_default = COMPOUND_TO_BINARY;
117428
+
117034
117429
  // src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts
117035
117430
  var TYPE_MAP2 = {
117036
117431
  u8: "uint8_t",
@@ -117058,18 +117453,6 @@ var STREX_MAP = {
117058
117453
  u32: "__STREXW",
117059
117454
  i32: "__STREXW"
117060
117455
  };
117061
- var SIMPLE_OP_MAP = {
117062
- "+=": "+",
117063
- "-=": "-",
117064
- "*=": "*",
117065
- "/=": "/",
117066
- "%=": "%",
117067
- "&=": "&",
117068
- "|=": "|",
117069
- "^=": "^",
117070
- "<<=": "<<",
117071
- ">>=": ">>"
117072
- };
117073
117456
  var CLAMP_OP_MAP = {
117074
117457
  "+=": "add",
117075
117458
  "-=": "sub",
@@ -117083,7 +117466,7 @@ function getClampHelperOp(cOp, typeInfo) {
117083
117466
  }
117084
117467
  function generateInnerAtomicOp(cOp, value, typeInfo) {
117085
117468
  const effects = [];
117086
- const simpleOp = SIMPLE_OP_MAP[cOp] || "+";
117469
+ const simpleOp = COMPOUND_TO_BINARY_default[cOp] || "+";
117087
117470
  const helperOp = getClampHelperOp(cOp, typeInfo);
117088
117471
  if (helperOp) {
117089
117472
  effects.push({
@@ -117748,8 +118131,14 @@ function generateRegularVariable(varDecl, varName, scopeName, isPrivate, orchest
117748
118131
  const arrayDims = varDecl.arrayDimension();
117749
118132
  const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
117750
118133
  const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
117751
- const type = orchestrator.generateType(varDecl.type());
118134
+ let type = orchestrator.generateType(varDecl.type());
117752
118135
  const fullName = QualifiedNameGenerator_default.forMember(scopeName, varName);
118136
+ const isOpaque = orchestrator.isOpaqueType(type);
118137
+ const isExternalStruct = orchestrator.isTypedefStructType(type);
118138
+ if (isOpaque || isExternalStruct) {
118139
+ type = `${type}*`;
118140
+ orchestrator.markOpaqueScopeVariable(fullName);
118141
+ }
117753
118142
  const constPrefix = isConst ? "const " : "";
117754
118143
  const prefix = isPrivate ? "static " : "";
117755
118144
  let decl = `${prefix}${constPrefix}${type} ${fullName}`;
@@ -117761,7 +118150,11 @@ function generateRegularVariable(varDecl, varName, scopeName, isPrivate, orchest
117761
118150
  decl += orchestrator.generateArrayDimensions(arrayDims);
117762
118151
  }
117763
118152
  decl += ArrayDimensionUtils_default.generateStringCapacityDim(varDecl.type());
117764
- decl += generateInitializer(varDecl, isArray, orchestrator);
118153
+ if (isOpaque || isExternalStruct) {
118154
+ decl += " = NULL";
118155
+ } else {
118156
+ decl += generateInitializer(varDecl, isArray, orchestrator);
118157
+ }
117765
118158
  return decl + ";";
117766
118159
  }
117767
118160
  function generateScopeFunction(funcDecl, scopeName, isPrivate, orchestrator) {
@@ -117973,6 +118366,7 @@ function generateScopedStructInline(node, scopeName, _input, orchestrator) {
117973
118366
  var ScopeGenerator_default = generateScope;
117974
118367
 
117975
118368
  // src/utils/BitUtils.ts
118369
+ var NARROW_TYPES = /* @__PURE__ */ new Set(["u8", "u16", "i8", "i16"]);
117976
118370
  var BitUtils = class _BitUtils {
117977
118371
  /**
117978
118372
  * Convert a boolean expression to an unsigned integer (0U or 1U).
@@ -118063,6 +118457,21 @@ var BitUtils = class _BitUtils {
118063
118457
  static formatHex(value) {
118064
118458
  return `0x${value.toString(16).toUpperCase()}U`;
118065
118459
  }
118460
+ /**
118461
+ * Wrap an expression with a cast for MISRA 10.3 compliance on narrow types.
118462
+ * Bit manipulation operations promote to int; this casts back to the target type.
118463
+ *
118464
+ * @param expr - The expression to potentially wrap
118465
+ * @param targetType - The C-Next type name (e.g., "u8", "u16")
118466
+ * @returns Expression wrapped with cast if narrow, or original expression
118467
+ */
118468
+ static wrapNarrowCast(expr, targetType) {
118469
+ if (!targetType || !NARROW_TYPES.has(targetType)) {
118470
+ return expr;
118471
+ }
118472
+ const cType = TYPE_MAP_default[targetType] ?? targetType;
118473
+ return `(${cType})(${expr})`;
118474
+ }
118066
118475
  /**
118067
118476
  * Generate code to read a single bit from a value.
118068
118477
  * Pattern: ((target >> offset) & 1)
@@ -118109,7 +118518,8 @@ var BitUtils = class _BitUtils {
118109
118518
  const is64Bit = targetType === "u64" || targetType === "i64";
118110
118519
  const one = is64Bit ? "1ULL" : "1U";
118111
118520
  const valueShift = is64Bit ? `((uint64_t)${intValue} << ${offset})` : `(${intValue} << ${offset})`;
118112
- return `${target} = (${target} & ~(${one} << ${offset})) | ${valueShift};`;
118521
+ const rhs = `(${target} & ~(${one} << ${offset})) | ${valueShift}`;
118522
+ return `${target} = ${_BitUtils.wrapNarrowCast(rhs, targetType)};`;
118113
118523
  }
118114
118524
  /**
118115
118525
  * Generate read-modify-write code for multi-bit assignment.
@@ -118124,7 +118534,8 @@ var BitUtils = class _BitUtils {
118124
118534
  */
118125
118535
  static multiBitWrite(target, offset, width, value, targetType) {
118126
118536
  const mask = _BitUtils.generateMask(width, targetType);
118127
- return `${target} = (${target} & ~(${mask} << ${offset})) | ((${value} & ${mask}) << ${offset});`;
118537
+ const rhs = `(${target} & ~(${mask} << ${offset})) | ((${value} & ${mask}) << ${offset})`;
118538
+ return `${target} = ${_BitUtils.wrapNarrowCast(rhs, targetType)};`;
118128
118539
  }
118129
118540
  /**
118130
118541
  * Generate write-only register code for single bit assignment.
@@ -118140,7 +118551,8 @@ var BitUtils = class _BitUtils {
118140
118551
  static writeOnlySingleBit(target, offset, value, targetType) {
118141
118552
  const intValue = _BitUtils.boolToInt(value);
118142
118553
  const castPrefix = targetType === "u64" || targetType === "i64" ? "(uint64_t)" : "";
118143
- return `${target} = (${castPrefix}${intValue} << ${offset});`;
118554
+ const rhs = `(${castPrefix}${intValue} << ${offset})`;
118555
+ return `${target} = ${_BitUtils.wrapNarrowCast(rhs, targetType)};`;
118144
118556
  }
118145
118557
  /**
118146
118558
  * Generate write-only register code for multi-bit assignment.
@@ -118156,7 +118568,8 @@ var BitUtils = class _BitUtils {
118156
118568
  */
118157
118569
  static writeOnlyMultiBit(target, offset, width, value, targetType) {
118158
118570
  const mask = _BitUtils.generateMask(width, targetType);
118159
- return `${target} = ((${value} & ${mask}) << ${offset});`;
118571
+ const rhs = `((${value} & ${mask}) << ${offset})`;
118572
+ return `${target} = ${_BitUtils.wrapNarrowCast(rhs, targetType)};`;
118160
118573
  }
118161
118574
  };
118162
118575
  var BitUtils_default = BitUtils;
@@ -120717,8 +121130,10 @@ var AssignmentKind = /* @__PURE__ */ ((AssignmentKind2) => {
120717
121130
  AssignmentKind2[AssignmentKind2["GLOBAL_REGISTER_BIT"] = 28] = "GLOBAL_REGISTER_BIT";
120718
121131
  AssignmentKind2[AssignmentKind2["THIS_MEMBER"] = 29] = "THIS_MEMBER";
120719
121132
  AssignmentKind2[AssignmentKind2["THIS_ARRAY"] = 30] = "THIS_ARRAY";
120720
- AssignmentKind2[AssignmentKind2["MEMBER_CHAIN"] = 31] = "MEMBER_CHAIN";
120721
- AssignmentKind2[AssignmentKind2["SIMPLE"] = 32] = "SIMPLE";
121133
+ AssignmentKind2[AssignmentKind2["THIS_BIT"] = 31] = "THIS_BIT";
121134
+ AssignmentKind2[AssignmentKind2["THIS_BIT_RANGE"] = 32] = "THIS_BIT_RANGE";
121135
+ AssignmentKind2[AssignmentKind2["MEMBER_CHAIN"] = 33] = "MEMBER_CHAIN";
121136
+ AssignmentKind2[AssignmentKind2["SIMPLE"] = 34] = "SIMPLE";
120722
121137
  return AssignmentKind2;
120723
121138
  })(AssignmentKind || {});
120724
121139
  var AssignmentKind_default = AssignmentKind;
@@ -120760,8 +121175,55 @@ var handlers_default = AssignmentHandlerRegistry;
120760
121175
  function gen() {
120761
121176
  return CodeGenState.generator;
120762
121177
  }
121178
+ function tryHandleCompoundNarrowingCast(ctx, target) {
121179
+ if (!ctx.isCompound || !ctx.firstIdTypeInfo) {
121180
+ return null;
121181
+ }
121182
+ const baseType = ctx.firstIdTypeInfo.baseType;
121183
+ const promotedType = NarrowingCastHelper_default.getPromotedType(baseType);
121184
+ if (promotedType !== "int" || baseType === "int") {
121185
+ return null;
121186
+ }
121187
+ const binaryOp = COMPOUND_TO_BINARY_default[ctx.cOp];
121188
+ if (!binaryOp) {
121189
+ return null;
121190
+ }
121191
+ const cType = TYPE_MAP_default[baseType] ?? baseType;
121192
+ const expr = `(${target} ${binaryOp} ${ctx.generatedValue})`;
121193
+ const castExpr = CppModeHelper_default.cast(cType, expr);
121194
+ return `${target} = ${castExpr};`;
121195
+ }
121196
+ function tryHandleIntToFloatConversion(ctx, target) {
121197
+ if (ctx.isCompound || !ctx.firstIdTypeInfo || !ctx.valueCtx) {
121198
+ return null;
121199
+ }
121200
+ const targetType = ctx.firstIdTypeInfo.baseType;
121201
+ const valueType = TypeResolver_default2.getExpressionType(ctx.valueCtx);
121202
+ if (!valueType) {
121203
+ return null;
121204
+ }
121205
+ if (!NarrowingCastHelper_default.isCrossTypeCategoryConversion(valueType, targetType)) {
121206
+ return null;
121207
+ }
121208
+ if (!NarrowingCastHelper_default.isIntegerType(valueType) || !NarrowingCastHelper_default.isFloatType(targetType)) {
121209
+ return null;
121210
+ }
121211
+ const castedValue = NarrowingCastHelper_default.wrapIntToFloat(
121212
+ ctx.generatedValue,
121213
+ targetType
121214
+ );
121215
+ return `${target} ${ctx.cOp} ${castedValue};`;
121216
+ }
120763
121217
  function handleSimpleAssignment(ctx) {
120764
121218
  const target = gen().generateAssignmentTarget(ctx.targetCtx);
121219
+ const compoundResult = tryHandleCompoundNarrowingCast(ctx, target);
121220
+ if (compoundResult) {
121221
+ return compoundResult;
121222
+ }
121223
+ const conversionResult = tryHandleIntToFloatConversion(ctx, target);
121224
+ if (conversionResult) {
121225
+ return conversionResult;
121226
+ }
120765
121227
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
120766
121228
  }
120767
121229
  var SimpleHandler_default = handleSimpleAssignment;
@@ -121414,6 +121876,8 @@ function handleStructChainBitRange(ctx) {
121414
121876
  var bitAccessHandlers = [
121415
121877
  [AssignmentKind_default.INTEGER_BIT, handleIntegerBit],
121416
121878
  [AssignmentKind_default.INTEGER_BIT_RANGE, handleIntegerBitRange],
121879
+ [AssignmentKind_default.THIS_BIT, handleIntegerBit],
121880
+ [AssignmentKind_default.THIS_BIT_RANGE, handleIntegerBitRange],
121417
121881
  [AssignmentKind_default.STRUCT_MEMBER_BIT, handleStructMemberBit],
121418
121882
  [AssignmentKind_default.ARRAY_ELEMENT_BIT, handleArrayElementBit],
121419
121883
  [AssignmentKind_default.STRUCT_CHAIN_BIT_RANGE, handleStructChainBitRange]
@@ -121965,14 +122429,28 @@ var AssignmentClassifier = class _AssignmentClassifier {
121965
122429
  return AssignmentKind_default.THIS_MEMBER;
121966
122430
  }
121967
122431
  /**
121968
- * Classify this.reg[bit] / this.arr[i] patterns with array access.
122432
+ * Classify this.reg[bit] / this.arr[i] / this.flags[3] patterns with array access.
122433
+ * Issue #954: Uses SubscriptClassifier to distinguish array vs bit access.
121969
122434
  */
121970
122435
  static classifyThisWithArrayAccess(ctx, scopedRegName) {
121971
122436
  if (CodeGenState.symbols.knownRegisters.has(scopedRegName)) {
121972
122437
  const hasBitRange = ctx.postfixOps.some((op) => op.COMMA() !== null);
121973
122438
  return hasBitRange ? AssignmentKind_default.SCOPED_REGISTER_BIT_RANGE : AssignmentKind_default.SCOPED_REGISTER_BIT;
121974
122439
  }
121975
- return AssignmentKind_default.THIS_ARRAY;
122440
+ const typeInfo = CodeGenState.getVariableTypeInfo(scopedRegName);
122441
+ const subscriptKind = SubscriptClassifier_default.classify({
122442
+ typeInfo: typeInfo ?? null,
122443
+ subscriptCount: ctx.lastSubscriptExprCount,
122444
+ isRegisterAccess: false
122445
+ });
122446
+ switch (subscriptKind) {
122447
+ case "bit_single":
122448
+ return AssignmentKind_default.THIS_BIT;
122449
+ case "bit_range":
122450
+ return AssignmentKind_default.THIS_BIT_RANGE;
122451
+ default:
122452
+ return AssignmentKind_default.THIS_ARRAY;
122453
+ }
121976
122454
  }
121977
122455
  /**
121978
122456
  * Classify simple array/bit access (no prefix, no member access).
@@ -122045,7 +122523,8 @@ var AssignmentClassifier = class _AssignmentClassifier {
122045
122523
  if (isGlobalAtomic) {
122046
122524
  return AssignmentKind_default.ATOMIC_RMW;
122047
122525
  }
122048
- if (typeInfo.overflowBehavior === "clamp" && TypeCheckUtils_default.isInteger(typeInfo.baseType)) {
122526
+ const ARITHMETIC_COMPOUND_OPS = /* @__PURE__ */ new Set(["+=", "-=", "*="]);
122527
+ if (typeInfo.overflowBehavior === "clamp" && TypeCheckUtils_default.isInteger(typeInfo.baseType) && ARITHMETIC_COMPOUND_OPS.has(ctx.cOp)) {
122049
122528
  return AssignmentKind_default.OVERFLOW_CLAMP;
122050
122529
  }
122051
122530
  return null;
@@ -124446,10 +124925,20 @@ ${assignments}`;
124446
124925
  _VariableDeclHelper.validateIntegerInitializer(ctx, typeName, {
124447
124926
  getExpressionType: callbacks.getExpressionType
124448
124927
  });
124449
- return CodeGenState.withExpectedType(
124450
- typeName,
124451
- () => `${decl} = ${callbacks.generateExpression(ctx.expression())}`
124452
- );
124928
+ return CodeGenState.withExpectedType(typeName, () => {
124929
+ let exprCode = callbacks.generateExpression(ctx.expression());
124930
+ const exprType = callbacks.getExpressionType(ctx.expression());
124931
+ if (exprType && NarrowingCastHelper_default.isCrossTypeCategoryConversion(exprType, typeName)) {
124932
+ if (NarrowingCastHelper_default.isIntegerType(exprType) && NarrowingCastHelper_default.isFloatType(typeName)) {
124933
+ exprCode = NarrowingCastHelper_default.wrapIntToFloat(exprCode, typeName);
124934
+ }
124935
+ if (NarrowingCastHelper_default.isFloatType(exprType) && NarrowingCastHelper_default.isIntegerType(typeName)) {
124936
+ const cType = TYPE_MAP_default[typeName] ?? typeName;
124937
+ exprCode = CppModeHelper_default.cast(cType, exprCode);
124938
+ }
124939
+ }
124940
+ return `${decl} = ${exprCode}`;
124941
+ });
124453
124942
  }
124454
124943
  // ========================================================================
124455
124944
  // Tier 4: Orchestrators (main entry points)
@@ -125310,7 +125799,7 @@ var TypeGenerationHelper_default = TypeGenerationHelper;
125310
125799
  var SIGNED_INTEGERS = /* @__PURE__ */ new Set(["i8", "i16", "i32", "i64"]);
125311
125800
  var UNSIGNED_INTEGERS = /* @__PURE__ */ new Set(["u8", "u16", "u32", "u64"]);
125312
125801
  var ALL_INTEGERS = /* @__PURE__ */ new Set([...SIGNED_INTEGERS, ...UNSIGNED_INTEGERS]);
125313
- var FLOAT_TYPES3 = /* @__PURE__ */ new Set(["f32", "f64"]);
125802
+ var FLOAT_TYPES4 = /* @__PURE__ */ new Set(["f32", "f64"]);
125314
125803
  var CastValidator = class _CastValidator {
125315
125804
  /**
125316
125805
  * Check if a type is an integer type.
@@ -125322,7 +125811,7 @@ var CastValidator = class _CastValidator {
125322
125811
  * Check if a type is a floating-point type.
125323
125812
  */
125324
125813
  static isFloatType(typeName) {
125325
- return FLOAT_TYPES3.has(typeName);
125814
+ return FLOAT_TYPES4.has(typeName);
125326
125815
  }
125327
125816
  /**
125328
125817
  * Check if a type is signed.
@@ -128368,6 +128857,30 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
128368
128857
  isFloatShadowCurrent(shadowName) {
128369
128858
  return CodeGenState.floatShadowCurrent.has(shadowName);
128370
128859
  }
128860
+ /**
128861
+ * Issue #948: Check if a type is an opaque (forward-declared) struct type.
128862
+ * Opaque types can only be used as pointers (cannot be instantiated).
128863
+ * Part of IOrchestrator interface.
128864
+ */
128865
+ isOpaqueType(typeName) {
128866
+ return CodeGenState.isOpaqueType(typeName);
128867
+ }
128868
+ /**
128869
+ * Issue #958: Check if a type is an external typedef struct type.
128870
+ * Used for scope variables which should always be pointers for external struct types.
128871
+ * Part of IOrchestrator interface.
128872
+ */
128873
+ isTypedefStructType(typeName) {
128874
+ return CodeGenState.isTypedefStructType(typeName);
128875
+ }
128876
+ /**
128877
+ * Issue #948: Mark a scope variable as having an opaque type.
128878
+ * These variables are generated as pointers with NULL initialization.
128879
+ * Part of IOrchestrator interface.
128880
+ */
128881
+ markOpaqueScopeVariable(qualifiedName) {
128882
+ CodeGenState.markOpaqueScopeVariable(qualifiedName);
128883
+ }
128371
128884
  // ===========================================================================
128372
128885
  // End IOrchestrator Implementation
128373
128886
  // ===========================================================================
@@ -130334,7 +130847,9 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
130334
130847
  const minComparison = minValue === "0" ? `0.0${floatSuffix}` : `((${floatCastType})${minValue})`;
130335
130848
  const maxComparison = `((${floatCastType})${maxValue})`;
130336
130849
  const finalCast = CppModeHelper_default.cast(targetType, `(${expr})`);
130337
- return `((${expr}) > ${maxComparison} ? ${maxValue} : (${expr}) < ${minComparison} ? ${minValue} : ${finalCast})`;
130850
+ const castMax = CppModeHelper_default.cast(targetType, maxValue);
130851
+ const castMin = minValue === "0" ? "0" : CppModeHelper_default.cast(targetType, minValue);
130852
+ return `((${expr}) > ${maxComparison} ? ${castMax} : (${expr}) < ${minComparison} ? ${castMin} : ${finalCast})`;
130338
130853
  }
130339
130854
  /**
130340
130855
  * ADR-023: Generate sizeof expression
@@ -132527,6 +133042,7 @@ var TSymbolInfoAdapter = class _TSymbolInfoAdapter {
132527
133042
  const registerMemberCTypes = /* @__PURE__ */ new Map();
132528
133043
  const scopePrivateConstValues = /* @__PURE__ */ new Map();
132529
133044
  const functionReturnTypes = /* @__PURE__ */ new Map();
133045
+ const opaqueTypes = /* @__PURE__ */ new Set();
132530
133046
  for (const symbol of symbols) {
132531
133047
  switch (symbol.kind) {
132532
133048
  case "struct":
@@ -132614,6 +133130,8 @@ var TSymbolInfoAdapter = class _TSymbolInfoAdapter {
132614
133130
  scopePrivateConstValues,
132615
133131
  // Function return types
132616
133132
  functionReturnTypes,
133133
+ // Issue #948: Opaque types
133134
+ opaqueTypes,
132617
133135
  // Methods
132618
133136
  getSingleFunctionForVariable: (scopeName, varName) => _TSymbolInfoAdapter.getSingleFunctionForVariable(
132619
133137
  scopeVariableUsage,
@@ -132811,42 +133329,35 @@ var TSymbolInfoAdapter = class _TSymbolInfoAdapter {
132811
133329
  );
132812
133330
  }
132813
133331
  return {
132814
- // Type sets - knownEnums and knownScopes are merged
133332
+ ...base,
132815
133333
  knownScopes: mergedKnownScopes,
132816
- knownStructs: base.knownStructs,
132817
133334
  knownEnums: mergedKnownEnums,
132818
- knownBitmaps: base.knownBitmaps,
132819
- knownRegisters: base.knownRegisters,
132820
- // Scope info
132821
- scopeMembers: base.scopeMembers,
132822
- scopeMemberVisibility: base.scopeMemberVisibility,
132823
- scopeVariableUsage: base.scopeVariableUsage,
132824
- // Struct info
132825
- structFields: base.structFields,
132826
- structFieldArrays: base.structFieldArrays,
132827
- structFieldDimensions: base.structFieldDimensions,
132828
- // Enum info - merged
132829
133335
  enumMembers: mergedEnumMembers,
132830
- // Bitmap info
132831
- bitmapFields: base.bitmapFields,
132832
- bitmapBackingType: base.bitmapBackingType,
132833
- bitmapBitWidth: base.bitmapBitWidth,
132834
- // Register info
132835
- scopedRegisters: base.scopedRegisters,
132836
- registerMemberAccess: base.registerMemberAccess,
132837
- registerMemberTypes: base.registerMemberTypes,
132838
- registerBaseAddresses: base.registerBaseAddresses,
132839
- registerMemberOffsets: base.registerMemberOffsets,
132840
- registerMemberCTypes: base.registerMemberCTypes,
132841
- // Private const values
132842
- scopePrivateConstValues: base.scopePrivateConstValues,
132843
- // Function return types - merged
132844
- functionReturnTypes: mergedFunctionReturnTypes,
132845
- // Methods - delegate to base's implementation
132846
- getSingleFunctionForVariable: base.getSingleFunctionForVariable,
132847
- hasPublicSymbols: base.hasPublicSymbols
133336
+ functionReturnTypes: mergedFunctionReturnTypes
132848
133337
  };
132849
133338
  }
133339
+ /**
133340
+ * Issue #948: Merge opaque types from an external source (e.g., SymbolTable)
133341
+ * into an existing ICodeGenSymbols.
133342
+ *
133343
+ * Opaque types are forward-declared struct types (like `typedef struct _foo foo;`)
133344
+ * that come from C headers and need to be tracked for correct scope variable
133345
+ * generation (as pointers with NULL initialization).
133346
+ *
133347
+ * @param base The ICodeGenSymbols from the current file
133348
+ * @param externalOpaqueTypes Array of opaque type names from external sources
133349
+ * @returns New ICodeGenSymbols with merged opaque types
133350
+ */
133351
+ static mergeOpaqueTypes(base, externalOpaqueTypes) {
133352
+ if (externalOpaqueTypes.length === 0) {
133353
+ return base;
133354
+ }
133355
+ const mergedOpaqueTypes = new Set(base.opaqueTypes);
133356
+ for (const typeName of externalOpaqueTypes) {
133357
+ mergedOpaqueTypes.add(typeName);
133358
+ }
133359
+ return { ...base, opaqueTypes: mergedOpaqueTypes };
133360
+ }
132850
133361
  };
132851
133362
  var TSymbolInfoAdapter_default = TSymbolInfoAdapter;
132852
133363
 
@@ -133233,6 +133744,30 @@ var DeclaratorUtils = class _DeclaratorUtils {
133233
133744
  }
133234
133745
  return void 0;
133235
133746
  }
133747
+ /**
133748
+ * Extract the first declarator name from an init-declarator-list.
133749
+ * For "typedef struct _widget_t widget_t;", this returns "widget_t".
133750
+ * Used for Issue #948 opaque type detection.
133751
+ */
133752
+ static extractFirstDeclaratorName(initDeclList) {
133753
+ const initDeclarators = initDeclList.initDeclarator?.();
133754
+ if (!initDeclarators || initDeclarators.length === 0) return void 0;
133755
+ const firstDeclarator = initDeclarators[0].declarator?.();
133756
+ if (!firstDeclarator) return void 0;
133757
+ return _DeclaratorUtils.extractDeclaratorName(firstDeclarator) ?? void 0;
133758
+ }
133759
+ /**
133760
+ * Check if the first declarator in an init-declarator-list has a pointer.
133761
+ * For "typedef struct X *handle_t;", the declarator is "*handle_t" which has a pointer.
133762
+ * Used for Issue #957 to distinguish pointer typedefs from opaque struct typedefs.
133763
+ */
133764
+ static firstDeclaratorHasPointer(initDeclList) {
133765
+ const initDeclarators = initDeclList.initDeclarator?.();
133766
+ if (!initDeclarators || initDeclarators.length === 0) return false;
133767
+ const firstDeclarator = initDeclarators[0].declarator?.();
133768
+ if (!firstDeclarator) return false;
133769
+ return Boolean(firstDeclarator.pointer?.());
133770
+ }
133236
133771
  };
133237
133772
  var DeclaratorUtils_default = DeclaratorUtils;
133238
133773
 
@@ -133245,15 +133780,15 @@ var StructCollector2 = class _StructCollector {
133245
133780
  * @param sourceFile Source file path
133246
133781
  * @param line Source line number
133247
133782
  * @param symbolTable Optional symbol table for field tracking
133248
- * @param typedefName Optional typedef name for anonymous structs
133249
- * @param isTypedef Whether this is part of a typedef declaration
133250
- * @param warnings Array to collect warnings
133783
+ * @param options Optional collection options (typedef info, warnings)
133251
133784
  */
133252
- static collect(structSpec, sourceFile, line, symbolTable, typedefName, isTypedef, warnings) {
133785
+ static collect(structSpec, sourceFile, line, symbolTable, options = {}) {
133786
+ const { typedefName, isTypedef, warnings, isPointerTypedef } = options;
133253
133787
  const identifier = structSpec.Identifier();
133254
133788
  const name = identifier?.getText() || typedefName;
133255
133789
  if (!name) return null;
133256
133790
  const isUnion = structSpec.structOrUnion()?.getText() === "union";
133791
+ const hasBody = structSpec.structDeclarationList() !== null;
133257
133792
  const fields = _StructCollector.collectFields(
133258
133793
  structSpec,
133259
133794
  name,
@@ -133261,8 +133796,15 @@ var StructCollector2 = class _StructCollector {
133261
133796
  warnings
133262
133797
  );
133263
133798
  const needsStructKeyword = Boolean(identifier && !isTypedef);
133264
- if (symbolTable && needsStructKeyword) {
133265
- symbolTable.markNeedsStructKeyword(name);
133799
+ if (symbolTable) {
133800
+ _StructCollector.updateSymbolTable(symbolTable, name, sourceFile, {
133801
+ needsStructKeyword,
133802
+ hasBody,
133803
+ isTypedef,
133804
+ typedefName,
133805
+ structTag: identifier?.getText(),
133806
+ isPointerTypedef
133807
+ });
133266
133808
  }
133267
133809
  return {
133268
133810
  kind: "struct",
@@ -133276,6 +133818,60 @@ var StructCollector2 = class _StructCollector {
133276
133818
  fields: fields.size > 0 ? fields : void 0
133277
133819
  };
133278
133820
  }
133821
+ /**
133822
+ * Update symbol table with struct metadata.
133823
+ * Extracted to reduce cognitive complexity of collect().
133824
+ */
133825
+ static updateSymbolTable(symbolTable, name, sourceFile, options) {
133826
+ const {
133827
+ needsStructKeyword,
133828
+ hasBody,
133829
+ isTypedef,
133830
+ typedefName,
133831
+ structTag,
133832
+ isPointerTypedef
133833
+ } = options;
133834
+ if (needsStructKeyword) {
133835
+ symbolTable.markNeedsStructKeyword(name);
133836
+ }
133837
+ if (isTypedef && !hasBody && typedefName && !isPointerTypedef) {
133838
+ symbolTable.markOpaqueType(typedefName);
133839
+ if (structTag) {
133840
+ symbolTable.registerStructTagAlias(structTag, typedefName);
133841
+ }
133842
+ }
133843
+ if (isTypedef && typedefName && !isPointerTypedef) {
133844
+ symbolTable.markTypedefStructType(typedefName, sourceFile);
133845
+ }
133846
+ if (hasBody) {
133847
+ _StructCollector.unmarkOpaqueTypesOnDefinition(
133848
+ symbolTable,
133849
+ sourceFile,
133850
+ structTag,
133851
+ typedefName
133852
+ );
133853
+ }
133854
+ }
133855
+ /**
133856
+ * Unmark opaque and typedef struct types when a full struct definition is encountered.
133857
+ * Handles: typedef struct _foo foo; struct _foo { ... };
133858
+ * Issue #958: Only unmarks typedefStructTypes when definition is in SAME file.
133859
+ */
133860
+ static unmarkOpaqueTypesOnDefinition(symbolTable, sourceFile, structTag, typedefName) {
133861
+ if (structTag) {
133862
+ const typedefAlias = symbolTable.getStructTagAlias(structTag);
133863
+ symbolTable.unmarkOpaqueType(structTag);
133864
+ symbolTable.unmarkTypedefStructType(structTag, sourceFile);
133865
+ if (typedefAlias) {
133866
+ symbolTable.unmarkOpaqueType(typedefAlias);
133867
+ symbolTable.unmarkTypedefStructType(typedefAlias, sourceFile);
133868
+ }
133869
+ }
133870
+ if (typedefName) {
133871
+ symbolTable.unmarkOpaqueType(typedefName);
133872
+ symbolTable.unmarkTypedefStructType(typedefName, sourceFile);
133873
+ }
133874
+ }
133279
133875
  /**
133280
133876
  * Collect fields from a struct/union definition.
133281
133877
  */
@@ -133412,6 +134008,15 @@ var FunctionCollector2 = class _FunctionCollector {
133412
134008
  isArray: p.isArray
133413
134009
  }));
133414
134010
  }
134011
+ /**
134012
+ * Resolve return type, appending '*' if declarator has a pointer.
134013
+ * Issue #895 Bug B / Issue #945: C grammar puts pointer before directDeclarator
134014
+ * (e.g., `widget_t *func()` has declarator.pointer() !== null)
134015
+ */
134016
+ static _resolveReturnType(baseType, declarator) {
134017
+ const hasPointer = declarator.pointer() !== null;
134018
+ return hasPointer ? `${baseType}*` : baseType;
134019
+ }
133415
134020
  /**
133416
134021
  * Collect a function symbol from a function definition.
133417
134022
  *
@@ -133425,7 +134030,11 @@ var FunctionCollector2 = class _FunctionCollector {
133425
134030
  if (!name) return null;
133426
134031
  const line = funcDef.start?.line ?? 0;
133427
134032
  const declSpecs = funcDef.declarationSpecifiers();
133428
- const returnType = declSpecs ? DeclaratorUtils_default.extractTypeFromDeclSpecs(declSpecs) : "int";
134033
+ const baseType = declSpecs ? DeclaratorUtils_default.extractTypeFromDeclSpecs(declSpecs) : "int";
134034
+ const returnType = _FunctionCollector._resolveReturnType(
134035
+ baseType,
134036
+ declarator
134037
+ );
133429
134038
  const parameters = _FunctionCollector._mapParameters(
133430
134039
  DeclaratorUtils_default.extractFunctionParameters(declarator)
133431
134040
  );
@@ -133455,8 +134064,10 @@ var FunctionCollector2 = class _FunctionCollector {
133455
134064
  const parameters = _FunctionCollector._mapParameters(
133456
134065
  DeclaratorUtils_default.extractFunctionParameters(declarator)
133457
134066
  );
133458
- const hasPointer = declarator.pointer() !== null;
133459
- const returnType = hasPointer ? `${baseType}*` : baseType;
134067
+ const returnType = _FunctionCollector._resolveReturnType(
134068
+ baseType,
134069
+ declarator
134070
+ );
133460
134071
  return {
133461
134072
  kind: "function",
133462
134073
  name,
@@ -133599,39 +134210,33 @@ var CResolver = class _CResolver {
133599
134210
  const line = decl.start?.line ?? 0;
133600
134211
  const isTypedef = DeclaratorUtils_default.hasStorageClass(declSpecs, "typedef");
133601
134212
  const isExtern = DeclaratorUtils_default.hasStorageClass(declSpecs, "extern");
134213
+ const ctx = {
134214
+ sourceFile,
134215
+ line,
134216
+ isTypedef,
134217
+ isExtern,
134218
+ symbols
134219
+ };
133602
134220
  const structSpec = DeclaratorUtils_default.findStructOrUnionSpecifier(declSpecs);
133603
134221
  if (structSpec) {
133604
- const typedefName = isTypedef ? DeclaratorUtils_default.extractTypedefNameFromSpecs(declSpecs) : void 0;
133605
- const structSymbol = StructCollector_default2.collect(
134222
+ _CResolver.collectStructSymbol(
133606
134223
  structSpec,
133607
- sourceFile,
133608
- line,
134224
+ decl,
134225
+ declSpecs,
134226
+ ctx,
133609
134227
  symbolTable,
133610
- typedefName,
133611
- isTypedef,
133612
134228
  warnings
133613
134229
  );
133614
- if (structSymbol) {
133615
- symbols.push(structSymbol);
133616
- }
133617
134230
  }
133618
134231
  const enumSpec = DeclaratorUtils_default.findEnumSpecifier(declSpecs);
133619
134232
  if (enumSpec) {
133620
- const enumResult = EnumCollector_default2.collect(enumSpec, sourceFile, line);
133621
- if (enumResult) {
133622
- symbols.push(enumResult.enum);
133623
- for (const member of enumResult.members) {
133624
- symbols.push(member);
133625
- }
133626
- }
134233
+ _CResolver.collectEnumSymbols(
134234
+ enumSpec,
134235
+ ctx.sourceFile,
134236
+ ctx.line,
134237
+ ctx.symbols
134238
+ );
133627
134239
  }
133628
- const ctx = {
133629
- sourceFile,
133630
- line,
133631
- isTypedef,
133632
- isExtern,
133633
- symbols
133634
- };
133635
134240
  const initDeclList = decl.initDeclaratorList();
133636
134241
  if (initDeclList) {
133637
134242
  const baseType = DeclaratorUtils_default.extractTypeFromDeclSpecs(declSpecs);
@@ -133645,6 +134250,63 @@ var CResolver = class _CResolver {
133645
134250
  );
133646
134251
  }
133647
134252
  }
134253
+ /**
134254
+ * Collect struct symbol from a struct specifier.
134255
+ * Extracted to reduce cognitive complexity of collectDeclaration().
134256
+ */
134257
+ static collectStructSymbol(structSpec, decl, declSpecs, ctx, symbolTable, warnings) {
134258
+ const typedefName = ctx.isTypedef ? _CResolver.extractTypedefName(decl, declSpecs) : void 0;
134259
+ const isPointerTypedef = ctx.isTypedef ? _CResolver.isPointerTypedef(decl) : false;
134260
+ const structSymbol = StructCollector_default2.collect(
134261
+ structSpec,
134262
+ ctx.sourceFile,
134263
+ ctx.line,
134264
+ symbolTable,
134265
+ {
134266
+ typedefName,
134267
+ isTypedef: ctx.isTypedef,
134268
+ warnings,
134269
+ isPointerTypedef
134270
+ }
134271
+ );
134272
+ if (structSymbol) {
134273
+ ctx.symbols.push(structSymbol);
134274
+ }
134275
+ }
134276
+ /**
134277
+ * Extract typedef name from declaration.
134278
+ * First try from init-declarator-list, then fall back to specifiers.
134279
+ */
134280
+ static extractTypedefName(decl, declSpecs) {
134281
+ const initDeclList = decl.initDeclaratorList();
134282
+ if (initDeclList) {
134283
+ const name = DeclaratorUtils_default.extractFirstDeclaratorName(initDeclList);
134284
+ if (name) return name;
134285
+ }
134286
+ return DeclaratorUtils_default.extractTypedefNameFromSpecs(declSpecs);
134287
+ }
134288
+ /**
134289
+ * Check if a typedef declaration has a pointer declarator.
134290
+ * For "typedef struct X *Y", the declarator is "*Y" which has a pointer.
134291
+ * Used for Issue #957 to distinguish pointer typedefs from opaque struct typedefs.
134292
+ */
134293
+ static isPointerTypedef(decl) {
134294
+ const initDeclList = decl.initDeclaratorList();
134295
+ if (!initDeclList) return false;
134296
+ return DeclaratorUtils_default.firstDeclaratorHasPointer(initDeclList);
134297
+ }
134298
+ /**
134299
+ * Collect enum symbols from an enum specifier.
134300
+ * Extracted to reduce cognitive complexity of collectDeclaration().
134301
+ */
134302
+ static collectEnumSymbols(enumSpec, sourceFile, line, symbols) {
134303
+ const enumResult = EnumCollector_default2.collect(enumSpec, sourceFile, line);
134304
+ if (!enumResult) return;
134305
+ symbols.push(enumResult.enum);
134306
+ for (const member of enumResult.members) {
134307
+ symbols.push(member);
134308
+ }
134309
+ }
133648
134310
  /**
133649
134311
  * Collect symbols from init declarator list.
133650
134312
  */
@@ -137838,6 +138500,9 @@ var IndexTypeListener = class extends CNextListener {
137838
138500
  if (TypeConstants_default.UNSIGNED_INDEX_TYPES.includes(resolvedType)) {
137839
138501
  continue;
137840
138502
  }
138503
+ if (CodeGenState.isKnownEnum(resolvedType)) {
138504
+ continue;
138505
+ }
137841
138506
  const { line, column } = ParserUtils_default.getPosition(ctx);
137842
138507
  this.analyzer.addError(line, column, "E0852", resolvedType);
137843
138508
  return;
@@ -137927,6 +138592,10 @@ var IndexTypeListener = class extends CNextListener {
137927
138592
  if (TypeConstants_default.SIGNED_TYPES.includes(currentType)) {
137928
138593
  return "bool";
137929
138594
  }
138595
+ const strippedType = currentType.replace(/\[[^\]]*\]$/, "");
138596
+ if (strippedType !== currentType) {
138597
+ return strippedType;
138598
+ }
137930
138599
  return currentType;
137931
138600
  }
137932
138601
  if (op.LPAREN()) {
@@ -138327,7 +138996,7 @@ var CacheKeyGenerator_default = CacheKeyGenerator;
138327
138996
 
138328
138997
  // src/utils/cache/CacheManager.ts
138329
138998
  var defaultFs7 = NodeFileSystem_default.instance;
138330
- var CACHE_VERSION = 4;
138999
+ var CACHE_VERSION = 6;
138331
139000
  var TRANSPILER_VERSION = package_default.version;
138332
139001
  var CacheManager = class {
138333
139002
  projectRoot;
@@ -138427,14 +139096,16 @@ var CacheManager = class {
138427
139096
  symbols,
138428
139097
  structFields,
138429
139098
  needsStructKeyword: cachedEntry.needsStructKeyword ?? [],
138430
- enumBitWidth
139099
+ enumBitWidth,
139100
+ opaqueTypes: cachedEntry.opaqueTypes ?? [],
139101
+ typedefStructTypes: cachedEntry.typedefStructTypes ?? []
138431
139102
  };
138432
139103
  }
138433
139104
  /**
138434
139105
  * Store symbols and struct fields for a file
138435
139106
  * ADR-055 Phase 7: Takes ISerializedSymbol[] directly
138436
139107
  */
138437
- setSymbols(filePath, symbols, structFields, needsStructKeyword, enumBitWidth) {
139108
+ setSymbols(filePath, symbols, structFields, needsStructKeyword, enumBitWidth, opaqueTypes, typedefStructTypes) {
138438
139109
  if (!this.cache) return;
138439
139110
  let cacheKey;
138440
139111
  try {
@@ -138462,7 +139133,9 @@ var CacheManager = class {
138462
139133
  symbols: serializedSymbols,
138463
139134
  structFields: serializedFields,
138464
139135
  needsStructKeyword,
138465
- enumBitWidth: serializedEnumBitWidth
139136
+ enumBitWidth: serializedEnumBitWidth,
139137
+ opaqueTypes,
139138
+ typedefStructTypes
138466
139139
  };
138467
139140
  this.cache.setKey(filePath, entry);
138468
139141
  this.dirty = true;
@@ -138490,12 +139163,16 @@ var CacheManager = class {
138490
139163
  filePath,
138491
139164
  symbolTable
138492
139165
  );
139166
+ const opaqueTypes = symbolTable.getAllOpaqueTypes();
139167
+ const typedefStructTypes = symbolTable.getAllTypedefStructTypes();
138493
139168
  this.setSymbols(
138494
139169
  filePath,
138495
139170
  symbols,
138496
139171
  structFields,
138497
139172
  needsStructKeyword,
138498
- enumBitWidth
139173
+ enumBitWidth,
139174
+ opaqueTypes,
139175
+ typedefStructTypes
138499
139176
  );
138500
139177
  }
138501
139178
  /**
@@ -138938,7 +139615,7 @@ var Transpiler = class {
138938
139615
  * Stage 6: Generate headers (per-file)
138939
139616
  */
138940
139617
  async _executePipeline(input, result) {
138941
- this._collectAllHeaderSymbols(input.headerFiles, result);
139618
+ await this._collectAllHeaderSymbols(input.headerFiles, result);
138942
139619
  CodeGenState.buildExternalStructFields();
138943
139620
  if (!this._collectAllCNextSymbolsFromPipeline(input.cnextFiles, result)) {
138944
139621
  return;
@@ -139047,6 +139724,13 @@ var Transpiler = class {
139047
139724
  externalEnumSources
139048
139725
  );
139049
139726
  }
139727
+ const externalOpaqueTypes = CodeGenState.symbolTable.getAllOpaqueTypes();
139728
+ if (externalOpaqueTypes.length > 0) {
139729
+ symbolInfo = TSymbolInfoAdapter_default.mergeOpaqueTypes(
139730
+ symbolInfo,
139731
+ externalOpaqueTypes
139732
+ );
139733
+ }
139050
139734
  CodeGenState.symbols = symbolInfo;
139051
139735
  const analyzerErrors = runAnalyzers_default(tree, tokenStream);
139052
139736
  if (analyzerErrors.length > 0) {
@@ -139220,11 +139904,12 @@ var Transpiler = class {
139220
139904
  }
139221
139905
  /**
139222
139906
  * Stage 2: Collect symbols from all C/C++ headers
139907
+ * Issue #945: Made async for preprocessing support.
139223
139908
  */
139224
- _collectAllHeaderSymbols(headerFiles, result) {
139909
+ async _collectAllHeaderSymbols(headerFiles, result) {
139225
139910
  for (const file of headerFiles) {
139226
139911
  try {
139227
- this.doCollectHeaderSymbols(file);
139912
+ await this.doCollectHeaderSymbols(file);
139228
139913
  result.filesProcessed++;
139229
139914
  } catch (err) {
139230
139915
  this.warnings.push(`Failed to process header ${file.path}: ${err}`);
@@ -139503,15 +140188,16 @@ var Transpiler = class {
139503
140188
  /**
139504
140189
  * Stage 2: Collect symbols from a single C/C++ header
139505
140190
  * Issue #592: Recursive include processing moved to IncludeResolver.resolveHeadersTransitively()
140191
+ * Issue #945: Added preprocessing support for conditional compilation
139506
140192
  * SonarCloud S3776: Refactored to use helper methods for reduced complexity.
139507
140193
  */
139508
- doCollectHeaderSymbols(file) {
140194
+ async doCollectHeaderSymbols(file) {
139509
140195
  const absolutePath = resolve12(file.path);
139510
140196
  this.state.markHeaderProcessed(absolutePath);
139511
140197
  if (this.tryRestoreFromCache(file)) {
139512
140198
  return;
139513
140199
  }
139514
- const content = this.fs.readFile(file.path);
140200
+ const content = await this.getHeaderContent(file);
139515
140201
  this.parseHeaderFile(file, content);
139516
140202
  if (this.config.debugMode) {
139517
140203
  const symbols = CodeGenState.symbolTable.getSymbolsByFile(file.path);
@@ -139542,9 +140228,54 @@ var Transpiler = class {
139542
140228
  cached.needsStructKeyword
139543
140229
  );
139544
140230
  CodeGenState.symbolTable.restoreEnumBitWidths(cached.enumBitWidth);
140231
+ CodeGenState.symbolTable.restoreOpaqueTypes(cached.opaqueTypes);
140232
+ CodeGenState.symbolTable.restoreTypedefStructTypes(
140233
+ cached.typedefStructTypes
140234
+ );
139545
140235
  this.detectCppFromFileType(file);
139546
140236
  return true;
139547
140237
  }
140238
+ /**
140239
+ * Get header content, optionally preprocessed.
140240
+ * Issue #945: Evaluates #if/#ifdef directives using system preprocessor.
140241
+ *
140242
+ * Only preprocesses when necessary to avoid side effects from full expansion.
140243
+ * Preprocessing is needed when the file has conditional compilation patterns
140244
+ * like #if MACRO != 0 that require expression evaluation.
140245
+ */
140246
+ async getHeaderContent(file) {
140247
+ const rawContent = this.fs.readFile(file.path);
140248
+ if (this.config.preprocess === false) {
140249
+ return rawContent;
140250
+ }
140251
+ if (!this.preprocessor.isAvailable()) {
140252
+ return rawContent;
140253
+ }
140254
+ if (!this.needsConditionalPreprocessing(rawContent)) {
140255
+ return rawContent;
140256
+ }
140257
+ const result = await this.preprocessor.preprocess(file.path, {
140258
+ defines: this.config.defines,
140259
+ includePaths: this.config.includeDirs,
140260
+ keepLineDirectives: false
140261
+ // We don't need line mappings for symbol collection
140262
+ });
140263
+ if (!result.success) {
140264
+ this.warnings.push(
140265
+ `Preprocessing failed for ${file.path}: ${result.error}. Using raw content.`
140266
+ );
140267
+ return rawContent;
140268
+ }
140269
+ return result.content;
140270
+ }
140271
+ /**
140272
+ * Check if a header file needs conditional preprocessing.
140273
+ * Issue #945: Only preprocess files with #if expressions that need evaluation.
140274
+ */
140275
+ needsConditionalPreprocessing(content) {
140276
+ const ifExpressionPattern = /#(?:if|elif)\s+(?!defined\s*\()(?![01]\s*(?:$|\n|\/\*|\/\/))\w+/m;
140277
+ return ifExpressionPattern.test(content);
140278
+ }
139548
140279
  /**
139549
140280
  * Restore cached symbols to the symbol table.
139550
140281
  * ADR-055 Phase 7: Converts ISerializedSymbol[] from cache to typed symbols.