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
@@ -110,6 +110,16 @@ class SymbolTable {
110
110
  */
111
111
  private readonly needsStructKeyword: Set<string> = new Set();
112
112
 
113
+ /**
114
+ * Issue #985 recovery: names of external function-like MACROS recovered by
115
+ * translation-unit preprocessing (ExternalDeclarationOracle) for headers cnext
116
+ * could not preprocess standalone. Macros have no declaration to parse, so
117
+ * only their names are known; consulted by the undeclared-`global.`-call check
118
+ * so it does not false-positive on a real macro (e.g. FreeRTOS pdMS_TO_TICKS).
119
+ * Recovered FUNCTIONS are registered as full symbols (with signatures) instead.
120
+ */
121
+ private readonly externalDeclarationNames: Set<string> = new Set();
122
+
113
123
  /**
114
124
  * Issue #958: Immutable struct symbol state — additive only, query-time resolution.
115
125
  * Replaces separate opaqueTypes, typedefStructTypes, structTagAliases fields.
@@ -355,6 +365,21 @@ class SymbolTable {
355
365
  }
356
366
  }
357
367
 
368
+ /**
369
+ * Register external declaration names recovered via TU preprocessing.
370
+ * @see externalDeclarationNames
371
+ */
372
+ addExternalDeclarationNames(names: ReadonlySet<string>): void {
373
+ for (const name of names) {
374
+ this.externalDeclarationNames.add(name);
375
+ }
376
+ }
377
+
378
+ /** Whether a name was recovered as an external function / function-like macro. */
379
+ hasExternalDeclaration(name: string): boolean {
380
+ return this.externalDeclarationNames.has(name);
381
+ }
382
+
358
383
  /**
359
384
  * Get a C symbol by name (returns first match, or undefined)
360
385
  */
@@ -1016,6 +1041,26 @@ class SymbolTable {
1016
1041
  });
1017
1042
  }
1018
1043
 
1044
+ /**
1045
+ * Issue #985: Clear a struct tag's recorded body. Used by external-declaration
1046
+ * recovery to undo a PHANTOM body — one fabricated by ANTLR error-recovery when
1047
+ * the normal pass parsed a header's huge preprocessed blob — so a type the
1048
+ * clean per-file re-parse proves opaque (e.g. lvgl `lv_obj_t`) resolves as
1049
+ * opaque again (and codegen uses a pointer).
1050
+ * @param structTag The struct tag name (e.g., "_lv_obj_t")
1051
+ */
1052
+ clearStructTagHasBody(structTag: string): void {
1053
+ if (!this.structState.structTagsWithBodies.has(structTag)) return;
1054
+ this.structState = produce(this.structState, (draft) => {
1055
+ draft.structTagsWithBodies.delete(structTag);
1056
+ });
1057
+ }
1058
+
1059
+ /** Issue #985: The struct tag a typedef aliases, if any (e.g. lv_obj_t -> _lv_obj_t). */
1060
+ getStructTagForTypedef(typedefName: string): string | undefined {
1061
+ return this.structState.typedefToTag.get(typedefName);
1062
+ }
1063
+
1019
1064
  /**
1020
1065
  * Issue #958: Get all struct tags with bodies for cache serialization.
1021
1066
  * @returns Array of struct tag names
@@ -1077,12 +1122,27 @@ class SymbolTable {
1077
1122
  * Issue #958: Check if a typedef aliases a struct type.
1078
1123
  * Used for scope variables, function parameters, and local variables
1079
1124
  * which should be pointers for C-header struct types.
1125
+ *
1126
+ * Issue #948: Performs query-time resolution - if the underlying struct
1127
+ * tag has a full body definition, this is NOT an external typedef struct
1128
+ * (it's a complete type that can use value semantics).
1129
+ *
1080
1130
  * @param typeName The type name to check
1081
1131
  * @returns true if this is a typedef'd struct type from C headers
1082
1132
  */
1083
1133
  isTypedefStructType(typeName: string): boolean {
1084
- const result = this.structState.typedefStructTypes.has(typeName);
1085
- return result;
1134
+ if (!this.structState.typedefStructTypes.has(typeName)) {
1135
+ return false;
1136
+ }
1137
+ // Issue #948: Query-time resolution - if the underlying struct tag
1138
+ // has a body definition, this typedef is NOT an external struct type.
1139
+ // Example: `typedef struct _point_t point_t;` followed by `struct _point_t { ... };`
1140
+ // The second declaration provides the body, so point_t is a complete type.
1141
+ const tag = this.structState.typedefToTag.get(typeName);
1142
+ if (tag && this.structState.structTagsWithBodies.has(tag)) {
1143
+ return false;
1144
+ }
1145
+ return true;
1086
1146
  }
