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
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for Issue #985 external-symbol recovery.
|
|
3
|
+
*
|
|
4
|
+
* Drives the real Transpiler over a fixture that forces the recovery path:
|
|
5
|
+
* widget.h refuses standalone inclusion (its #error fires unless guard.h ran
|
|
6
|
+
* first), its API is emitted by a guard.h macro and decorated with a trailing
|
|
7
|
+
* attribute macro, and it declares a pointer-parameter function plus an opaque
|
|
8
|
+
* handle. This exercises Transpiler._collectExternalDeclarations and its
|
|
9
|
+
* helpers, ExternalDeclarationOracle, and the downstream codegen that adds `&`
|
|
10
|
+
* for a struct passed to a pointer param and `*` for an opaque handle.
|
|
11
|
+
*
|
|
12
|
+
* Requires a C preprocessor toolchain; skips gracefully when none is available.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
15
|
+
import { mkdtempSync, writeFileSync, rmSync, readFileSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import Transpiler from "../Transpiler";
|
|
19
|
+
import CodeGenState from "../state/CodeGenState";
|
|
20
|
+
import Preprocessor from "../logic/preprocessor/Preprocessor";
|
|
21
|
+
|
|
22
|
+
const GUARD_H = `#define WIDGET_GUARD 1
|
|
23
|
+
#define WIDGET_FEATURE 1
|
|
24
|
+
#define USE_PTHREAD 0
|
|
25
|
+
#define PRIVILEGED
|
|
26
|
+
#define WIDGET_SCALE(x) ((x) + 1)
|
|
27
|
+
#define DECLARE_WIDGET_API \\
|
|
28
|
+
typedef struct _widget_t widget_t; \\
|
|
29
|
+
typedef struct { int mode; } widget_cfg_t; \\
|
|
30
|
+
extern const widget_cfg_t widget_default_cfg; \\
|
|
31
|
+
int widget_install(const widget_cfg_t *config) PRIVILEGED; \\
|
|
32
|
+
void widget_apply(const widget_cfg_t *cfg) PRIVILEGED; \\
|
|
33
|
+
widget_t *widget_create(void) PRIVILEGED;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
// A header cnext DISCOVERS (it walks #includes unconditionally) but that cannot
|
|
37
|
+
// preprocess standalone — its <no-such-header> mirrors lvgl's OSAL headers
|
|
38
|
+
// probing <semaphore.h>. Its standalone failure sets anyHeaderPreprocessFailed,
|
|
39
|
+
// which is what triggers the external-declaration recovery pass. It is guarded
|
|
40
|
+
// out of the recovery TU (USE_PTHREAD is 0), so the union still preprocesses.
|
|
41
|
+
const PTHREAD_IMPL_H = `#include <cnext_no_such_header_zzz.h>\n`;
|
|
42
|
+
|
|
43
|
+
const WIDGET_H = `#ifndef WIDGET_GUARD
|
|
44
|
+
#error "include guard.h before widget.h"
|
|
45
|
+
#endif
|
|
46
|
+
#if USE_PTHREAD
|
|
47
|
+
#include "pthread_impl.h"
|
|
48
|
+
#endif
|
|
49
|
+
#if WIDGET_FEATURE
|
|
50
|
+
DECLARE_WIDGET_API
|
|
51
|
+
#endif
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const MAIN_CNX = `#include "guard.h"
|
|
55
|
+
#include "widget.h"
|
|
56
|
+
|
|
57
|
+
scope Demo {
|
|
58
|
+
widget_t handle;
|
|
59
|
+
|
|
60
|
+
void build() {
|
|
61
|
+
widget_cfg_t cfg <- {mode: 1};
|
|
62
|
+
i32 err <- global.widget_install(cfg);
|
|
63
|
+
global.widget_apply(widget_default_cfg);
|
|
64
|
+
i32 scaled <- WIDGET_SCALE(4);
|
|
65
|
+
handle <- global.widget_create();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
describe("external-symbol recovery (integration)", () => {
|
|
71
|
+
let dir: string;
|
|
72
|
+
const available = new Preprocessor().isAvailable();
|
|
73
|
+
|
|
74
|
+
beforeAll(() => {
|
|
75
|
+
dir = mkdtempSync(join(tmpdir(), "cnext-recovery-"));
|
|
76
|
+
writeFileSync(join(dir, "guard.h"), GUARD_H);
|
|
77
|
+
writeFileSync(join(dir, "widget.h"), WIDGET_H);
|
|
78
|
+
writeFileSync(join(dir, "pthread_impl.h"), PTHREAD_IMPL_H);
|
|
79
|
+
writeFileSync(join(dir, "main.cnx"), MAIN_CNX);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
afterAll(() => {
|
|
83
|
+
if (dir) rmSync(dir, { recursive: true, force: true });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("recovers full signatures so codegen adds & and pointer handles", async () => {
|
|
87
|
+
if (!available) return; // no toolchain in this env
|
|
88
|
+
|
|
89
|
+
const transpiler = new Transpiler({
|
|
90
|
+
input: join(dir, "main.cnx"),
|
|
91
|
+
includeDirs: [dir],
|
|
92
|
+
outDir: join(dir, "out"),
|
|
93
|
+
cppRequired: true,
|
|
94
|
+
noCache: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
98
|
+
|
|
99
|
+
const unresolved = result.errors.find(
|
|
100
|
+
(e) =>
|
|
101
|
+
e.message.includes("widget_install") ||
|
|
102
|
+
e.message.includes("widget_create") ||
|
|
103
|
+
// Function-like macro recovered by name (no declaration to parse).
|
|
104
|
+
e.message.includes("WIDGET_SCALE"),
|
|
105
|
+
);
|
|
106
|
+
expect(unresolved).toBeUndefined();
|
|
107
|
+
expect(result.success).toBe(true);
|
|
108
|
+
|
|
109
|
+
const generated = result.outputFiles.find((f) => f.endsWith("main.cpp"));
|
|
110
|
+
expect(generated).toBeDefined();
|
|
111
|
+
const code = readFileSync(generated!, "utf8");
|
|
112
|
+
|
|
113
|
+
// Local struct arg passed to a pointer param -> address-of.
|
|
114
|
+
expect(code).toMatch(/widget_install\(\s*&/);
|
|
115
|
+
// Extern C global (struct value) passed to a pointer param -> address-of,
|
|
116
|
+
// resolved via the C symbol table fallback (#985).
|
|
117
|
+
expect(code).toMatch(/widget_apply\(\s*&widget_default_cfg/);
|
|
118
|
+
// Opaque handle -> pointer field.
|
|
119
|
+
expect(code).toMatch(/widget_t\s*\*\s*\w*handle/);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("folds a recovered struct into externalStructFields so it stays subject to init-completeness checking", async () => {
|
|
123
|
+
if (!available) return;
|
|
124
|
+
|
|
125
|
+
// widget_cfg_t only becomes known through #985 recovery (its typedef is
|
|
126
|
+
// emitted by the DECLARE_WIDGET_API macro inside the un-standalone header).
|
|
127
|
+
// The external-struct snapshot InitializationAnalyzer consults must include
|
|
128
|
+
// it — otherwise recovered structs escape init checking that cleanly
|
|
129
|
+
// preprocessed structs get. Regression: the snapshot must be taken AFTER the
|
|
130
|
+
// recovery pass, not before it.
|
|
131
|
+
const transpiler = new Transpiler({
|
|
132
|
+
input: join(dir, "main.cnx"),
|
|
133
|
+
includeDirs: [dir],
|
|
134
|
+
outDir: join(dir, "out"),
|
|
135
|
+
cppRequired: true,
|
|
136
|
+
noCache: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
140
|
+
expect(result.success).toBe(true);
|
|
141
|
+
|
|
142
|
+
const external = CodeGenState.getExternalStructFields();
|
|
143
|
+
expect(external.has("widget_cfg_t")).toBe(true);
|
|
144
|
+
expect([...(external.get("widget_cfg_t") ?? [])]).toContain("mode");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("re-applies recovery on a warm header cache (Issue #985 regression)", async () => {
|
|
148
|
+
if (!available) return;
|
|
149
|
+
|
|
150
|
+
// Fresh project dir with a project marker so on-disk caching activates
|
|
151
|
+
// (determineProjectRoot needs a marker like cnext.config.json). The first
|
|
152
|
+
// run populates .cnx/ with the DEGRADED header symbols (cached before the
|
|
153
|
+
// recovery pass runs); the second run restores them from cache. The
|
|
154
|
+
// recovery gate must survive caching so warm builds re-apply the fix.
|
|
155
|
+
const cacheDir = mkdtempSync(join(tmpdir(), "cnext-recovery-cache-"));
|
|
156
|
+
try {
|
|
157
|
+
writeFileSync(join(cacheDir, "cnext.config.json"), "{}\n");
|
|
158
|
+
writeFileSync(join(cacheDir, "guard.h"), GUARD_H);
|
|
159
|
+
writeFileSync(join(cacheDir, "widget.h"), WIDGET_H);
|
|
160
|
+
writeFileSync(join(cacheDir, "pthread_impl.h"), PTHREAD_IMPL_H);
|
|
161
|
+
writeFileSync(join(cacheDir, "main.cnx"), MAIN_CNX);
|
|
162
|
+
|
|
163
|
+
// Caching ON (noCache defaults to false).
|
|
164
|
+
const run = async () =>
|
|
165
|
+
new Transpiler({
|
|
166
|
+
input: join(cacheDir, "main.cnx"),
|
|
167
|
+
includeDirs: [cacheDir],
|
|
168
|
+
outDir: join(cacheDir, "out"),
|
|
169
|
+
cppRequired: true,
|
|
170
|
+
}).transpile({ kind: "files" });
|
|
171
|
+
|
|
172
|
+
// Cold cache: populates .cnx/ with the degraded header symbols.
|
|
173
|
+
const cold = await run();
|
|
174
|
+
expect(cold.success).toBe(true);
|
|
175
|
+
|
|
176
|
+
// Warm cache: headers unchanged and restored from cache. The recovery
|
|
177
|
+
// pass must still fire and produce the same corrected codegen — before
|
|
178
|
+
// the fix, the warm build regenerated the by-value / non-compiling output.
|
|
179
|
+
const warm = await run();
|
|
180
|
+
expect(warm.success).toBe(true);
|
|
181
|
+
|
|
182
|
+
const generated = warm.outputFiles.find((f) => f.endsWith("main.cpp"));
|
|
183
|
+
expect(generated).toBeDefined();
|
|
184
|
+
const code = readFileSync(generated!, "utf8");
|
|
185
|
+
expect(code).toMatch(/widget_install\(\s*&/);
|
|
186
|
+
expect(code).toMatch(/widget_apply\(\s*&widget_default_cfg/);
|
|
187
|
+
expect(code).toMatch(/widget_t\s*\*\s*\w*handle/);
|
|
188
|
+
} finally {
|
|
189
|
+
rmSync(cacheDir, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("does nothing when no header failed to preprocess (clean project)", async () => {
|
|
194
|
+
if (!available) return;
|
|
195
|
+
|
|
196
|
+
// A .cnx with no C includes never trips anyHeaderPreprocessFailed, so the
|
|
197
|
+
// recovery pass is a no-op and transpilation still succeeds.
|
|
198
|
+
const cleanDir = mkdtempSync(join(tmpdir(), "cnext-clean-"));
|
|
199
|
+
try {
|
|
200
|
+
writeFileSync(
|
|
201
|
+
join(cleanDir, "main.cnx"),
|
|
202
|
+
`scope Demo {\n void run() {\n i32 x <- 1;\n }\n}\n`,
|
|
203
|
+
);
|
|
204
|
+
const transpiler = new Transpiler({
|
|
205
|
+
input: join(cleanDir, "main.cnx"),
|
|
206
|
+
outDir: join(cleanDir, "out"),
|
|
207
|
+
noCache: true,
|
|
208
|
+
});
|
|
209
|
+
const result = await transpiler.transpile({ kind: "files" });
|
|
210
|
+
expect(result.success).toBe(true);
|
|
211
|
+
} finally {
|
|
212
|
+
rmSync(cleanDir, { recursive: true, force: true });
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for detectAssemblySyntax
|
|
3
|
+
*
|
|
4
|
+
* Some framework headers (e.g. xtensa `coreasm.h`, pulled in transitively by
|
|
5
|
+
* FreeRTOS port headers) are GNU-assembler sources, not C: they `#define
|
|
6
|
+
* _ASMLANGUAGE` and contain `.macro` bodies whose instruction mnemonics (e.g.
|
|
7
|
+
* `loop`) were mis-collected as C symbols, causing false "defined in multiple
|
|
8
|
+
* languages" conflicts against C-Next symbols like Arduino `loop()`.
|
|
9
|
+
* detectAssemblySyntax lets the header pipeline skip such files.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from "vitest";
|
|
12
|
+
import detectAssemblySyntax from "../detectAssemblySyntax";
|
|
13
|
+
|
|
14
|
+
describe("detectAssemblySyntax", () => {
|
|
15
|
+
describe("assembler sources - should return true", () => {
|
|
16
|
+
it("detects a GAS .macro block (xtensa coreasm.h shape)", () => {
|
|
17
|
+
const coreasm = [
|
|
18
|
+
"#define _ASMLANGUAGE",
|
|
19
|
+
"\t.macro\tfloop_\tar, startlabel, endlabelref",
|
|
20
|
+
"\tloop\t\\ar, \\endlabelref",
|
|
21
|
+
"\t.endm",
|
|
22
|
+
].join("\n");
|
|
23
|
+
expect(detectAssemblySyntax(coreasm)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("detects the _ASMLANGUAGE marker on its own", () => {
|
|
27
|
+
expect(detectAssemblySyntax("#define _ASMLANGUAGE 1\n")).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("detects section/global directives at line start", () => {
|
|
31
|
+
expect(detectAssemblySyntax(" .section .text\n .global _start\n")).toBe(
|
|
32
|
+
true,
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("detects .macro regardless of leading whitespace", () => {
|
|
37
|
+
expect(detectAssemblySyntax(" .macro find_ms_setbit ad, as\n")).toBe(
|
|
38
|
+
true,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("detects a spaced '# define _ASMLANGUAGE' directive", () => {
|
|
43
|
+
expect(detectAssemblySyntax("# define _ASMLANGUAGE\n")).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("detects portable assembly markers defined whole-file", () => {
|
|
47
|
+
expect(detectAssemblySyntax("#define __ASSEMBLY__ 1\n")).toBe(true);
|
|
48
|
+
expect(detectAssemblySyntax("#define __ASSEMBLER__\n")).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("C / C++ headers - should return false", () => {
|
|
53
|
+
it("returns false for a C function named loop", () => {
|
|
54
|
+
expect(
|
|
55
|
+
detectAssemblySyntax("void loop(void);\nint add(int a, int b);"),
|
|
56
|
+
).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns false for structs, enums, typedefs", () => {
|
|
60
|
+
expect(
|
|
61
|
+
detectAssemblySyntax(
|
|
62
|
+
"typedef struct { int x; } Point;\nenum Color { RED, GREEN };",
|
|
63
|
+
),
|
|
64
|
+
).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("does not false-positive on member access or identifiers containing 'macro'", () => {
|
|
68
|
+
expect(
|
|
69
|
+
detectAssemblySyntax("int y = p.x;\nint n = cfg.macro_count;"),
|
|
70
|
+
).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns false for a C++ class header", () => {
|
|
74
|
+
expect(
|
|
75
|
+
detectAssemblySyntax("class Foo : public Bar { public: int x; };"),
|
|
76
|
+
).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("returns false for a C header that guards prototypes with #ifndef _ASMLANGUAGE", () => {
|
|
80
|
+
// xtensa xtruntime.h shape: raw-fallback content still holds the guard
|
|
81
|
+
// line. The C prototypes inside must NOT be dropped as assembler.
|
|
82
|
+
const xtruntime = [
|
|
83
|
+
"#ifndef _ASMLANGUAGE",
|
|
84
|
+
"extern void xthal_window_spill(void);",
|
|
85
|
+
"extern unsigned xthal_get_ccount(void);",
|
|
86
|
+
"#endif",
|
|
87
|
+
].join("\n");
|
|
88
|
+
expect(detectAssemblySyntax(xtruntime)).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns false for a C header guarded by #ifndef __ASSEMBLY__ / #ifdef __ASSEMBLER__", () => {
|
|
92
|
+
// Linux-kernel-style guards fence the C prototypes; on the raw-fallback
|
|
93
|
+
// path the guard lines survive but the file is still a normal C header.
|
|
94
|
+
const guarded = [
|
|
95
|
+
"#ifndef __ASSEMBLY__",
|
|
96
|
+
"extern int kernel_init(void);",
|
|
97
|
+
"#endif",
|
|
98
|
+
"#ifdef __ASSEMBLER__",
|
|
99
|
+
"/* asm-only region */",
|
|
100
|
+
"#endif",
|
|
101
|
+
].join("\n");
|
|
102
|
+
expect(detectAssemblySyntax(guarded)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("does not match a longer macro name that merely starts with _ASMLANGUAGE", () => {
|
|
106
|
+
// Exact-token match: `#define _ASMLANGUAGE_CONFIG 1` is not the marker.
|
|
107
|
+
expect(detectAssemblySyntax("#define _ASMLANGUAGE_CONFIG 1\n")).toBe(
|
|
108
|
+
false,
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("returns false for empty content", () => {
|
|
113
|
+
expect(detectAssemblySyntax("")).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -1080,6 +1080,14 @@ class FunctionCallAnalyzer {
|
|
|
1080
1080
|
}
|
|
1081
1081
|
}
|
|
1082
1082
|
|
|
1083
|
+
// Issue #985 recovery: a function-like MACRO recovered by translation-unit
|
|
1084
|
+
// preprocessing (e.g. FreeRTOS pdMS_TO_TICKS). Recovered functions are
|
|
1085
|
+
// registered as full symbols (found above via getOverloads); macros have no
|
|
1086
|
+
// declaration to parse, so only their name is known.
|
|
1087
|
+
if (this.symbolTable.hasExternalDeclaration(name)) {
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1083
1091
|
return false;
|
|
1084
1092
|
}
|
|
1085
1093
|
}
|