c-next 0.2.16 → 0.2.18

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 (118) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +8897 -6260
  3. package/dist/index.js.map +4 -4
  4. package/grammar/CNext.g4 +12 -0
  5. package/package.json +4 -2
  6. package/src/transpiler/Transpiler.ts +376 -48
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
  9. package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
  10. package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
  11. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
  12. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  13. package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
  14. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
  15. package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
  16. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  17. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  18. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  19. package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
  20. package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
  21. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  22. package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
  23. package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
  24. package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
  25. package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
  26. package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
  27. package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
  28. package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
  29. package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
  30. package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
  31. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  32. package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
  33. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  34. package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
  35. package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
  36. package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
  37. package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
  38. package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
  39. package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
  40. package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
  41. package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
  42. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
  43. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
  44. package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
  45. package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
  46. package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
  47. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
  48. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  49. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  50. package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
  51. package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
  52. package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
  53. package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
  54. package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
  55. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  56. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
  57. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  58. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
  59. package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
  60. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
  61. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
  62. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
  63. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
  64. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
  65. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
  66. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
  67. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
  68. package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
  69. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
  70. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
  71. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
  72. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
  73. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
  74. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  75. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  76. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  77. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
  78. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  79. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
  80. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  81. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  82. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
  83. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
  84. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
  85. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
  86. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  87. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  88. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
  89. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  90. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  91. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  92. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  93. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
  94. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  95. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  96. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
  97. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  98. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  99. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  100. package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
  101. package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
  102. package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
  103. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  104. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  105. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  106. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
  107. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  108. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  109. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  110. package/src/transpiler/state/CodeGenState.ts +91 -22
  111. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  112. package/src/transpiler/types/ICachedFileEntry.ts +7 -0
  113. package/src/utils/LiteralUtils.ts +23 -0
  114. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  115. package/src/utils/cache/CacheManager.ts +13 -2
  116. package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
  117. package/src/utils/constants/TypeConstants.ts +13 -0
  118. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -74,8 +74,13 @@ describe("SubscriptClassifier", () => {
74
74
  });
75
75
  });
76
76
 
