c-next 0.2.16 → 0.2.17
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 +16 -0
- package/dist/index.js +1347 -414
- package/dist/index.js.map +3 -3
- package/grammar/CNext.g4 +4 -0
- package/package.json +5 -1
- package/src/transpiler/Transpiler.ts +90 -22
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- 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__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
- package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -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/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -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 +48 -43
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- 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/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
- 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__/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 +268 -1
- 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/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 +75 -0
- 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 +71 -6
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -21,6 +21,7 @@ import ISignedShiftError from "./types/ISignedShiftError";
|
|
|
21
21
|
import ParserUtils from "../../../utils/ParserUtils";
|
|
22
22
|
import TypeConstants from "../../../utils/constants/TypeConstants";
|
|
23
23
|
import ExpressionUtils from "../../../utils/ExpressionUtils";
|
|
24
|
+
import CodeGenState from "../../state/CodeGenState";
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* First pass: Collect variable declarations with their types
|
|
@@ -28,25 +29,36 @@ import ExpressionUtils from "../../../utils/ExpressionUtils";
|
|
|
28
29
|
class SignedVariableCollector extends CNextListener {
|
|
29
30
|
private readonly signedVars: Set<string> = new Set();
|
|
30
31
|
|
|
32
|
+
// Track all variable types (for resolving struct member chains)
|
|
33
|
+
private readonly varTypes: Map<string, string> = new Map();
|
|
34
|
+
|
|
31
35
|
public getSignedVars(): Set<string> {
|
|
32
36
|
return this.signedVars;
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
public getVarTypes(): Map<string, string> {
|
|
40
|
+
return this.varTypes;
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
/**
|
|
36
|
-
* Track a typed identifier
|
|
44
|
+
* Track a typed identifier - add to signedVars if signed, always track type
|
|
37
45
|
*/
|
|
38
|
-
private
|
|
46
|
+
private trackType(
|
|
39
47
|
typeCtx: Parser.TypeContext | null,
|
|
40
48
|
identifier: { getText(): string } | null,
|
|
41
49
|
): void {
|
|
42
|
-
if (!typeCtx) return;
|
|
50
|
+
if (!typeCtx || !identifier) return;
|
|
43
51
|
|
|
44
52
|
const typeName = typeCtx.getText();
|
|
45
|
-
|
|
53
|
+
const varName = identifier.getText();
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
// Always track the variable's type for member chain resolution
|
|
56
|
+
this.varTypes.set(varName, typeName);
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
// Also track in signedVars if it's a signed type
|
|
59
|
+
if (TypeConstants.SIGNED_TYPES.includes(typeName)) {
|
|
60
|
+
this.signedVars.add(varName);
|
|
61
|
+
}
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
/**
|
|
@@ -55,21 +67,21 @@ class SignedVariableCollector extends CNextListener {
|
|
|
55
67
|
override enterVariableDeclaration = (
|
|
56
68
|
ctx: Parser.VariableDeclarationContext,
|
|
57
69
|
): void => {
|
|
58
|
-
this.
|
|
70
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
59
71
|
};
|
|
60
72
|
|
|
61
73
|
/**
|
|
62
74
|
* Track function parameters with signed types
|
|
63
75
|
*/
|
|
64
76
|
override enterParameter = (ctx: Parser.ParameterContext): void => {
|
|
65
|
-
this.
|
|
77
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
66
78
|
};
|
|
67
79
|
|
|
68
80
|
/**
|
|
69
81
|
* Track for-loop variable declarations with signed types
|
|
70
82
|
*/
|
|
71
83
|
override enterForVarDecl = (ctx: Parser.ForVarDeclContext): void => {
|
|
72
|
-
this.
|
|
84
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
73
85
|
};
|
|
74
86
|
}
|
|
75
87
|
|
|
@@ -82,10 +94,18 @@ class SignedShiftListener extends CNextListener {
|
|
|
82
94
|
// eslint-disable-next-line @typescript-eslint/lines-between-class-members
|
|
83
95
|
private readonly signedVars: Set<string>;
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/lines-between-class-members
|
|
98
|
+
private readonly varTypes: Map<string, string>;
|
|
99
|
+
|
|
100
|
+
constructor(
|
|
101
|
+
analyzer: SignedShiftAnalyzer,
|
|
102
|
+
signedVars: Set<string>,
|
|
103
|
+
varTypes: Map<string, string>,
|
|
104
|
+
) {
|
|
86
105
|
super();
|
|
87
106
|
this.analyzer = analyzer;
|
|
88
107
|
this.signedVars = signedVars;
|
|
108
|
+
this.varTypes = varTypes;
|
|
89
109
|
}
|
|
90
110
|
|
|
91
111
|
/**
|
|
@@ -116,6 +136,97 @@ class SignedShiftListener extends CNextListener {
|
|
|
116
136
|
}
|
|
117
137
|
};
|
|
118
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Check compound shift-assign statements for signed targets
|
|
141
|
+
* assignmentStatement: assignmentTarget assignmentOperator expression ';'
|
|
142
|
+
* Issue #1008: <<<- and >><- must also be rejected on signed types
|
|
143
|
+
*
|
|
144
|
+
* Handles both simple identifiers (x <<<- 2) and member chains (s.x <<<- 2)
|
|
145
|
+
*/
|
|
146
|
+
override enterAssignmentStatement = (
|
|
147
|
+
ctx: Parser.AssignmentStatementContext,
|
|
148
|
+
): void => {
|
|
149
|
+
const opCtx = ctx.assignmentOperator();
|
|
150
|
+
if (!opCtx) return;
|
|
151
|
+
|
|
152
|
+
const isLeftShiftAssign = opCtx.LSHIFT_ASSIGN() !== null;
|
|
153
|
+
const isRightShiftAssign = opCtx.RSHIFT_ASSIGN() !== null;
|
|
154
|
+
if (!isLeftShiftAssign && !isRightShiftAssign) return;
|
|
155
|
+
|
|
156
|
+
const target = ctx.assignmentTarget();
|
|
157
|
+
if (!target) return;
|
|
158
|
+
|
|
159
|
+
// Get the base identifier from the assignment target
|
|
160
|
+
const identifier = target.IDENTIFIER();
|
|
161
|
+
if (!identifier) return;
|
|
162
|
+
|
|
163
|
+
const baseName = identifier.getText();
|
|
164
|
+
const postfixOps = target.postfixTargetOp();
|
|
165
|
+
|
|
166
|
+
// Check if the final target type is signed
|
|
167
|
+
if (this.isSignedTarget(baseName, postfixOps)) {
|
|
168
|
+
const operator = isLeftShiftAssign ? "<<<-" : ">><-";
|
|
169
|
+
const { line, column } = ParserUtils.getPosition(target);
|
|
170
|
+
this.analyzer.addError(line, column, operator);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolve the final type of an assignment target, handling member chains.
|
|
176
|
+
* Returns true if the final target is a signed type.
|
|
177
|
+
*
|
|
178
|
+
* Examples:
|
|
179
|
+
* - "x" with no postfix ops → check if x is signed
|
|
180
|
+
* - "s" with postfixOps [".x"] → check if s.x field is signed
|
|
181
|
+
* - "arr" with postfixOps ["[0]", ".field"] → check if field is signed
|
|
182
|
+
*/
|
|
183
|
+
private isSignedTarget(
|
|
184
|
+
baseName: string,
|
|
185
|
+
postfixOps: Parser.PostfixTargetOpContext[],
|
|
186
|
+
): boolean {
|
|
187
|
+
// Simple case: no member access, just a variable
|
|
188
|
+
if (postfixOps.length === 0) {
|
|
189
|
+
return this.signedVars.has(baseName);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Member chain case: resolve through the chain
|
|
193
|
+
let currentType = this.varTypes.get(baseName);
|
|
194
|
+
if (!currentType) {
|
|
195
|
+
// Unknown base type - can't resolve, skip
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Walk through the postfix operations
|
|
200
|
+
for (const op of postfixOps) {
|
|
201
|
+
const memberIdent = op.IDENTIFIER();
|
|
202
|
+
if (memberIdent) {
|
|
203
|
+
// Member access: .fieldName
|
|
204
|
+
const fieldName = memberIdent.getText();
|
|
205
|
+
const fieldType = CodeGenState.getStructFieldType(
|
|
206
|
+
currentType,
|
|
207
|
+
fieldName,
|
|
208
|
+
);
|
|
209
|
+
if (!fieldType) {
|
|
210
|
+
// Unknown field - can't resolve, skip
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
currentType = fieldType;
|
|
214
|
+
} else {
|
|
215
|
+
// Array subscript: [expr] - doesn't change the base type for primitives
|
|
216
|
+
// For arrays like u8[4], after [i] we still have u8
|
|
217
|
+
// Strip array dimensions if present
|
|
218
|
+
const bracketIndex = currentType.indexOf("[");
|
|
219
|
+
if (bracketIndex !== -1) {
|
|
220
|
+
currentType = currentType.substring(0, bracketIndex);
|
|
221
|
+
}
|
|
222
|
+
// Otherwise keep the type as-is (e.g., bit indexing on u8)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if the final resolved type is signed
|
|
227
|
+
return TypeConstants.SIGNED_TYPES.includes(currentType);
|
|
228
|
+
}
|
|
229
|
+
|
|
119
230
|
/**
|
|
120
231
|
* Check if an additive expression contains a signed type operand
|
|
121
232
|
*/
|
|
@@ -202,13 +313,14 @@ class SignedShiftAnalyzer {
|
|
|
202
313
|
public analyze(tree: Parser.ProgramContext): ISignedShiftError[] {
|
|
203
314
|
this.errors = [];
|
|
204
315
|
|
|
205
|
-
// First pass: collect signed variables
|
|
316
|
+
// First pass: collect signed variables and all variable types
|
|
206
317
|
const collector = new SignedVariableCollector();
|
|
207
318
|
ParseTreeWalker.DEFAULT.walk(collector, tree);
|
|
208
319
|
const signedVars = collector.getSignedVars();
|
|
320
|
+
const varTypes = collector.getVarTypes();
|
|
209
321
|
|
|
210
322
|
// Second pass: detect shift with signed operands
|
|
211
|
-
const listener = new SignedShiftListener(this, signedVars);
|
|
323
|
+
const listener = new SignedShiftListener(this, signedVars, varTypes);
|
|
212
324
|
ParseTreeWalker.DEFAULT.walk(listener, tree);
|
|
213
325
|
|
|
214
326
|
return this.errors;
|
|
@@ -1117,4 +1117,204 @@ describe("FunctionCallAnalyzer", () => {
|
|
|
1117
1117
|
);
|
|
1118
1118
|
});
|
|
1119
1119
|
});
|
|
1120
|
+
|
|
1121
|
+
// ========================================================================
|
|
1122
|
+
// Issue #985: global. prefix function call validation
|
|
1123
|
+
// ========================================================================
|
|
1124
|
+
|
|
1125
|
+
describe("global prefix function calls (Issue #985)", () => {
|
|
1126
|
+
it("should detect undeclared global.func() call", () => {
|
|
1127
|
+
const code = `
|
|
1128
|
+
scope Test {
|
|
1129
|
+
void run() {
|
|
1130
|
+
u32 now <- global.millis();
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
`;
|
|
1134
|
+
const tree = parse(code);
|
|
1135
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1136
|
+
const errors = analyzer.analyze(tree);
|
|
1137
|
+
|
|
1138
|
+
expect(errors).toHaveLength(1);
|
|
1139
|
+
expect(errors[0].code).toBe("E0422");
|
|
1140
|
+
expect(errors[0].message).toContain("'millis'");
|
|
1141
|
+
expect(errors[0].message).toContain(
|
|
1142
|
+
"not declared in any included header",
|
|
1143
|
+
);
|
|
1144
|
+
expect(errors[0].message).toContain("#include <Arduino.h>");
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
it("should detect undeclared global.func() with unknown function", () => {
|
|
1148
|
+
const code = `
|
|
1149
|
+
scope Test {
|
|
1150
|
+
void run() {
|
|
1151
|
+
u32 result <- global.unknownFunc();
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
`;
|
|
1155
|
+
const tree = parse(code);
|
|
1156
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1157
|
+
const errors = analyzer.analyze(tree);
|
|
1158
|
+
|
|
1159
|
+
expect(errors).toHaveLength(1);
|
|
1160
|
+
expect(errors[0].code).toBe("E0422");
|
|
1161
|
+
expect(errors[0].message).toContain("'unknownFunc'");
|
|
1162
|
+
expect(errors[0].message).toContain(
|
|
1163
|
+
"not declared in any included header",
|
|
1164
|
+
);
|
|
1165
|
+
expect(errors[0].message).not.toContain("#include");
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
it("should allow global.func() when function is defined", () => {
|
|
1169
|
+
const code = `
|
|
1170
|
+
void helper() {
|
|
1171
|
+
u32 x <- 5;
|
|
1172
|
+
}
|
|
1173
|
+
scope Test {
|
|
1174
|
+
void run() {
|
|
1175
|
+
global.helper();
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
`;
|
|
1179
|
+
const tree = parse(code);
|
|
1180
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1181
|
+
const errors = analyzer.analyze(tree);
|
|
1182
|
+
|
|
1183
|
+
expect(errors).toHaveLength(0);
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
it("should allow global.func() when header is included", () => {
|
|
1187
|
+
const code = `
|
|
1188
|
+
#include <Arduino.h>
|
|
1189
|
+
scope Test {
|
|
1190
|
+
void run() {
|
|
1191
|
+
u32 now <- global.millis();
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
`;
|
|
1195
|
+
const tree = parse(code);
|
|
1196
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1197
|
+
const errors = analyzer.analyze(tree);
|
|
1198
|
+
|
|
1199
|
+
expect(errors).toHaveLength(0);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
it("should allow global.Scope.method() when scope is defined", () => {
|
|
1203
|
+
const code = `
|
|
1204
|
+
scope Motor {
|
|
1205
|
+
public void start() {
|
|
1206
|
+
u32 x <- 5;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
scope Controller {
|
|
1210
|
+
void run() {
|
|
1211
|
+
global.Motor.start();
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
`;
|
|
1215
|
+
const tree = parse(code);
|
|
1216
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1217
|
+
const errors = analyzer.analyze(tree);
|
|
1218
|
+
|
|
1219
|
+
expect(errors).toHaveLength(0);
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
it("should detect undeclared global.Scope.method() call", () => {
|
|
1223
|
+
const code = `
|
|
1224
|
+
scope Motor {
|
|
1225
|
+
public void start() {
|
|
1226
|
+
u32 x <- 5;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
scope Controller {
|
|
1230
|
+
void run() {
|
|
1231
|
+
global.Motor.undefinedMethod();
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
`;
|
|
1235
|
+
const tree = parse(code);
|
|
1236
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1237
|
+
const errors = analyzer.analyze(tree);
|
|
1238
|
+
|
|
1239
|
+
expect(errors).toHaveLength(1);
|
|
1240
|
+
expect(errors[0].code).toBe("E0422");
|
|
1241
|
+
expect(errors[0].functionName).toBe("Motor_undefinedMethod");
|
|
1242
|
+
expect(errors[0].message).toContain("called before definition");
|
|
1243
|
+
expect(errors[0].message).not.toContain(
|
|
1244
|
+
"not declared in any included header",
|
|
1245
|
+
);
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
it("should NOT resolve global.helper() to scope method Test_helper", () => {
|
|
1249
|
+
const code = `
|
|
1250
|
+
scope Test {
|
|
1251
|
+
void helper() {
|
|
1252
|
+
}
|
|
1253
|
+
void run() {
|
|
1254
|
+
global.helper();
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
`;
|
|
1258
|
+
const tree = parse(code);
|
|
1259
|
+
const symbolTable = new SymbolTable();
|
|
1260
|
+
|
|
1261
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1262
|
+
const errors = analyzer.analyze(tree, symbolTable);
|
|
1263
|
+
|
|
1264
|
+
expect(errors).toHaveLength(1);
|
|
1265
|
+
expect(errors[0].code).toBe("E0422");
|
|
1266
|
+
expect(errors[0].functionName).toBe("helper");
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
it("should say 'called before definition' for global.func() on local function", () => {
|
|
1270
|
+
const code = `
|
|
1271
|
+
scope Test {
|
|
1272
|
+
void run() {
|
|
1273
|
+
global.helper();
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
void helper() {
|
|
1277
|
+
}
|
|
1278
|
+
`;
|
|
1279
|
+
const tree = parse(code);
|
|
1280
|
+
const symbolTable = new SymbolTable();
|
|
1281
|
+
|
|
1282
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1283
|
+
const errors = analyzer.analyze(tree, symbolTable);
|
|
1284
|
+
|
|
1285
|
+
expect(errors).toHaveLength(1);
|
|
1286
|
+
expect(errors[0].code).toBe("E0422");
|
|
1287
|
+
expect(errors[0].message).toContain("called before definition");
|
|
1288
|
+
expect(errors[0].message).not.toContain(
|
|
1289
|
+
"not declared in any included header",
|
|
1290
|
+
);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
it("should allow global.func() for external C function", () => {
|
|
1294
|
+
const code = `
|
|
1295
|
+
scope Test {
|
|
1296
|
+
void run() {
|
|
1297
|
+
global.externalFunc();
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
`;
|
|
1301
|
+
const tree = parse(code);
|
|
1302
|
+
const symbolTable = new SymbolTable();
|
|
1303
|
+
symbolTable.addCSymbol({
|
|
1304
|
+
name: "externalFunc",
|
|
1305
|
+
kind: "function",
|
|
1306
|
+
sourceLanguage: ESourceLanguage.C,
|
|
1307
|
+
sourceFile: "external.h",
|
|
1308
|
+
sourceLine: 1,
|
|
1309
|
+
isExported: true,
|
|
1310
|
+
type: "void",
|
|
1311
|
+
parameters: [],
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
1315
|
+
const errors = analyzer.analyze(tree, symbolTable);
|
|
1316
|
+
|
|
1317
|
+
expect(errors).toHaveLength(0);
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1120
1320
|
});
|