c-next 0.2.17 → 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 +2 -2
- package/dist/index.js +7645 -5941
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +8 -0
- package/package.json +1 -3
- package/src/transpiler/Transpiler.ts +286 -26
- 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 +8 -0
- 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/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -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 +1257 -1185
- 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 +45 -0
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +49 -0
- 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 +45 -4
- 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.test.ts +13 -1
- 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 +86 -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/expressions/CallExprGenerator.ts +28 -15
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- 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/StringDeclHelper.ts +16 -13
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +57 -11
- 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/headers/HeaderGeneratorUtils.ts +65 -24
- package/src/transpiler/state/CodeGenState.ts +20 -16
- package/src/transpiler/types/ICachedFileEntry.ts +7 -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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Issue #579: Shared subscript classifier for array vs bit access
|
|
2
|
+
* Issue #579 / Issue #1100: Shared subscript classifier for array vs bit access
|
|
3
3
|
*
|
|
4
4
|
* This utility unifies the classification logic used by:
|
|
5
5
|
* - AssignmentClassifier (assignment path)
|
|
@@ -7,8 +7,28 @@
|
|
|
7
7
|
*
|
|
8
8
|
* The classification rule is:
|
|
9
9
|
* 1. If isArray or isString -> array access
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
10
|
+
* 2. Otherwise -> bit manipulation
|
|
11
|
+
*
|
|
12
|
+
* This rule is identical for parameters and local variables. ADR-006 requires
|
|
13
|
+
* array parameters to use explicit array syntax (`u8[8] buf`), which sets
|
|
14
|
+
* isArray on the parameter's type info exactly like a local array declaration
|
|
15
|
+
* does. A scalar-typed parameter (no array brackets) is therefore classified
|
|
16
|
+
* the same way a scalar local variable is: bit manipulation (ADR-007 — "Any
|
|
17
|
+
* integer type can be indexed to access individual bits", with no carve-out
|
|
18
|
+
* for parameters).
|
|
19
|
+
*
|
|
20
|
+
* Issue #1100: A prior version of this classifier treated ANY function
|
|
21
|
+
* parameter without explicit array syntax as array access (`isParameter &&
|
|
22
|
+
* !isArray -> array`), added in #579 to support buffer-style parameters
|
|
23
|
+
* declared without brackets (e.g. `void fillBuffer(u8 buf)`). That blanket
|
|
24
|
+
* rule was a divergent decision path from local-variable classification: it
|
|
25
|
+
* silently broke ADR-007 bit-indexing for scalar parameters (`u32 v; ...
|
|
26
|
+
* v[4]` inside a function became `v[4]` array-subscript C code on a pointer
|
|
27
|
+
* instead of a shift-and-mask bit read), producing invalid/incorrect C
|
|
28
|
+
* for the extremely common embedded pattern of reading a bit out of a
|
|
29
|
+
* hardware-register-shaped scalar parameter. Buffer-style parameters must now
|
|
30
|
+
* use explicit array syntax (`u8[N] buf`), which was already the ADR-006
|
|
31
|
+
* documented and supported spelling — see tests/params/param-array-indexing.test.cnx.
|
|
12
32
|
*/
|
|
13
33
|
import TSubscriptKind from "./TSubscriptKind";
|
|
14
34
|
import TTypeInfo from "../types/TTypeInfo";
|
|
@@ -63,9 +83,14 @@ class SubscriptClassifier {
|
|
|
63
83
|
* Determine if a type should use array access semantics.
|
|
64
84
|
*
|
|
65
85
|
* Array access is used when:
|
|
66
|
-
* - Type is explicitly an array (isArray: true)
|
|
86
|
+
* - Type is explicitly an array (isArray: true) — including array
|
|
87
|
+
* parameters declared with explicit syntax, e.g. `u8[8] buf` (ADR-006)
|
|
67
88
|
* - Type is a string (strings are char arrays)
|
|
68
|
-
*
|
|
89
|
+
*
|
|
90
|
+
* Parameters are NOT special-cased here (Issue #1100): whether a subscript
|
|
91
|
+
* is array access or bit access depends solely on the declared type, the
|
|
92
|
+
* same rule used for local variables. A scalar parameter without array
|
|
93
|
+
* syntax is bit-indexable exactly like a scalar local variable (ADR-007).
|
|
69
94
|
*
|
|
70
95
|
* @param typeInfo - Type information, or null if unknown
|
|
71
96
|
* @returns true if subscript should be treated as array access
|
|
@@ -82,12 +107,6 @@ class SubscriptClassifier {
|
|
|
82
107
|
return true;
|
|
83
108
|
}
|
|
84
109
|
|
|
85
|
-
// Issue #579: Non-array parameter becomes pointer in C (ADR-006)
|
|
86
|
-
// So buf[i] is array access, not bit access
|
|
87
|
-
if (typeInfo.isParameter) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
110
|
// Otherwise it's a scalar - use bit access
|
|
92
111
|
return false;
|
|
93
112
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
type TSubscriptKind =
|
|
8
8
|
| "array_element" // Single array element access: arr[i]
|
|
9
|
-
| "array_slice" // Array slice
|
|
9
|
+
| "array_slice" // Array slice: arr[offset, length] (per-element little-endian writes, ADR-007/#1081)
|
|
10
10
|
| "bit_single" // Single bit access: flags[3]
|
|
11
11
|
| "bit_range"; // Bit range extraction: value[start, width]
|
|
12
12
|
|
|
@@ -74,8 +74,13 @@ describe("SubscriptClassifier", () => {
|
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
describe("parameter types (Issue #
|
|
78
|
-
|
|
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("
|
|
96
|
+
expect(result).toBe("bit_single");
|
|
92
97
|
});
|
|
93
98
|
|
|
94
|
-
it("returns
|
|
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("
|
|
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
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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 =
|
|
@@ -30,6 +30,7 @@ import ICallbackTypeInfo from "../output/codegen/types/ICallbackTypeInfo";
|
|
|
30
30
|
import ITargetCapabilities from "../output/codegen/types/ITargetCapabilities";
|
|
31
31
|
import TOverflowBehavior from "../output/codegen/types/TOverflowBehavior";
|
|
32
32
|
import TYPE_WIDTH from "../output/codegen/types/TYPE_WIDTH";
|
|
33
|
+
import type ICodeGenApi from "../output/codegen/types/ICodeGenApi";
|
|
33
34
|
import TypeResolver from "../../utils/TypeResolver";
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -73,10 +74,26 @@ export default class CodeGenState {
|
|
|
73
74
|
// ===========================================================================
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Reference to the CodeGenerator instance for handlers to call methods
|
|
77
|
-
*
|
|
77
|
+
* Reference to the CodeGenerator instance for handlers to call its methods,
|
|
78
|
+
* typed to the method subset they use (ICodeGenApi). `import type` keeps this a
|
|
79
|
+
* compile-time-only edge, and CodeGenState already imports sibling types from
|
|
80
|
+
* output/codegen/types, so this introduces no runtime/circular dependency.
|
|
78
81
|
*/
|
|
79
|
-
static generator:
|
|
82
|
+
static generator: ICodeGenApi | null = null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The CodeGenerator instance, asserted present. Handlers call this instead of
|
|
86
|
+
* each casting `generator` themselves — one source of truth for the access and
|
|
87
|
+
* null-check (the generator is always set before any handler runs).
|
|
88
|
+
*/
|
|
89
|
+
static requireGenerator(): ICodeGenApi {
|
|
90
|
+
if (CodeGenState.generator === null) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"CodeGenState.generator is not set; codegen accessed before initialization.",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return CodeGenState.generator;
|
|
96
|
+
}
|
|
80
97
|
|
|
81
98
|
// ===========================================================================
|
|
82
99
|
// SYMBOL DATA (read-only after initialization)
|
|
@@ -142,9 +159,6 @@ export default class CodeGenState {
|
|
|
142
159
|
/** Tracks which parameters are modified (directly or transitively) */
|
|
143
160
|
static modifiedParameters: Map<string, Set<string>> = new Map();
|
|
144
161
|
|
|
145
|
-
/** Issue #579: Parameters with subscript access (must become pointers) */
|
|
146
|
-
static subscriptAccessedParameters: Map<string, Set<string>> = new Map();
|
|
147
|
-
|
|
148
162
|
/** Parameters that should pass by value (small, unmodified primitives) */
|
|
149
163
|
static passByValueParams: Map<string, Set<string>> = new Map();
|
|
150
164
|
|
|
@@ -363,7 +377,6 @@ export default class CodeGenState {
|
|
|
363
377
|
|
|
364
378
|
// Pass-by-value analysis
|
|
365
379
|
this.modifiedParameters = new Map();
|
|
366
|
-
this.subscriptAccessedParameters = new Map();
|
|
367
380
|
this.passByValueParams = new Map();
|
|
368
381
|
this.functionCallGraph = new Map();
|
|
369
382
|
this.functionParamLists = new Map();
|
|
@@ -770,15 +783,6 @@ export default class CodeGenState {
|
|
|
770
783
|
return this.passByValueParams.get(funcName)?.has(paramName) ?? false;
|
|
771
784
|
}
|
|
772
785
|
|
|
773
|
-
/**
|
|
774
|
-
* Check if a parameter has subscript access.
|
|
775
|
-
*/
|
|
776
|
-
static hasSubscriptAccess(funcName: string, paramName: string): boolean {
|
|
777
|
-
return (
|
|
778
|
-
this.subscriptAccessedParameters.get(funcName)?.has(paramName) ?? false
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
786
|
/**
|
|
783
787
|
* Get the function signature for a function.
|
|
784
788
|
*/
|
|
@@ -26,6 +26,13 @@ interface ICachedFileEntry {
|
|
|
26
26
|
structTagAliases?: Array<[string, string]>;
|
|
27
27
|
/** Issue #958: Struct tags that have full definitions (bodies) */
|
|
28
28
|
structTagsWithBodies?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Issue #985: This header could not be preprocessed standalone and fell back
|
|
31
|
+
* to raw content, so its cached symbols are degraded (phantom struct bodies,
|
|
32
|
+
* missing/by-value framework signatures). Persisted so a warm-cache build
|
|
33
|
+
* still triggers the external-declaration recovery pass and re-applies the fix.
|
|
34
|
+
*/
|
|
35
|
+
preprocessFailed?: boolean;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
export default ICachedFileEntry;
|
|
@@ -32,7 +32,7 @@ import ESourceLanguage from "../types/ESourceLanguage";
|
|
|
32
32
|
const defaultFs = NodeFileSystem.instance;
|
|
33
33
|
|
|
34
34
|
/** Current cache format version - increment when serialization format changes */
|
|
35
|
-
const CACHE_VERSION =
|
|
35
|
+
const CACHE_VERSION = 8; // Issue #985: Add preprocessFailed marker to cache
|
|
36
36
|
|
|
37
37
|
const TRANSPILER_VERSION = packageJson.version;
|
|
38
38
|
|
|
@@ -138,6 +138,7 @@ class CacheManager {
|
|
|
138
138
|
typedefStructTypes: Array<[string, string]>;
|
|
139
139
|
structTagAliases: Array<[string, string]>;
|
|
140
140
|
structTagsWithBodies: string[];
|
|
141
|
+
preprocessFailed: boolean;
|
|
141
142
|
} | null {
|
|
142
143
|
if (!this.cache) return null;
|
|
143
144
|
|
|
@@ -181,6 +182,7 @@ class CacheManager {
|
|
|
181
182
|
typedefStructTypes: cachedEntry.typedefStructTypes ?? [],
|
|
182
183
|
structTagAliases: cachedEntry.structTagAliases ?? [],
|
|
183
184
|
structTagsWithBodies: cachedEntry.structTagsWithBodies ?? [],
|
|
185
|
+
preprocessFailed: cachedEntry.preprocessFailed ?? false,
|
|
184
186
|
};
|
|
185
187
|
}
|
|
186
188
|
|
|
@@ -199,6 +201,7 @@ class CacheManager {
|
|
|
199
201
|
typedefStructTypes?: Array<[string, string]>;
|
|
200
202
|
structTagAliases?: Array<[string, string]>;
|
|
201
203
|
structTagsWithBodies?: string[];
|
|
204
|
+
preprocessFailed?: boolean;
|
|
202
205
|
},
|
|
203
206
|
): void {
|
|
204
207
|
if (!this.cache) return;
|
|
@@ -247,6 +250,7 @@ class CacheManager {
|
|
|
247
250
|
typedefStructTypes: options?.typedefStructTypes,
|
|
248
251
|
structTagAliases: options?.structTagAliases,
|
|
249
252
|
structTagsWithBodies: options?.structTagsWithBodies,
|
|
253
|
+
preprocessFailed: options?.preprocessFailed,
|
|
250
254
|
};
|
|
251
255
|
|
|
252
256
|
this.cache.setKey(filePath, entry);
|
|
@@ -263,8 +267,14 @@ class CacheManager {
|
|
|
263
267
|
*
|
|
264
268
|
* @param filePath - Path to the file being cached
|
|
265
269
|
* @param symbolTable - SymbolTable containing all parsed symbols
|
|
270
|
+
* @param preprocessFailed - Issue #985: header fell back to raw content, so its
|
|
271
|
+
* symbols are degraded and a warm-cache build must re-run recovery
|
|
266
272
|
*/
|
|
267
|
-
setSymbolsFromTable(
|
|
273
|
+
setSymbolsFromTable(
|
|
274
|
+
filePath: string,
|
|
275
|
+
symbolTable: SymbolTable,
|
|
276
|
+
preprocessFailed = false,
|
|
277
|
+
): void {
|
|
268
278
|
// ADR-055 Phase 7: Serialize TAnySymbol directly to ISerializedSymbol
|
|
269
279
|
const typedSymbols = symbolTable.getSymbolsByFile(filePath);
|
|
270
280
|
const symbols = typedSymbols.map((s) => this.serializeTypedSymbol(s));
|
|
@@ -302,6 +312,7 @@ class CacheManager {
|
|
|
302
312
|
typedefStructTypes,
|
|
303
313
|
structTagAliases,
|
|
304
314
|
structTagsWithBodies,
|
|
315
|
+
preprocessFailed,
|
|
305
316
|
});
|
|
306
317
|
}
|
|
307
318
|
|
|
@@ -200,6 +200,23 @@ describe("CacheManager", () => {
|
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
it("defaults preprocessFailed to false and round-trips it when set", async () => {
|
|
204
|
+
const cleanFile = join(testDir, "clean.h");
|
|
205
|
+
const failedFile = join(testDir, "failed.h");
|
|
206
|
+
writeFileSync(cleanFile, "// test");
|
|
207
|
+
writeFileSync(failedFile, "// test");
|
|
208
|
+
|
|
209
|
+
// Issue #985: a header that fell back to raw content records that fact so
|
|
210
|
+
// a warm-cache build re-runs external-declaration recovery.
|
|
211
|
+
cacheManager.setSymbols(cleanFile, [], new Map());
|
|
212
|
+
cacheManager.setSymbols(failedFile, [], new Map(), {
|
|
213
|
+
preprocessFailed: true,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(cacheManager.getSymbols(cleanFile)!.preprocessFailed).toBe(false);
|
|
217
|
+
expect(cacheManager.getSymbols(failedFile)!.preprocessFailed).toBe(true);
|
|
218
|
+
});
|
|
219
|
+
|
|
203
220
|
it("should preserve optional symbol fields", async () => {
|
|
204
221
|
const testFile = join(testDir, "test.h");
|
|
205
222
|
writeFileSync(testFile, "// test");
|
|
@@ -1494,7 +1511,7 @@ describe("CacheManager", () => {
|
|
|
1494
1511
|
const content = mockFs.getWrittenContent("/project/.cnx/config.json");
|
|
1495
1512
|
expect(content).toBeDefined();
|
|
1496
1513
|
const newConfig = JSON.parse(content!);
|
|
1497
|
-
expect(newConfig.version).toBe(
|
|
1514
|
+
expect(newConfig.version).toBe(8); // Current CACHE_VERSION (Issue #985 preprocessFailed marker)
|
|
1498
1515
|
});
|
|
1499
1516
|
|
|
1500
1517
|
it("should not cache files that do not exist in IFileSystem", async () => {
|
|
@@ -42,6 +42,19 @@ class TypeConstants {
|
|
|
42
42
|
* - ArrayIndexTypeAnalyzer: detecting signed integer subscript indexes
|
|
43
43
|
*/
|
|
44
44
|
static readonly SIGNED_TYPES: readonly string[] = ["i8", "i16", "i32", "i64"];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Unsigned integer types (excludes `bool`, unlike `UNSIGNED_INDEX_TYPES`).
|
|
48
|
+
*
|
|
49
|
+
* Used by:
|
|
50
|
+
* - MixedTypeCategoryAnalyzer: classifying an operand's essential type category
|
|
51
|
+
*/
|
|
52
|
+
static readonly UNSIGNED_INT_TYPES: readonly string[] = [
|
|
53
|
+
"u8",
|
|
54
|
+
"u16",
|
|
55
|
+
"u32",
|
|
56
|
+
"u64",
|
|
57
|
+
];
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
export default TypeConstants;
|