1087
1147
 
1088
1148
  /**
@@ -772,6 +772,55 @@ describe("SymbolTable", () => {
772
772
  expect(symbolTable.isOpaqueType("handle_t")).toBe(true);
773
773
  expect(symbolTable.getAllOpaqueTypes()).toHaveLength(2);
774
774
  });
775
+
776
+ it("clearStructTagHasBody restores opacity for a phantom body (#985)", () => {
777
+ symbolTable.markOpaqueType("obj_t");
778
+ symbolTable.registerStructTagAlias("_obj", "obj_t");
779
+ symbolTable.markStructTagHasBody("_obj"); // phantom body from a blob parse
780
+ expect(symbolTable.isOpaqueType("obj_t")).toBe(false);
781
+
782
+ symbolTable.clearStructTagHasBody("_obj");
783
+ expect(symbolTable.isOpaqueType("obj_t")).toBe(true);
784
+ expect(symbolTable.getAllStructTagsWithBodies()).not.toContain("_obj");
785
+ });
786
+
787
+ it("clearStructTagHasBody is a no-op when the tag has no body", () => {
788
+ // Must not throw or spuriously mutate when the tag was never marked.
789
+ expect(() => symbolTable.clearStructTagHasBody("_never")).not.toThrow();
790
+ expect(symbolTable.getAllStructTagsWithBodies()).not.toContain("_never");
791
+ });
792
+
793
+ it("getStructTagForTypedef returns the aliased tag or undefined", () => {
794
+ symbolTable.registerStructTagAlias("_lv_obj_t", "lv_obj_t");
795
+ expect(symbolTable.getStructTagForTypedef("lv_obj_t")).toBe("_lv_obj_t");
796
+ expect(symbolTable.getStructTagForTypedef("unknown_t")).toBeUndefined();
797
+ });
798
+ });
799
+
800
+ // ========================================================================
801
+ // External Declaration Names (Issue #985 macro recovery)
802
+ // ========================================================================
803
+
804
+ describe("External Declaration Names", () => {
805
+ it("registers and looks up recovered function-like macro names", () => {
806
+ expect(symbolTable.hasExternalDeclaration("pdMS_TO_TICKS")).toBe(false);
807
+ symbolTable.addExternalDeclarationNames(
808
+ new Set(["pdMS_TO_TICKS", "portTICK_PERIOD_MS"]),
809
+ );
810
+ expect(symbolTable.hasExternalDeclaration("pdMS_TO_TICKS")).toBe(true);
811
+ expect(symbolTable.hasExternalDeclaration("portTICK_PERIOD_MS")).toBe(
812
+ true,
813
+ );
814
+ expect(symbolTable.hasExternalDeclaration("not_recovered")).toBe(false);
815
+ });
816
+
817
+ it("accumulates across calls and tolerates an empty set", () => {
818
+ symbolTable.addExternalDeclarationNames(new Set(["A"]));
819
+ symbolTable.addExternalDeclarationNames(new Set());
820
+ symbolTable.addExternalDeclarationNames(new Set(["B"]));
821
+ expect(symbolTable.hasExternalDeclaration("A")).toBe(true);
822
+ expect(symbolTable.hasExternalDeclaration("B")).toBe(true);
823
+ });
775
824
  });
776
825
 
777
826
  // ========================================================================
@@ -785,14 +834,16 @@ describe("SymbolTable", () => {
785
834
  expect(symbolTable.isTypedefStructType("other_t")).toBe(false);
786
835
  });
787
836
 
788
- it("should always return true for typedef struct types (additive only)", () => {
789
- // Issue #958: typedef struct types are never unmarked
837
+ it("should return false when underlying struct tag has body (issue #948)", () => {
838
+ // Issue #948: Query-time resolution - if the underlying struct tag
839
+ // has a full body definition, this is NOT an external typedef struct
790
840
  symbolTable.markTypedefStructType("point_t", "point.h");
791
841
  expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
792
- // Even after marking the body, typedef struct type stays marked
842
+ // After marking the body, typedef struct type should return false
843
+ // because it's now a complete type (value semantics, not pointer)
793
844
  symbolTable.registerStructTagAlias("_point", "point_t");
794
845
  symbolTable.markStructTagHasBody("_point");
795
- expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
846
+ expect(symbolTable.isTypedefStructType("point_t")).toBe(false);
796
847
  });
797
848
 
798
849
  it("should get all typedef struct types", () => {
@@ -75,16 +75,28 @@ class VariableCollector {
75
75
  }
76
76
 
77
77
  /**
78
- * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr).
78
+ * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr, u16[] arr).
79
+ * Handles size inference from initializer when dimension is empty.
79
80
  */