77
- describe("parameter types (Issue #579)", () => {
78
- it("returns array_element for non-array parameter with single index", () => {
77
+ describe("parameter types (Issue #1100)", () => {
78
+ // A parameter's array-ness is determined solely by its declared type
79
+ // (isArray, from explicit `T[N]` syntax, ADR-006) — identical to local
80
+ // variables. `isParameter` alone must NOT force array classification;
81
+ // that was the Issue #579 heuristic reverted by Issue #1100 because it
82
+ // broke ADR-007 bit-indexing for scalar parameters.
83
+ it("returns bit_single for non-array parameter with single index", () => {
79
84
  const typeInfo: TTypeInfo = {
80
85
  baseType: "u8",
81
86
  bitWidth: 8,
@@ -88,10 +93,10 @@ describe("SubscriptClassifier", () => {
88
93
  subscriptCount: 1,
89
94
  isRegisterAccess: false,
90
95
  });
91
- expect(result).toBe("array_element");
96
+ expect(result).toBe("bit_single");
92
97
  });
93
98
 
94
- it("returns array_slice for non-array parameter with two indices", () => {
99
+ it("returns bit_range for non-array parameter with two indices", () => {
95
100
  const typeInfo: TTypeInfo = {
96
101
  baseType: "u8",
97
102
  bitWidth: 8,
@@ -104,7 +109,23 @@ describe("SubscriptClassifier", () => {
104
109
  subscriptCount: 2,
105
110
  isRegisterAccess: false,
106
111
  });
107
- expect(result).toBe("array_slice");
112
+ expect(result).toBe("bit_range");
113
+ });
114
+
115
+ it("returns array_element for array parameter with single index", () => {
116
+ const typeInfo: TTypeInfo = {
117
+ baseType: "u8",
118
+ bitWidth: 8,
119
+ isArray: true,
120
+ isConst: false,
121
+ isParameter: true,
122
+ };
123
+ const result = SubscriptClassifier.classify({
124
+ typeInfo,
125
+ subscriptCount: 1,
126
+ isRegisterAccess: false,
127
+ });
128
+ expect(result).toBe("array_element");
108
129
  });
109
130
  });
110
131
 
@@ -187,7 +208,7 @@ describe("SubscriptClassifier", () => {
187
208
  expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(true);
188
209
  });
189
210
 
190
- it("returns true for parameter type", () => {
211
+ it("returns false for non-array parameter type (Issue #1100)", () => {
191
212
  const typeInfo: TTypeInfo = {
192
213
  baseType: "u8",
193
214
  bitWidth: 8,
@@ -195,6 +216,17 @@ describe("SubscriptClassifier", () => {
195
216
  isConst: false,
196
217
  isParameter: true,
197
218
  };
219
+ expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(false);
220
+ });
221
+
222
+ it("returns true for array parameter type", () => {
223
+ const typeInfo: TTypeInfo = {
224
+ baseType: "u8",
225
+ bitWidth: 8,
226
+ isArray: true,
227
+ isConst: false,
228
+ isParameter: true,
229
+ };
198
230
  expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(true);
199
231
  });
200
232
 
@@ -10,7 +10,8 @@ interface ICallbackTypeInfo {
10
10
  name: string;
11
11
  type: string; // C type
12
12
  isConst: boolean;
13
- isPointer: boolean; // Non-array params become pointers
13
+ isPointer: boolean; // Non-array params become pointers (C mode only for structs)
14
+ isStruct: boolean; // True if parameter type is a struct (ADR-006 reference semantics)
14
15
  isArray: boolean; // Array parameters pass naturally as pointers
15
16
  arrayDims: string; // Array dimensions if applicable
16
17
  }>;
@@ -66,6 +66,13 @@ interface IParameterInput {
66
66
  * When the C typedef has `const T*`, this preserves const on the generated param.
67
67
  */
68
68
  forceConst?: boolean;
69
+
70
+ /**
71
+ * Issue #995: Whether the parameter type is an opaque handle (incomplete struct typedef).
72
+ * This is a pass-through flag; the rule (suppress auto-const, force pointer) is applied
73
+ * in ParameterSignatureBuilder to avoid dual code paths.
74
+ */
75
+ isOpaqueHandle?: boolean;
69
76
  }
70
77
 
71
78
  export default IParameterInput;
@@ -116,6 +116,14 @@ abstract class BaseHeaderGenerator {
116
116
  ...HeaderGeneratorUtils.generateEnumSection(groups.enums, typeInput),
117
117
  ...HeaderGeneratorUtils.generateBitmapSection(groups.bitmaps, typeInput),
118
118
  ...HeaderGeneratorUtils.generateTypeAliasSection(groups.types),
119
+ ...HeaderGeneratorUtils.generateCallbackStructForwardDecls(
120
+ groups.structs,
121
+ typeInput,
122
+ ),
123
+ ...HeaderGeneratorUtils.generateCallbackTypedefSection(
124
+ typeInput,
125
+ options.cppMode,
126
+ ),
119
127
  ...HeaderGeneratorUtils.generateStructSection(
120
128
  groups.structs,
121
129
  groups.classes,
@@ -17,6 +17,7 @@ import generateStructHeader from "./generators/generateStructHeader";
17
17
  import generateBitmapHeader from "./generators/generateBitmapHeader";
18
18
  import VariableDeclarationFormatter from "../codegen/helpers/VariableDeclarationFormatter";
19
19
  import type IVariableFormatInput from "../codegen/types/IVariableFormatInput";
20
+ import MisraSuppressionUtils from "../MisraSuppressionUtils";
20
21
 
21
22
  const { mapType, isBuiltInType } = typeUtils;
22
23
 
@@ -267,6 +268,65 @@ class HeaderGeneratorUtils {
267
268
  ];
268
269
  }
269
270
 
271
+ /**
272
+ * Extract header file stem from include directive for deduplication.
273
+ * E.g., '#include <foo/bar.hpp>' -> 'bar'
274
+ * SonarCloud S8786: Avoid backtracking by using separate patterns.
275
+ */
276
+ private static extractIncludeStem(include: string): string {
277
+ // Try angle brackets first, then quotes - avoids backtracking regex
278
+ const angleMatch = /<([^<>]+)>/.exec(include);
279
+ if (angleMatch) {
280
+ return angleMatch[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
281
+ }
282
+ const quoteMatch = /"([^"]+)"/.exec(include);
283
+ if (quoteMatch) {
284
+ return quoteMatch[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
285
+ }
286
+ return include;
287
+ }
288
+
289
+ /**
290
+ * Add user includes with MISRA suppression comments.
291
+ * SonarCloud S3776: Extracted from generateIncludes.
292
+ */
293
+ private static addUserIncludes(
294
+ lines: string[],
295
+ userIncludes: string[] | undefined,
296
+ ): void {
297
+ if (!userIncludes || userIncludes.length === 0) return;
298
+ for (const include of userIncludes) {
299
+ // Issue #850: Add MISRA suppression for banned headers
300
+ const suppression =
301
+ MisraSuppressionUtils.getMisraSuppressionComment(include);
302
+ if (suppression) {
303
+ lines.push(suppression);
304
+ }
305
+ lines.push(include);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Add external type headers, deduplicating against user includes.
311
+ * SonarCloud S3776: Extracted from generateIncludes.
312
+ */
313
+ private static addExternalTypeHeaders(
314
+ lines: string[],
315
+ headersToInclude: Set<string>,
316
+ userIncludes: string[] | undefined,
317
+ ): void {
318
+ const userIncludeSet = new Set(userIncludes ?? []);
319
+ const userIncludeStems = new Set(
320
+ (userIncludes ?? []).map(HeaderGeneratorUtils.extractIncludeStem),
321
+ );
322
+ for (const directive of headersToInclude) {
323
+ if (userIncludeSet.has(directive)) continue;
324
+ const stem = HeaderGeneratorUtils.extractIncludeStem(directive);
325
+ if (stem && userIncludeStems.has(stem)) continue;
326
+ lines.push(directive);
327
+ }
328
+ }
329
+
270
330
  /**
271
331
  * Generate all include directives (system, user, and external type headers)
272
332
  */
@@ -282,37 +342,18 @@ class HeaderGeneratorUtils {
282
342
  }
283
343
 
284
344
  // User includes (already have correct extension from IncludeExtractor)
285
- if (options.userIncludes && options.userIncludes.length > 0) {
286
- for (const include of options.userIncludes) {
287
- lines.push(include);
288
- }
289
- }
345
+ HeaderGeneratorUtils.addUserIncludes(lines, options.userIncludes);
290
346
 
291
347
  // External type header includes (skip duplicates of user includes)
292
348
  // Dedup by basename stem to handle:
293
349
  // - Different path styles (e.g., <AppConfig.hpp> vs "../AppConfig.hpp")
294
350
  // - Extension mismatch from timing (.h from IncludeResolver before cppDetected,
295
351
  // .hpp from IncludeExtractor after cppDetected)
296
- const userIncludeSet = new Set(options.userIncludes ?? []);
297
- const extractStem = (inc: string): string => {
298
- const match = /["<]([^">]+)[">]/.exec(inc);
299
- if (!match) return inc;
300
- return match[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
301
- };
302
- const userIncludeStems = new Set(
303
- (options.userIncludes ?? []).map(extractStem),
352
+ HeaderGeneratorUtils.addExternalTypeHeaders(
353
+ lines,
354
+ headersToInclude,
355
+ options.userIncludes,
304
356
  );
305
- for (const directive of headersToInclude) {
306
- if (userIncludeSet.has(directive)) {
307
- continue;
308
- }
309
- // Check if a user include already covers the same file
310
- const stem = extractStem(directive);
311
- if (stem && userIncludeStems.has(stem)) {
312
- continue;
313
- }
314
- lines.push(directive);
315
- }
316
357
 
317
358
  // Add blank line if any includes were added
318
359
  const hasIncludes =
@@ -416,6 +457,81 @@ class HeaderGeneratorUtils {
416
457
  return lines;
417
458
  }
418
459
 
460
+ /**
461
+ * ADR-029: Generate forward declarations for structs used in callback typedefs.
462
+ * This ensures struct types can be used in callback parameter types before
463
+ * the full struct definition.
464
+ */
465
+ static generateCallbackStructForwardDecls(
466
+ structs: IHeaderSymbol[],
467
+ typeInput?: IHeaderTypeInput,
468
+ ): string[] {
469
+ if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
470
+ return [];
471
+ }
472
+
473
+ // Collect struct types that are used in callback parameters
474
+ const usedStructTypes = new Set<string>();
475
+ for (const [, cbInfo] of typeInput.callbackTypes) {
476
+ for (const p of cbInfo.parameters) {
477
+ if (p.isStruct) {
478
+ usedStructTypes.add(p.type);
479
+ }
480
+ }
481
+ }
482
+
483
+ if (usedStructTypes.size === 0) {
484
+ return [];
485
+ }
486
+
487
+ // Get local struct names to only forward-declare those
488
+ const localStructNames = new Set(structs.map((s) => s.name));
489
+
490
+ const lines: string[] = [];
491
+ for (const structType of usedStructTypes) {
492
+ if (localStructNames.has(structType)) {
493
+ lines.push(`typedef struct ${structType} ${structType};`);
494
+ }
495
+ }
496
+
497
+ return lines.length > 0 ? [...lines, ""] : [];
498
+ }
499
+
500
+ /**
501
+ * ADR-029: Generate callback typedef section
502
+ * Generates function pointer typedefs for callbacks used as struct field types
503
+ */
504
+ static generateCallbackTypedefSection(
505
+ typeInput?: IHeaderTypeInput,
506
+ isCppMode?: boolean,
507
+ ): string[] {
508
+ if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
509
+ return [];
510
+ }
511
+
512
+ const lines: string[] = ["/* Callback typedefs */"];
513
+ for (const [, cbInfo] of typeInput.callbackTypes) {
514
+ const params =
515
+ cbInfo.parameters.length > 0
516
+ ? cbInfo.parameters
517
+ .map((p) => {
518
+ // ADR-006: Struct parameters become pointers (C) or references (C++)
519
+ if (p.isStruct) {
520
+ const ptrOrRef = isCppMode ? "&" : "*";
521
+ return `${p.type}${ptrOrRef}`;
522
+ }
523
+ return p.type;
524
+ })
525
+ .join(", ")
526
+ : "void";
527
+ lines.push(
528
+ `typedef ${cbInfo.returnType} (*${cbInfo.typedefName})(${params});`,
529
+ );
530
+ }
531
+ lines.push("");
532
+ return lines;
533
+ }
534
+
419
535
  /**
420
536
  * Generate struct and class definitions section
421
537
  */
@@ -804,4 +804,284 @@ describe("HeaderGeneratorUtils", () => {
804
804
  expect(lines).toContain("#endif /* MY_FILE_H */");
805
805
  });
806
806
  });
807
+
808
+ describe("generateCallbackStructForwardDecls", () => {
809
+ it("returns empty array when no callback types provided", () => {
810
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
811
+ [],
812
+ );
813
+ expect(result).toEqual([]);
814
+ });
815
+
816
+ it("returns empty array when callback types is undefined", () => {
817
+ const input = {
818
+ structFields: new Map(),
819
+ structFieldDimensions: new Map(),
820
+ enumMembers: new Map(),
821
+ bitmapBackingType: new Map(),
822
+ bitmapFields: new Map(),
823
+ callbackTypes: undefined,
824
+ };
825
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
826
+ [],
827
+ input,
828
+ );
829
+ expect(result).toEqual([]);
830
+ });
831
+
832
+ it("generates forward declarations for struct parameters", () => {
833
+ const structs = [makeSymbol({ name: "DataStruct", kind: "struct" })];
834
+ const input = {
835
+ structFields: new Map(),
836
+ structFieldDimensions: new Map(),
837
+ enumMembers: new Map(),
838
+ bitmapBackingType: new Map(),
839
+ bitmapFields: new Map(),
840
+ callbackTypes: new Map([
841
+ [
842
+ "processData",
843
+ {
844
+ typedefName: "processData_t",
845
+ returnType: "void",
846
+ parameters: [
847
+ { type: "DataStruct", isStruct: true },
848
+ { type: "uint32_t", isStruct: false },
849
+ ],
850
+ },
851
+ ],
852
+ ]),
853
+ };
854
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
855
+ structs,
856
+ input,
857
+ );
858
+ expect(result).toContain("typedef struct DataStruct DataStruct;");
859
+ });
860
+
861
+ it("deduplicates struct forward declarations", () => {
862
+ const structs = [makeSymbol({ name: "MyStruct", kind: "struct" })];
863
+ const input = {
864
+ structFields: new Map(),
865
+ structFieldDimensions: new Map(),
866
+ enumMembers: new Map(),
867
+ bitmapBackingType: new Map(),
868
+ bitmapFields: new Map(),
869
+ callbackTypes: new Map([
870
+ [
871
+ "handler1",
872
+ {
873
+ typedefName: "handler1_t",
874
+ returnType: "void",
875
+ parameters: [{ type: "MyStruct", isStruct: true }],
876
+ },
877
+ ],
878
+ [
879
+ "handler2",
880
+ {
881
+ typedefName: "handler2_t",
882
+ returnType: "void",
883
+ parameters: [{ type: "MyStruct", isStruct: true }],
884
+ },
885
+ ],
886
+ ]),
887
+ };
888
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
889
+ structs,
890
+ input,
891
+ );
892
+ const myStructDecls = result.filter((l) => l.includes("MyStruct"));
893
+ expect(myStructDecls.length).toBe(1);
894
+ });
895
+
896
+ it("ignores non-struct parameters", () => {
897
+ const input = {
898
+ structFields: new Map(),
899
+ structFieldDimensions: new Map(),
900
+ enumMembers: new Map(),
901
+ bitmapBackingType: new Map(),
902
+ bitmapFields: new Map(),
903
+ callbackTypes: new Map([
904
+ [
905
+ "handler",
906
+ {
907
+ typedefName: "handler_t",
908
+ returnType: "uint32_t",
909
+ parameters: [
910
+ { type: "uint32_t", isStruct: false },
911
+ { type: "bool", isStruct: false },
912
+ ],
913
+ },
914
+ ],
915
+ ]),
916
+ };
917
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
918
+ [],
919
+ input,
920
+ );
921
+ expect(result).toEqual([]);
922
+ });
923
+
924
+ it("only includes local structs in forward declarations", () => {
925
+ // ExternalStruct is not in the local structs list
926
+ const structs = [makeSymbol({ name: "LocalStruct", kind: "struct" })];
927
+ const input = {
928
+ structFields: new Map(),
929
+ structFieldDimensions: new Map(),
930
+ enumMembers: new Map(),
931
+ bitmapBackingType: new Map(),
932
+ bitmapFields: new Map(),
933
+ callbackTypes: new Map([
934
+ [
935
+ "handler",
936
+ {
937
+ typedefName: "handler_t",
938
+ returnType: "void",
939
+ parameters: [
940
+ { type: "LocalStruct", isStruct: true },
941
+ { type: "ExternalStruct", isStruct: true },
942
+ ],
943
+ },
944
+ ],
945
+ ]),
946
+ };
947
+ const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
948
+ structs,
949
+ input,
950
+ );
951
+ expect(result).toContain("typedef struct LocalStruct LocalStruct;");
952
+ expect(result).not.toContain("ExternalStruct");
953
+ });
954
+ });
955
+
956
+ describe("generateCallbackTypedefSection", () => {
957
+ it("returns empty array when no callback types provided", () => {
958
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection();
959
+ expect(result).toEqual([]);
960
+ });
961
+
962
+ it("returns empty array when callback types is undefined", () => {
963
+ const input = {
964
+ structFields: new Map(),
965
+ structFieldDimensions: new Map(),
966
+ enumMembers: new Map(),
967
+ bitmapBackingType: new Map(),
968
+ bitmapFields: new Map(),
969
+ callbackTypes: undefined,
970
+ };
971
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
972
+ input,
973
+ false,
974
+ );
975
+ expect(result).toEqual([]);
976
+ });
977
+
978
+ it("generates C-style function pointer typedef", () => {
979
+ const input = {
980
+ structFields: new Map(),
981
+ structFieldDimensions: new Map(),
982
+ enumMembers: new Map(),
983
+ bitmapBackingType: new Map(),
984
+ bitmapFields: new Map(),
985
+ callbackTypes: new Map([
986
+ [
987
+ "processU32",
988
+ {
989
+ typedefName: "processU32_t",
990
+ returnType: "uint32_t",
991
+ parameters: [{ type: "uint32_t", isStruct: false }],
992
+ },
993
+ ],
994
+ ]),
995
+ };
996
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
997
+ input,
998
+ false,
999
+ );
1000
+ expect(result.join("\n")).toContain(
1001
+ "typedef uint32_t (*processU32_t)(uint32_t);",
1002
+ );
1003
+ });
1004
+
1005
+ it("generates C++ reference for struct parameters", () => {
1006
+ const input = {
1007
+ structFields: new Map(),
1008
+ structFieldDimensions: new Map(),
1009
+ enumMembers: new Map(),
1010
+ bitmapBackingType: new Map(),
1011
+ bitmapFields: new Map(),
1012
+ callbackTypes: new Map([
1013
+ [
1014
+ "processPoint",
1015
+ {
1016
+ typedefName: "processPoint_t",
1017
+ returnType: "uint32_t",
1018
+ parameters: [{ type: "Point", isStruct: true }],
1019
+ },
1020
+ ],
1021
+ ]),
1022
+ };
1023
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
1024
+ input,
1025
+ true,
1026
+ );
1027
+ expect(result.join("\n")).toContain("Point&");
1028
+ });
1029
+
1030
+ it("generates C pointer for struct parameters", () => {
1031
+ const input = {
1032
+ structFields: new Map(),
1033
+ structFieldDimensions: new Map(),
1034
+ enumMembers: new Map(),
1035
+ bitmapBackingType: new Map(),
1036
+ bitmapFields: new Map(),
1037
+ callbackTypes: new Map([
1038
+ [
1039
+ "processPoint",
1040
+ {
1041
+ typedefName: "processPoint_t",
1042
+ returnType: "uint32_t",
1043
+ parameters: [{ type: "Point", isStruct: true }],
1044
+ },
1045
+ ],
1046
+ ]),
1047
+ };
1048
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
1049
+ input,
1050
+ false,
1051
+ );
1052
+ expect(result.join("\n")).toContain("Point*");
1053
+ });
1054
+
1055
+ it("handles multiple parameters", () => {
1056
+ const input = {
1057
+ structFields: new Map(),
1058
+ structFieldDimensions: new Map(),
1059
+ enumMembers: new Map(),
1060
+ bitmapBackingType: new Map(),
1061
+ bitmapFields: new Map(),
1062
+ callbackTypes: new Map([
1063
+ [
1064
+ "multiParam",
1065
+ {
1066
+ typedefName: "multiParam_t",
1067
+ returnType: "void",
1068
+ parameters: [
1069
+ { type: "uint32_t", isStruct: false },
1070
+ { type: "Point", isStruct: true },
1071
+ { type: "bool", isStruct: false },
1072
+ ],
1073
+ },
1074
+ ],
1075
+ ]),
1076
+ };
1077
+ const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
1078
+ input,
1079
+ false,
1080
+ );
1081
+ const typedef = result.find((l) => l.includes("multiParam_t"));
1082
+ expect(typedef).toContain("uint32_t");
1083
+ expect(typedef).toContain("Point*");
1084
+ expect(typedef).toContain("bool");
1085
+ });
1086
+ });
807
1087
  });
@@ -31,6 +31,19 @@ interface IHeaderTypeInput {
31
31
  string,
32
32
  ReadonlyMap<string, { readonly offset: number; readonly width: number }>
33
33
  >;
34
+
35
+ /** Callback types: functionName -> typedef info for header generation */
36
+ readonly callbackTypes?: ReadonlyMap<
37
+ string,
38
+ {
39
+ readonly typedefName: string;
40
+ readonly returnType: string;
41
+ readonly parameters: ReadonlyArray<{
42
+ readonly type: string;
43
+ readonly isStruct: boolean;
44
+ }>;
45
+ }
46
+ >;
34
47
  }
35
48
 
36
49
  export default IHeaderTypeInput;