c-next 0.2.2 → 0.2.4
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 +59 -57
- package/dist/index.js +641 -191
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/cli/Runner.ts +1 -1
- package/src/cli/__tests__/Runner.test.ts +8 -8
- package/src/cli/serve/ServeCommand.ts +29 -9
- package/src/transpiler/Transpiler.ts +105 -200
- package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
- package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
- package/src/transpiler/data/IncludeResolver.ts +11 -3
- package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
- package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +170 -14
- package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +327 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
- package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
- package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
- package/src/transpiler/logic/symbols/c/index.ts +50 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +99 -2
- package/src/transpiler/logic/symbols/c/utils/__tests__/DeclaratorUtils.test.ts +128 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +31 -5
- package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +49 -36
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +90 -25
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +10 -7
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
- package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +6 -3
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +36 -22
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +9 -1
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +8 -6
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
- package/src/transpiler/state/CodeGenState.ts +6 -0
- package/src/transpiler/types/IPipelineFile.ts +2 -2
- package/src/transpiler/types/IPipelineInput.ts +1 -1
- package/src/transpiler/types/TTranspileInput.ts +18 -0
- package/src/utils/constants/TypeConstants.ts +22 -0
|
@@ -16,8 +16,8 @@ describe("Transpiler", () => {
|
|
|
16
16
|
mockFs = new MockFileSystem();
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
describe("
|
|
20
|
-
//
|
|
19
|
+
describe("transpile source mode (primary unit testing target)", () => {
|
|
20
|
+
// transpile({ kind: 'source' }) is the main API that benefits from MockFileSystem
|
|
21
21
|
// because it doesn't rely on FileDiscovery for the main code path
|
|
22
22
|
|
|
23
23
|
it("transpiles source string without file I/O", async () => {
|
|
@@ -26,9 +26,12 @@ describe("Transpiler", () => {
|
|
|
26
26
|
mockFs,
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
const result =
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const result = (
|
|
30
|
+
await transpiler.transpile({
|
|
31
|
+
kind: "source",
|
|
32
|
+
source: "u32 add(u32 a, u32 b) { return a + b; }",
|
|
33
|
+
})
|
|
34
|
+
).files[0];
|
|
32
35
|
|
|
33
36
|
expect(result.success).toBe(true);
|
|
34
37
|
expect(result.code).toContain("uint32_t");
|
|
@@ -41,7 +44,10 @@ describe("Transpiler", () => {
|
|
|
41
44
|
mockFs,
|
|
42
45
|
);
|
|
43
46
|
|
|
44
|
-
const result = await transpiler.
|
|
47
|
+
const result = await transpiler.transpile({
|
|
48
|
+
kind: "source",
|
|
49
|
+
source: "@@@invalid",
|
|
50
|
+
});
|
|
45
51
|
|
|
46
52
|
expect(result.success).toBe(false);
|
|
47
53
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
@@ -53,11 +59,16 @@ describe("Transpiler", () => {
|
|
|
53
59
|
mockFs,
|
|
54
60
|
);
|
|
55
61
|
|
|
56
|
-
const result =
|
|
62
|
+
const result = (
|
|
63
|
+
await transpiler.transpile({
|
|
64
|
+
kind: "source",
|
|
65
|
+
source: `
|
|
57
66
|
scope API {
|
|
58
67
|
public void doSomething() { }
|
|
59
68
|
}
|
|
60
|
-
|
|
69
|
+
`,
|
|
70
|
+
})
|
|
71
|
+
).files[0];
|
|
61
72
|
|
|
62
73
|
expect(result.success).toBe(true);
|
|
63
74
|
expect(result.headerCode).toBeDefined();
|
|
@@ -70,9 +81,12 @@ describe("Transpiler", () => {
|
|
|
70
81
|
mockFs,
|
|
71
82
|
);
|
|
72
83
|
|
|
73
|
-
const result =
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
const result = (
|
|
85
|
+
await transpiler.transpile({
|
|
86
|
+
kind: "source",
|
|
87
|
+
source: "void privateFunc() { }",
|
|
88
|
+
})
|
|
89
|
+
).files[0];
|
|
76
90
|
|
|
77
91
|
expect(result.success).toBe(true);
|
|
78
92
|
expect(result.headerCode).toBeUndefined();
|
|
@@ -84,9 +98,12 @@ describe("Transpiler", () => {
|
|
|
84
98
|
mockFs,
|
|
85
99
|
);
|
|
86
100
|
|
|
87
|
-
const result =
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
const result = (
|
|
102
|
+
await transpiler.transpile({
|
|
103
|
+
kind: "source",
|
|
104
|
+
source: "u32 test() { return 42; }",
|
|
105
|
+
})
|
|
106
|
+
).files[0];
|
|
90
107
|
|
|
91
108
|
expect(result.success).toBe(true);
|
|
92
109
|
expect(result.code).toBe("");
|
|
@@ -99,9 +116,12 @@ describe("Transpiler", () => {
|
|
|
99
116
|
);
|
|
100
117
|
|
|
101
118
|
// This should parse but might have semantic issues
|
|
102
|
-
const result =
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
const result = (
|
|
120
|
+
await transpiler.transpile({
|
|
121
|
+
kind: "source",
|
|
122
|
+
source: "void test() { undefinedVar <- 5; }",
|
|
123
|
+
})
|
|
124
|
+
).files[0];
|
|
105
125
|
|
|
106
126
|
// Should still succeed (undefined vars become C identifiers)
|
|
107
127
|
expect(result.success).toBe(true);
|
|
@@ -113,9 +133,12 @@ describe("Transpiler", () => {
|
|
|
113
133
|
mockFs,
|
|
114
134
|
);
|
|
115
135
|
|
|
116
|
-
const result =
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
const result = (
|
|
137
|
+
await transpiler.transpile({
|
|
138
|
+
kind: "source",
|
|
139
|
+
source: `void test() {\n u32 large <- 1000;\n u8 small <- large;\n}`,
|
|
140
|
+
})
|
|
141
|
+
).files[0];
|
|
119
142
|
|
|
120
143
|
expect(result.success).toBe(false);
|
|
121
144
|
expect(result.errors).toHaveLength(1);
|
|
@@ -131,9 +154,12 @@ describe("Transpiler", () => {
|
|
|
131
154
|
);
|
|
132
155
|
|
|
133
156
|
// Ternary with bare variable produces error without line prefix
|
|
134
|
-
const result =
|
|
135
|
-
|
|
136
|
-
|
|
157
|
+
const result = (
|
|
158
|
+
await transpiler.transpile({
|
|
159
|
+
kind: "source",
|
|
160
|
+
source: `void test() { u32 x <- 5; u32 r <- (x) ? 1 : 0; }`,
|
|
161
|
+
})
|
|
162
|
+
).files[0];
|
|
137
163
|
|
|
138
164
|
expect(result.success).toBe(false);
|
|
139
165
|
expect(result.errors[0].line).toBe(1);
|
|
@@ -147,7 +173,10 @@ describe("Transpiler", () => {
|
|
|
147
173
|
mockFs,
|
|
148
174
|
);
|
|
149
175
|
|
|
150
|
-
const result =
|
|
176
|
+
const result = (
|
|
177
|
+
await transpiler.transpile({
|
|
178
|
+
kind: "source",
|
|
179
|
+
source: `
|
|
151
180
|
u8 byte <- 255;
|
|
152
181
|
u16 word <- 65535;
|
|
153
182
|
u32 dword <- 0xFFFFFFFF;
|
|
@@ -156,7 +185,9 @@ describe("Transpiler", () => {
|
|
|
156
185
|
i32 sdword <- -1;
|
|
157
186
|
f32 floatVal <- 3.14;
|
|
158
187
|
f64 doubleVal <- 2.718;
|
|
159
|
-
|
|
188
|
+
`,
|
|
189
|
+
})
|
|
190
|
+
).files[0];
|
|
160
191
|
|
|
161
192
|
expect(result.success).toBe(true);
|
|
162
193
|
expect(result.code).toContain("uint8_t");
|
|
@@ -175,12 +206,17 @@ describe("Transpiler", () => {
|
|
|
175
206
|
mockFs,
|
|
176
207
|
);
|
|
177
208
|
|
|
178
|
-
const result =
|
|
209
|
+
const result = (
|
|
210
|
+
await transpiler.transpile({
|
|
211
|
+
kind: "source",
|
|
212
|
+
source: `
|
|
179
213
|
void test() {
|
|
180
214
|
u32 x;
|
|
181
215
|
x <- 42;
|
|
182
216
|
}
|
|
183
|
-
|
|
217
|
+
`,
|
|
218
|
+
})
|
|
219
|
+
).files[0];
|
|
184
220
|
|
|
185
221
|
expect(result.success).toBe(true);
|
|
186
222
|
expect(result.code).toContain("x = 42");
|
|
@@ -192,11 +228,16 @@ describe("Transpiler", () => {
|
|
|
192
228
|
mockFs,
|
|
193
229
|
);
|
|
194
230
|
|
|
195
|
-
const result =
|
|
231
|
+
const result = (
|
|
232
|
+
await transpiler.transpile({
|
|
233
|
+
kind: "source",
|
|
234
|
+
source: `
|
|
196
235
|
bool test(u32 a, u32 b) {
|
|
197
236
|
return a = b;
|
|
198
237
|
}
|
|
199
|
-
|
|
238
|
+
`,
|
|
239
|
+
})
|
|
240
|
+
).files[0];
|
|
200
241
|
|
|
201
242
|
expect(result.success).toBe(true);
|
|
202
243
|
expect(result.code).toContain("a == b");
|
|
@@ -208,12 +249,17 @@ describe("Transpiler", () => {
|
|
|
208
249
|
mockFs,
|
|
209
250
|
);
|
|
210
251
|
|
|
211
|
-
const result =
|
|
252
|
+
const result = (
|
|
253
|
+
await transpiler.transpile({
|
|
254
|
+
kind: "source",
|
|
255
|
+
source: `
|
|
212
256
|
struct Point {
|
|
213
257
|
i32 x;
|
|
214
258
|
i32 y;
|
|
215
259
|
}
|
|
216
|
-
|
|
260
|
+
`,
|
|
261
|
+
})
|
|
262
|
+
).files[0];
|
|
217
263
|
|
|
218
264
|
expect(result.success).toBe(true);
|
|
219
265
|
expect(result.code).toContain("typedef struct");
|
|
@@ -227,13 +273,18 @@ describe("Transpiler", () => {
|
|
|
227
273
|
mockFs,
|
|
228
274
|
);
|
|
229
275
|
|
|
230
|
-
const result =
|
|
276
|
+
const result = (
|
|
277
|
+
await transpiler.transpile({
|
|
278
|
+
kind: "source",
|
|
279
|
+
source: `
|
|
231
280
|
enum Color {
|
|
232
281
|
RED,
|
|
233
282
|
GREEN,
|
|
234
283
|
BLUE
|
|
235
284
|
}
|
|
236
|
-
|
|
285
|
+
`,
|
|
286
|
+
})
|
|
287
|
+
).files[0];
|
|
237
288
|
|
|
238
289
|
expect(result.success).toBe(true);
|
|
239
290
|
expect(result.code).toContain("typedef enum");
|
|
@@ -248,12 +299,17 @@ describe("Transpiler", () => {
|
|
|
248
299
|
mockFs,
|
|
249
300
|
);
|
|
250
301
|
|
|
251
|
-
const result =
|
|
302
|
+
const result = (
|
|
303
|
+
await transpiler.transpile({
|
|
304
|
+
kind: "source",
|
|
305
|
+
source: `
|
|
252
306
|
scope LED {
|
|
253
307
|
public void on() { }
|
|
254
308
|
public void off() { }
|
|
255
309
|
}
|
|
256
|
-
|
|
310
|
+
`,
|
|
311
|
+
})
|
|
312
|
+
).files[0];
|
|
257
313
|
|
|
258
314
|
expect(result.success).toBe(true);
|
|
259
315
|
expect(result.code).toContain("LED_on");
|
|
@@ -277,7 +333,7 @@ describe("Transpiler", () => {
|
|
|
277
333
|
mockFs,
|
|
278
334
|
);
|
|
279
335
|
|
|
280
|
-
const result = await transpiler.
|
|
336
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
281
337
|
|
|
282
338
|
expect(result.success).toBe(true);
|
|
283
339
|
expect(result.filesProcessed).toBe(1);
|
|
@@ -300,7 +356,7 @@ describe("Transpiler", () => {
|
|
|
300
356
|
mockFs,
|
|
301
357
|
);
|
|
302
358
|
|
|
303
|
-
await transpiler.
|
|
359
|
+
await transpiler.transpile({ kind: "files" });
|
|
304
360
|
|
|
305
361
|
const mkdirCalls = mockFs.getMkdirLog();
|
|
306
362
|
expect(mkdirCalls.some((c) => c.path === "/project/build")).toBe(true);
|
|
@@ -319,7 +375,7 @@ describe("Transpiler", () => {
|
|
|
319
375
|
mockFs,
|
|
320
376
|
);
|
|
321
377
|
|
|
322
|
-
await transpiler.
|
|
378
|
+
await transpiler.transpile({ kind: "files" });
|
|
323
379
|
|
|
324
380
|
const mkdirCalls = mockFs.getMkdirLog();
|
|
325
381
|
expect(mkdirCalls.some((c) => c.path === "/project/include")).toBe(
|
|
@@ -346,7 +402,7 @@ describe("Transpiler", () => {
|
|
|
346
402
|
mockFs,
|
|
347
403
|
);
|
|
348
404
|
|
|
349
|
-
const result = await transpiler.
|
|
405
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
350
406
|
|
|
351
407
|
expect(result.success).toBe(true);
|
|
352
408
|
|
|
@@ -365,7 +421,7 @@ describe("Transpiler", () => {
|
|
|
365
421
|
mockFs,
|
|
366
422
|
);
|
|
367
423
|
|
|
368
|
-
const result = await transpiler.
|
|
424
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
369
425
|
|
|
370
426
|
expect(result.success).toBe(false);
|
|
371
427
|
expect(result.errors[0].message).toContain("Input not found");
|
|
@@ -382,7 +438,7 @@ describe("Transpiler", () => {
|
|
|
382
438
|
mockFs,
|
|
383
439
|
);
|
|
384
440
|
|
|
385
|
-
const result = await transpiler.
|
|
441
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
386
442
|
|
|
387
443
|
expect(result.warnings).toContain("No C-Next source files found");
|
|
388
444
|
});
|
|
@@ -398,7 +454,7 @@ describe("Transpiler", () => {
|
|
|
398
454
|
mockFs,
|
|
399
455
|
);
|
|
400
456
|
|
|
401
|
-
const result = await transpiler.
|
|
457
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
402
458
|
|
|
403
459
|
expect(result.success).toBe(false);
|
|
404
460
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
@@ -429,7 +485,7 @@ describe("Transpiler", () => {
|
|
|
429
485
|
noCache: true,
|
|
430
486
|
});
|
|
431
487
|
|
|
432
|
-
const result = await transpiler.
|
|
488
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
433
489
|
|
|
434
490
|
expect(result.success).toBe(true);
|
|
435
491
|
expect(result.filesProcessed).toBe(1);
|
|
@@ -445,7 +501,7 @@ describe("Transpiler", () => {
|
|
|
445
501
|
noCache: true,
|
|
446
502
|
});
|
|
447
503
|
|
|
448
|
-
const result = await transpiler.
|
|
504
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
449
505
|
|
|
450
506
|
expect(result.success).toBe(false);
|
|
451
507
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
@@ -461,7 +517,7 @@ describe("Transpiler", () => {
|
|
|
461
517
|
noCache: true,
|
|
462
518
|
});
|
|
463
519
|
|
|
464
|
-
const result = await transpiler.
|
|
520
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
465
521
|
|
|
466
522
|
expect(result.success).toBe(false);
|
|
467
523
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
@@ -478,7 +534,7 @@ describe("Transpiler", () => {
|
|
|
478
534
|
noCache: true,
|
|
479
535
|
});
|
|
480
536
|
|
|
481
|
-
const result = await transpiler.
|
|
537
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
482
538
|
|
|
483
539
|
expect(result.success).toBe(false);
|
|
484
540
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
@@ -495,7 +551,7 @@ describe("Transpiler", () => {
|
|
|
495
551
|
noCache: true,
|
|
496
552
|
});
|
|
497
553
|
|
|
498
|
-
const result = await transpiler.
|
|
554
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
499
555
|
|
|
500
556
|
expect(result.success).toBe(true);
|
|
501
557
|
expect(result.outputFiles.length).toBeGreaterThan(0);
|
|
@@ -518,7 +574,7 @@ describe("Transpiler", () => {
|
|
|
518
574
|
noCache: true,
|
|
519
575
|
});
|
|
520
576
|
|
|
521
|
-
const result = await transpiler.
|
|
577
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
522
578
|
|
|
523
579
|
expect(result.success).toBe(true);
|
|
524
580
|
// Should have both .c and .h output
|
|
@@ -527,4 +583,50 @@ describe("Transpiler", () => {
|
|
|
527
583
|
});
|
|
528
584
|
});
|
|
529
585
|
});
|
|
586
|
+
|
|
587
|
+
describe("transpile() unified entry point", () => {
|
|
588
|
+
let mockFs: MockFileSystem;
|
|
589
|
+
|
|
590
|
+
beforeEach(() => {
|
|
591
|
+
mockFs = new MockFileSystem();
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it("transpile({ kind: 'source' }) returns ITranspilerResult with files[]", async () => {
|
|
595
|
+
const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
|
|
596
|
+
|
|
597
|
+
const result = await transpiler.transpile({
|
|
598
|
+
kind: "source",
|
|
599
|
+
source: "void main() { }",
|
|
600
|
+
});
|
|
601
|
+
expect(result.success).toBe(true);
|
|
602
|
+
expect(result.files).toHaveLength(1);
|
|
603
|
+
expect(result.files[0].code).toContain("int main");
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it("transpile({ kind: 'source' }) returns header in files[0].headerCode", async () => {
|
|
607
|
+
const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
|
|
608
|
+
|
|
609
|
+
const result = await transpiler.transpile({
|
|
610
|
+
kind: "source",
|
|
611
|
+
source: `
|
|
612
|
+
scope API {
|
|
613
|
+
public void doSomething() { }
|
|
614
|
+
}
|
|
615
|
+
`,
|
|
616
|
+
});
|
|
617
|
+
expect(result.success).toBe(true);
|
|
618
|
+
expect(result.files[0].headerCode).toContain("API_doSomething");
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("transpile({ kind: 'source' }) returns errors in result", async () => {
|
|
622
|
+
const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
|
|
623
|
+
|
|
624
|
+
const result = await transpiler.transpile({
|
|
625
|
+
kind: "source",
|
|
626
|
+
source: "@@@invalid",
|
|
627
|
+
});
|
|
628
|
+
expect(result.success).toBe(false);
|
|
629
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
630
|
+
});
|
|
631
|
+
});
|
|
530
632
|
});
|
|
@@ -422,7 +422,7 @@ describe("Transpiler.determineProjectRoot", () => {
|
|
|
422
422
|
});
|
|
423
423
|
|
|
424
424
|
// Run transpiler to trigger cache creation
|
|
425
|
-
await transpiler.
|
|
425
|
+
await transpiler.transpile({ kind: "files" });
|
|
426
426
|
|
|
427
427
|
// Cache directory should be in project root, not src dir
|
|
428
428
|
expect(existsSync(join(projectDir, ".cnx"))).toBe(true);
|
|
@@ -440,7 +440,7 @@ describe("Transpiler.determineProjectRoot", () => {
|
|
|
440
440
|
noCache: true,
|
|
441
441
|
});
|
|
442
442
|
|
|
443
|
-
await transpiler.
|
|
443
|
+
await transpiler.transpile({ kind: "files" });
|
|
444
444
|
|
|
445
445
|
// No cache directory should be created
|
|
446
446
|
expect(existsSync(join(projectDir, ".cnx"))).toBe(false);
|
|
@@ -37,7 +37,7 @@ interface IResolvedIncludes {
|
|
|
37
37
|
* Unified include resolution for the C-Next Pipeline
|
|
38
38
|
*
|
|
39
39
|
* This class encapsulates the complete include resolution workflow,
|
|
40
|
-
* used by
|
|
40
|
+
* used by the unified `transpile()` entry point for both file and source modes.
|
|
41
41
|
*
|
|
42
42
|
* Key responsibilities:
|
|
43
43
|
* - Extract #include directives from source content
|
|
@@ -161,6 +161,14 @@ class IncludeResolver {
|
|
|
161
161
|
|
|
162
162
|
if (file.type === EFileType.CNext) {
|
|
163
163
|
result.cnextIncludes.push(file);
|
|
164
|
+
// Issue #854: Track header directive for cnext includes so their types
|
|
165
|
+
// can be mapped by ExternalTypeHeaderBuilder, preventing duplicate
|
|
166
|
+
// forward declarations (MISRA Rule 5.6)
|
|
167
|
+
const headerPath = includeInfo.path.replace(/\.cnx$|\.cnext$/, ".h");
|
|
168
|
+
const directive = includeInfo.isLocal
|
|
169
|
+
? `#include "${headerPath}"`
|
|
170
|
+
: `#include <${headerPath}>`;
|
|
171
|
+
result.headerIncludeDirectives.set(absolutePath, directive);
|
|
164
172
|
}
|
|
165
173
|
}
|
|
166
174
|
|
|
@@ -351,8 +359,8 @@ class IncludeResolver {
|
|
|
351
359
|
/**
|
|
352
360
|
* Build search paths from a source file location
|
|
353
361
|
*
|
|
354
|
-
* Consolidates the search path building logic used by
|
|
355
|
-
*
|
|
362
|
+
* Consolidates the search path building logic used by the unified
|
|
363
|
+
* transpile() entry point.
|
|
356
364
|
*
|
|
357
365
|
* Search order (highest to lowest priority):
|
|
358
366
|
* 1. Source file's directory (for relative includes)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for IncludeResolver
|
|
3
3
|
*
|
|
4
|
-
* Tests the unified include resolution logic used by
|
|
5
|
-
*
|
|
4
|
+
* Tests the unified include resolution logic used by
|
|
5
|
+
* the transpile() entry point for both file and source modes.
|
|
6
6
|
*/
|
|
7
7
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
8
8
|
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|