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.
- package/README.md +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- 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
|
-
|
|
1085
|
-
|
|
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
|
|
789
|
-
// Issue #
|
|
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
|
-
//
|
|
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(
|
|
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
|
-
|
|
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
|
-
*
|
|
9
|
+
* Common interface for type contexts that share the same type accessors.
|
|
10
|
+
* Both TypeContext and ArrayTypeContext have these methods.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
):
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
*
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
+
});
|