80
81
  private static collectArrayTypeDimensions(
81
82
  arrayTypeCtx: Parser.ArrayTypeContext,
82
83
  constValues: Map<string, number> | undefined,
84
+ initExpr: Parser.ExpressionContext | null,
83
85
  ): (number | string)[] {
84
86
  const dimensions: (number | string)[] = [];
85
87
  for (const dim of arrayTypeCtx.arrayTypeDimension()) {
86
88
  const sizeExpr = dim.expression();
87
- if (!sizeExpr) continue;
89
+
90
+ if (!sizeExpr) {
91
+ // Issue #636: Empty dimension [] - infer size from array initializer
92
+ if (initExpr) {
93
+ const inferredSize = ArrayInitializerUtils.getInferredSize(initExpr);
94
+ if (inferredSize !== undefined) {
95
+ dimensions.push(inferredSize);
96
+ }
97
+ }
98
+ continue;
99
+ }
88
100
 
89
101
  const dimText = sizeExpr.getText();
90
102
  const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
@@ -146,6 +158,7 @@ class VariableCollector {
146
158
  ...VariableCollector.collectArrayTypeDimensions(
147
159
  arrayTypeCtx,
148
160
  constValues,
161
+ initExpr,
149
162
  ),
150
163
  );
151
164
  }
@@ -6,14 +6,16 @@ import * as Parser from "../../../parser/grammar/CNextParser";
6
6
  import CNEXT_TO_C_TYPE_MAP from "../../../../../utils/constants/TypeMappings";
7
7
 
8
8
  /**
9
- * Resolve scoped type (this.Type) to full name.
9
+ * Common interface for type contexts that share the same type accessors.
10
+ * Both TypeContext and ArrayTypeContext have these methods.
10
11
  */
11
- function resolveScopedType(
12
- scopedTypeCtx: Parser.ScopedTypeContext,
13
- scopeName?: string,
14
- ): string {
15
- const typeName = scopedTypeCtx.IDENTIFIER().getText();
16
- return scopeName ? `${scopeName}_${typeName}` : typeName;
12
+ interface ITypeAccessors {
13
+ primitiveType(): Parser.PrimitiveTypeContext | null;
14
+ userType(): Parser.UserTypeContext | null;
15
+ stringType(): Parser.StringTypeContext | null;
16
+ scopedType(): Parser.ScopedTypeContext | null;
17
+ qualifiedType(): Parser.QualifiedTypeContext | null;
18
+ globalType(): Parser.GlobalTypeContext | null;
17
19
  }
18
20
 
19
21
  /**
@@ -25,19 +27,50 @@ function resolveStringType(stringCtx: Parser.StringTypeContext): string {
25
27
  }
26
28
 
27
29
  /**
28
- * Extract inner type from arrayType context.
30
+ * Dispatch type resolution for contexts that share common type accessors.
31
+ * Handles scoped, qualified, global, primitive, string, and user types.
32
+ * Used by both bare type contexts and array element type contexts.
33
+ *
34
+ * @returns The resolved type name, or null if no matching type accessor found
29
35
  */
30
- function resolveArrayInnerType(arrayTypeCtx: Parser.ArrayTypeContext): string {
31
- if (arrayTypeCtx.primitiveType()) {
32
- return arrayTypeCtx.primitiveType()!.getText();
36
+ function dispatchTypeResolution(
37
+ accessors: ITypeAccessors,
38
+ scopeName?: string,
39
+ ): string | null {
40
+ // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
41
+ if (accessors.scopedType()) {
42
+ const typeName = accessors.scopedType()!.IDENTIFIER().getText();
43
+ return scopeName ? `${scopeName}_${typeName}` : typeName;
44
+ }
45
+
46
+ // Handle global.Type for global types inside scope
47
+ // global.ECategory -> ECategory (just the type name, no scope prefix)
48
+ if (accessors.globalType()) {
49
+ return accessors.globalType()!.IDENTIFIER().getText();
33
50
  }
34
- if (arrayTypeCtx.userType()) {
35
- return arrayTypeCtx.userType()!.getText();
51
+
52
+ // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
53
+ if (accessors.qualifiedType()) {
54
+ const identifiers = accessors.qualifiedType()!.IDENTIFIER();
55
+ return identifiers.map((id) => id.getText()).join("_");
56
+ }
57
+
58
+ // Handle user-defined types
59
+ if (accessors.userType()) {
60
+ return accessors.userType()!.getText();
36
61
  }
37
- // Fallback for other nested types - strip the dimension part
38
- const text = arrayTypeCtx.getText();
39
- const bracketIdx = text.indexOf("[");
40
- return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
62
+
63
+ // Handle primitive types
64
+ if (accessors.primitiveType()) {
65
+ return accessors.primitiveType()!.getText();
66
+ }
67
+
68
+ // Handle string types - preserve capacity for validation (Issue #139)
69
+ if (accessors.stringType()) {
70
+ return resolveStringType(accessors.stringType()!);
71
+ }
72
+
73
+ return null;
41
74
  }
42
75
 
43
76
  class TypeUtils {
@@ -56,42 +89,23 @@ class TypeUtils {
56
89
  ): string {
57
90
  if (!ctx) return "void";
58
91
 
59
- // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
60
- if (ctx.scopedType()) {
61
- return resolveScopedType(ctx.scopedType()!, scopeName);
62
- }
63
-
64
- // Issue #478: Handle global.Type for global types inside scope
65
- // global.ECategory -> ECategory (just the type name, no scope prefix)
66
- if (ctx.globalType()) {
67
- return ctx.globalType()!.IDENTIFIER().getText();
68
- }
69
-
70
- // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
71
- if (ctx.qualifiedType()) {
72
- const identifiers = ctx.qualifiedType()!.IDENTIFIER();
73
- return identifiers.map((id) => id.getText()).join("_");
74
- }
75
-
76
- // Handle user-defined types
77
- if (ctx.userType()) {
78
- return ctx.userType()!.getText();
79
- }
80
-
81
- // Handle primitive types
82
- if (ctx.primitiveType()) {
83
- return ctx.primitiveType()!.getText();
84
- }
85
-
86
- // Handle string types - preserve capacity for validation (Issue #139)
87
- if (ctx.stringType()) {
88
- return resolveStringType(ctx.stringType()!);
89
- }
90
-
91
92
  // Handle arrayType: Type[size] - extract the inner type without dimension
92
93
  // The dimension is tracked separately in arrayDimensions
93
94
  if (ctx.arrayType()) {
94
- return resolveArrayInnerType(ctx.arrayType()!);
95
+ const result = dispatchTypeResolution(ctx.arrayType()!, scopeName);
96
+ if (result !== null) {
97
+ return result;
98
+ }
99
+ // Fallback for unrecognized array types - strip the dimension part
100
+ const text = ctx.arrayType()!.getText();
101
+ const bracketIdx = text.indexOf("[");
102
+ return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
103
+ }
104
+
105
+ // Non-array types - dispatch directly
106
+ const result = dispatchTypeResolution(ctx, scopeName);
107
+ if (result !== null) {
108
+ return result;
95
109
  }
96
110
 
97
111
  // Fallback
@@ -0,0 +1,52 @@
1
+ /**
2
+ * MISRA Suppression Utilities
3
+ *
4
+ * Issue #850: Shared helpers for emitting MISRA inline suppression comments.
5
+ * Used by both CodeGenerator (for .c files) and HeaderGeneratorUtils (for .h files).
6
+ */
7
+
8
+ /**
9
+ * Headers that violate MISRA C:2012 rules and need inline suppression.
10
+ * Maps header name to the MISRA rule it violates.
11
+ */
12
+ const MISRA_BANNED_HEADERS: ReadonlyMap<string, string> = new Map([
13
+ // MISRA Rule 21.6: Standard library I/O functions shall not be used
14
+ ["stdio.h", "misra-c2012-21.6"],
15
+ ]);
16
+
17
+ /**
18
+ * Regex to extract header name from angle-bracket includes.
19
+ * Uses possessive matching via atomic group simulation to avoid backtracking.
20
+ * Matches: #include <header.h> -> captures "header.h"
21
+ */
22
+ const ANGLE_BRACKET_INCLUDE_REGEX = /<([^<>]+)>/;
23
+
24
+ /**
25
+ * Check if an include directive needs MISRA suppression.
26
+ * @param includeText The full include directive (e.g., "#include <stdio.h>")
27
+ * @returns true if suppression is needed
28
+ */
29
+ function needsMisraSuppression(includeText: string): boolean {
30
+ const match = ANGLE_BRACKET_INCLUDE_REGEX.exec(includeText);
31
+ if (!match) return false;
32
+ return MISRA_BANNED_HEADERS.has(match[1]);
33
+ }
34
+
35
+ /**
36
+ * Get the MISRA suppression comment for an include directive.
37
+ * @param includeText The full include directive (e.g., "#include <stdio.h>")
38
+ * @returns The suppression comment, or null if not needed
39
+ */
40
+ function getMisraSuppressionComment(includeText: string): string | null {
41
+ const match = ANGLE_BRACKET_INCLUDE_REGEX.exec(includeText);
42
+ if (!match) return null;
43
+ const rule = MISRA_BANNED_HEADERS.get(match[1]);
44
+ return rule ? `// cppcheck-suppress ${rule}` : null;
45
+ }
46
+
47
+ const MisraSuppressionUtils = {
48
+ needsMisraSuppression,
49
+ getMisraSuppressionComment,
50
+ };
51
+
52
+ export default MisraSuppressionUtils;
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import MisraSuppressionUtils from "../MisraSuppressionUtils";
3
+
4
+ describe("MisraSuppressionUtils", () => {
5
+ describe("needsMisraSuppression", () => {
6
+ it("returns true for stdio.h", () => {
7
+ expect(
8
+ MisraSuppressionUtils.needsMisraSuppression("#include <stdio.h>"),
9
+ ).toBe(true);
10
+ });
11
+
12
+ it("returns false for other system headers", () => {
13
+ expect(
14
+ MisraSuppressionUtils.needsMisraSuppression("#include <stdint.h>"),
15
+ ).toBe(false);
16
+ expect(
17
+ MisraSuppressionUtils.needsMisraSuppression("#include <string.h>"),
18
+ ).toBe(false);
19
+ });
20
+
21
+ it("returns false for quote includes", () => {
22
+ expect(
23
+ MisraSuppressionUtils.needsMisraSuppression('#include "stdio.h"'),
24
+ ).toBe(false);
25
+ });
26
+
27
+ it("returns false for non-include text", () => {
28
+ expect(MisraSuppressionUtils.needsMisraSuppression("void foo();")).toBe(
29
+ false,
30
+ );
31
+ });
32
+ });
33
+
34
+ describe("getMisraSuppressionComment", () => {
35
+ it("returns suppression comment for stdio.h", () => {
36
+ expect(
37
+ MisraSuppressionUtils.getMisraSuppressionComment("#include <stdio.h>"),
38
+ ).toBe("// cppcheck-suppress misra-c2012-21.6");
39
+ });
40
+
41
+ it("returns null for other system headers", () => {
42
+ expect(
43
+ MisraSuppressionUtils.getMisraSuppressionComment("#include <stdint.h>"),
44
+ ).toBeNull();
45
+ });
46
+
47
+ it("returns null for quote includes", () => {
48
+ expect(
49
+ MisraSuppressionUtils.getMisraSuppressionComment('#include "stdio.h"'),
50
+ ).toBeNull();
51
+ });
52
+
53
+ it("returns null for non-include text", () => {
54
+ expect(
55
+ MisraSuppressionUtils.getMisraSuppressionComment("void foo();"),
56
+ ).toBeNull();
57
+ });
58
+
59
+ it("handles whitespace in include directives", () => {
60
+ expect(
61
+ MisraSuppressionUtils.getMisraSuppressionComment(
62
+ "#include <stdio.h> ",
63
+ ),
64
+ ).toBe("// cppcheck-suppress misra-c2012-21.6");
65
+ });
66
+ });
67
+ });