c-next 0.1.62 → 0.1.63
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 +86 -63
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +3 -2
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* Tests all validation methods for 100% coverage
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
7
7
|
import TypeValidator from "../TypeValidator";
|
|
8
|
+
import TypeResolver from "../TypeResolver";
|
|
9
|
+
import CodeGenState from "../CodeGenState";
|
|
8
10
|
import type ICodeGenSymbols from "../../../types/ICodeGenSymbols";
|
|
9
11
|
import type TTypeInfo from "../types/TTypeInfo";
|
|
10
12
|
import type TParameterInfo from "../types/TParameterInfo";
|
|
11
13
|
import type ICallbackTypeInfo from "../types/ICallbackTypeInfo";
|
|
12
|
-
import type ITypeValidatorDeps from "../types/ITypeValidatorDeps";
|
|
13
14
|
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
14
15
|
|
|
15
16
|
// ========================================================================
|
|
@@ -50,39 +51,52 @@ function createMockSymbols(
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
// ========================================================================
|
|
53
|
-
// Test Helpers -
|
|
54
|
+
// Test Helpers - Setup State
|
|
54
55
|
// ========================================================================
|
|
55
56
|
|
|
56
|
-
interface
|
|
57
|
+
interface SetupStateOptions {
|
|
57
58
|
symbols?: ICodeGenSymbols;
|
|
58
59
|
typeRegistry?: Map<string, TTypeInfo>;
|
|
59
60
|
callbackTypes?: Map<string, ICallbackTypeInfo>;
|
|
60
61
|
knownFunctions?: Set<string>;
|
|
61
|
-
knownGlobals?: Set<string>;
|
|
62
62
|
currentScope?: string | null;
|
|
63
63
|
scopeMembers?: Map<string, Set<string>>;
|
|
64
64
|
currentParameters?: Map<string, TParameterInfo>;
|
|
65
65
|
localVariables?: Set<string>;
|
|
66
|
-
resolveIdentifier?: (name: string) => string;
|
|
67
|
-
getExpressionType?: (ctx: unknown) => string | null;
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
function setupState(options: SetupStateOptions = {}): void {
|
|
69
|
+
CodeGenState.reset();
|
|
70
|
+
if (options.symbols) {
|
|
71
|
+
CodeGenState.symbols = options.symbols;
|
|
72
|
+
} else {
|
|
73
|
+
CodeGenState.symbols = createMockSymbols();
|
|
74
|
+
}
|
|
75
|
+
if (options.typeRegistry) {
|
|
76
|
+
for (const [k, v] of options.typeRegistry) {
|
|
77
|
+
CodeGenState.typeRegistry.set(k, v);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (options.callbackTypes) {
|
|
81
|
+
for (const [k, v] of options.callbackTypes) {
|
|
82
|
+
CodeGenState.callbackTypes.set(k, v);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (options.knownFunctions) {
|
|
86
|
+
CodeGenState.knownFunctions = options.knownFunctions;
|
|
87
|
+
}
|
|
88
|
+
if (options.currentScope !== undefined) {
|
|
89
|
+
CodeGenState.currentScope = options.currentScope;
|
|
90
|
+
}
|
|
91
|
+
if (options.scopeMembers) {
|
|
92
|
+
CodeGenState.scopeMembers = options.scopeMembers;
|
|
93
|
+
}
|
|
94
|
+
if (options.currentParameters) {
|
|
95
|
+
CodeGenState.currentParameters = options.currentParameters;
|
|
96
|
+
}
|
|
97
|
+
if (options.localVariables) {
|
|
98
|
+
CodeGenState.localVariables = options.localVariables;
|
|
99
|
+
}
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
// ========================================================================
|
|
@@ -238,14 +252,25 @@ function createMockSwitchCase(
|
|
|
238
252
|
// ========================================================================
|
|
239
253
|
|
|
240
254
|
describe("TypeValidator", () => {
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
CodeGenState.reset();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
afterEach(() => {
|
|
260
|
+
vi.restoreAllMocks();
|
|
261
|
+
});
|
|
262
|
+
|
|
241
263
|
describe("validateIncludeNotImplementationFile", () => {
|
|
242
264
|
it("allows header file includes", () => {
|
|
243
|
-
|
|
265
|
+
setupState();
|
|
244
266
|
expect(() =>
|
|
245
|
-
|
|
267
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
268
|
+
'#include "file.h"',
|
|
269
|
+
1,
|
|
270
|
+
),
|
|
246
271
|
).not.toThrow();
|
|
247
272
|
expect(() =>
|
|
248
|
-
|
|
273
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
249
274
|
"#include <file.hpp>",
|
|
250
275
|
1,
|
|
251
276
|
),
|
|
@@ -253,22 +278,31 @@ describe("TypeValidator", () => {
|
|
|
253
278
|
});
|
|
254
279
|
|
|
255
280
|
it("rejects .c file includes", () => {
|
|
256
|
-
|
|
281
|
+
setupState();
|
|
257
282
|
expect(() =>
|
|
258
|
-
|
|
283
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
284
|
+
'#include "impl.c"',
|
|
285
|
+
5,
|
|
286
|
+
),
|
|
259
287
|
).toThrow("E0503");
|
|
260
288
|
expect(() =>
|
|
261
|
-
|
|
289
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
290
|
+
'#include "impl.c"',
|
|
291
|
+
5,
|
|
292
|
+
),
|
|
262
293
|
).toThrow("impl.c");
|
|
263
294
|
expect(() =>
|
|
264
|
-
|
|
295
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
296
|
+
'#include "impl.c"',
|
|
297
|
+
5,
|
|
298
|
+
),
|
|
265
299
|
).toThrow("Line 5");
|
|
266
300
|
});
|
|
267
301
|
|
|
268
302
|
it("rejects .cpp file includes", () => {
|
|
269
|
-
|
|
303
|
+
setupState();
|
|
270
304
|
expect(() =>
|
|
271
|
-
|
|
305
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
272
306
|
'#include "impl.cpp"',
|
|
273
307
|
1,
|
|
274
308
|
),
|
|
@@ -276,16 +310,19 @@ describe("TypeValidator", () => {
|
|
|
276
310
|
});
|
|
277
311
|
|
|
278
312
|
it("rejects .cc file includes", () => {
|
|
279
|
-
|
|
313
|
+
setupState();
|
|
280
314
|
expect(() =>
|
|
281
|
-
|
|
315
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
316
|
+
"#include <impl.cc>",
|
|
317
|
+
1,
|
|
318
|
+
),
|
|
282
319
|
).toThrow("E0503");
|
|
283
320
|
});
|
|
284
321
|
|
|
285
322
|
it("rejects .cxx file includes", () => {
|
|
286
|
-
|
|
323
|
+
setupState();
|
|
287
324
|
expect(() =>
|
|
288
|
-
|
|
325
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
289
326
|
'#include "impl.cxx"',
|
|
290
327
|
1,
|
|
291
328
|
),
|
|
@@ -293,9 +330,9 @@ describe("TypeValidator", () => {
|
|
|
293
330
|
});
|
|
294
331
|
|
|
295
332
|
it("rejects .c++ file includes", () => {
|
|
296
|
-
|
|
333
|
+
setupState();
|
|
297
334
|
expect(() =>
|
|
298
|
-
|
|
335
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
299
336
|
'#include "impl.c++"',
|
|
300
337
|
1,
|
|
301
338
|
),
|
|
@@ -303,12 +340,15 @@ describe("TypeValidator", () => {
|
|
|
303
340
|
});
|
|
304
341
|
|
|
305
342
|
it("is case-insensitive for extensions", () => {
|
|
306
|
-
|
|
343
|
+
setupState();
|
|
307
344
|
expect(() =>
|
|
308
|
-
|
|
345
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
346
|
+
'#include "impl.C"',
|
|
347
|
+
1,
|
|
348
|
+
),
|
|
309
349
|
).toThrow("E0503");
|
|
310
350
|
expect(() =>
|
|
311
|
-
|
|
351
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
312
352
|
'#include "impl.CPP"',
|
|
313
353
|
1,
|
|
314
354
|
),
|
|
@@ -316,36 +356,39 @@ describe("TypeValidator", () => {
|
|
|
316
356
|
});
|
|
317
357
|
|
|
318
358
|
it("handles malformed includes gracefully", () => {
|
|
319
|
-
|
|
359
|
+
setupState();
|
|
320
360
|
// No path extracted - should not throw
|
|
321
361
|
expect(() =>
|
|
322
|
-
|
|
362
|
+
TypeValidator.validateIncludeNotImplementationFile("#include", 1),
|
|
323
363
|
).not.toThrow();
|
|
324
364
|
expect(() =>
|
|
325
|
-
|
|
365
|
+
TypeValidator.validateIncludeNotImplementationFile('#include ""', 1),
|
|
326
366
|
).not.toThrow();
|
|
327
367
|
});
|
|
328
368
|
|
|
329
369
|
it("handles angle bracket includes", () => {
|
|
330
|
-
|
|
370
|
+
setupState();
|
|
331
371
|
expect(() =>
|
|
332
|
-
|
|
372
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
333
373
|
"#include <system.h>",
|
|
334
374
|
1,
|
|
335
375
|
),
|
|
336
376
|
).not.toThrow();
|
|
337
377
|
expect(() =>
|
|
338
|
-
|
|
378
|
+
TypeValidator.validateIncludeNotImplementationFile(
|
|
379
|
+
"#include <impl.c>",
|
|
380
|
+
1,
|
|
381
|
+
),
|
|
339
382
|
).toThrow("E0503");
|
|
340
383
|
});
|
|
341
384
|
});
|
|
342
385
|
|
|
343
386
|
describe("validateIncludeNoCnxAlternative", () => {
|
|
344
387
|
it("skips .cnx includes", () => {
|
|
345
|
-
|
|
388
|
+
setupState();
|
|
346
389
|
const fileExists = vi.fn(() => true);
|
|
347
390
|
expect(() =>
|
|
348
|
-
|
|
391
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
349
392
|
'#include "file.cnx"',
|
|
350
393
|
1,
|
|
351
394
|
"/src/main.cnx",
|
|
@@ -357,10 +400,10 @@ describe("TypeValidator", () => {
|
|
|
357
400
|
});
|
|
358
401
|
|
|
359
402
|
it("skips non-header files", () => {
|
|
360
|
-
|
|
403
|
+
setupState();
|
|
361
404
|
const fileExists = vi.fn(() => true);
|
|
362
405
|
expect(() =>
|
|
363
|
-
|
|
406
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
364
407
|
'#include "file.txt"',
|
|
365
408
|
1,
|
|
366
409
|
"/src/main.cnx",
|
|
@@ -372,10 +415,10 @@ describe("TypeValidator", () => {
|
|
|
372
415
|
});
|
|
373
416
|
|
|
374
417
|
it("throws E0504 when .cnx alternative exists for quoted include", () => {
|
|
375
|
-
|
|
418
|
+
setupState();
|
|
376
419
|
const fileExists = vi.fn(() => true);
|
|
377
420
|
expect(() =>
|
|
378
|
-
|
|
421
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
379
422
|
'#include "utils.h"',
|
|
380
423
|
10,
|
|
381
424
|
"/src/main.cnx",
|
|
@@ -384,7 +427,7 @@ describe("TypeValidator", () => {
|
|
|
384
427
|
),
|
|
385
428
|
).toThrow("E0504");
|
|
386
429
|
expect(() =>
|
|
387
|
-
|
|
430
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
388
431
|
'#include "utils.h"',
|
|
389
432
|
10,
|
|
390
433
|
"/src/main.cnx",
|
|
@@ -395,10 +438,10 @@ describe("TypeValidator", () => {
|
|
|
395
438
|
});
|
|
396
439
|
|
|
397
440
|
it("does not throw when .cnx alternative does not exist", () => {
|
|
398
|
-
|
|
441
|
+
setupState();
|
|
399
442
|
const fileExists = vi.fn(() => false);
|
|
400
443
|
expect(() =>
|
|
401
|
-
|
|
444
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
402
445
|
'#include "utils.h"',
|
|
403
446
|
1,
|
|
404
447
|
"/src/main.cnx",
|
|
@@ -409,10 +452,10 @@ describe("TypeValidator", () => {
|
|
|
409
452
|
});
|
|
410
453
|
|
|
411
454
|
it("throws E0504 when .cnx alternative exists for angle bracket include", () => {
|
|
412
|
-
|
|
455
|
+
setupState();
|
|
413
456
|
const fileExists = vi.fn(() => true);
|
|
414
457
|
expect(() =>
|
|
415
|
-
|
|
458
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
416
459
|
"#include <lib/utils.hpp>",
|
|
417
460
|
5,
|
|
418
461
|
"/src/main.cnx",
|
|
@@ -423,10 +466,10 @@ describe("TypeValidator", () => {
|
|
|
423
466
|
});
|
|
424
467
|
|
|
425
468
|
it("searches through all include paths for angle includes", () => {
|
|
426
|
-
|
|
469
|
+
setupState();
|
|
427
470
|
const fileExists = vi.fn((path: string) => path.includes("/lib2/"));
|
|
428
471
|
expect(() =>
|
|
429
|
-
|
|
472
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
430
473
|
"#include <utils.h>",
|
|
431
474
|
1,
|
|
432
475
|
"/src/main.cnx",
|
|
@@ -438,11 +481,11 @@ describe("TypeValidator", () => {
|
|
|
438
481
|
});
|
|
439
482
|
|
|
440
483
|
it("handles quoted include without sourcePath", () => {
|
|
441
|
-
|
|
484
|
+
setupState();
|
|
442
485
|
const fileExists = vi.fn(() => true);
|
|
443
486
|
// No source path - cannot resolve relative include
|
|
444
487
|
expect(() =>
|
|
445
|
-
|
|
488
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
446
489
|
'#include "utils.h"',
|
|
447
490
|
1,
|
|
448
491
|
null,
|
|
@@ -453,10 +496,10 @@ describe("TypeValidator", () => {
|
|
|
453
496
|
});
|
|
454
497
|
|
|
455
498
|
it("handles malformed includes", () => {
|
|
456
|
-
|
|
499
|
+
setupState();
|
|
457
500
|
const fileExists = vi.fn(() => true);
|
|
458
501
|
expect(() =>
|
|
459
|
-
|
|
502
|
+
TypeValidator.validateIncludeNoCnxAlternative(
|
|
460
503
|
"#include",
|
|
461
504
|
1,
|
|
462
505
|
"/src/main.cnx",
|
|
@@ -473,50 +516,50 @@ describe("TypeValidator", () => {
|
|
|
473
516
|
|
|
474
517
|
describe("validateBitmapFieldLiteral", () => {
|
|
475
518
|
it("allows values within field width", () => {
|
|
476
|
-
|
|
519
|
+
setupState();
|
|
477
520
|
const expr = createMockExpression("7");
|
|
478
521
|
expect(() =>
|
|
479
|
-
|
|
522
|
+
TypeValidator.validateBitmapFieldLiteral(expr, 3, "flags"),
|
|
480
523
|
).not.toThrow();
|
|
481
524
|
});
|
|
482
525
|
|
|
483
526
|
it("throws for decimal values exceeding field width", () => {
|
|
484
|
-
|
|
527
|
+
setupState();
|
|
485
528
|
const expr = createMockExpression("8");
|
|
486
529
|
expect(() =>
|
|
487
|
-
|
|
530
|
+
TypeValidator.validateBitmapFieldLiteral(expr, 3, "flags"),
|
|
488
531
|
).toThrow("Value 8 exceeds 3-bit field 'flags' maximum of 7");
|
|
489
532
|
});
|
|
490
533
|
|
|
491
534
|
it("validates hex literals", () => {
|
|
492
|
-
|
|
535
|
+
setupState();
|
|
493
536
|
const expr = createMockExpression("0xFF");
|
|
494
537
|
expect(() =>
|
|
495
|
-
|
|
538
|
+
TypeValidator.validateBitmapFieldLiteral(expr, 8, "byte"),
|
|
496
539
|
).not.toThrow();
|
|
497
540
|
const exprBad = createMockExpression("0x100");
|
|
498
541
|
expect(() =>
|
|
499
|
-
|
|
542
|
+
TypeValidator.validateBitmapFieldLiteral(exprBad, 8, "byte"),
|
|
500
543
|
).toThrow("Value 256 exceeds 8-bit field 'byte' maximum of 255");
|
|
501
544
|
});
|
|
502
545
|
|
|
503
546
|
it("validates binary literals", () => {
|
|
504
|
-
|
|
547
|
+
setupState();
|
|
505
548
|
const expr = createMockExpression("0b1111");
|
|
506
549
|
expect(() =>
|
|
507
|
-
|
|
550
|
+
TypeValidator.validateBitmapFieldLiteral(expr, 4, "nibble"),
|
|
508
551
|
).not.toThrow();
|
|
509
552
|
const exprBad = createMockExpression("0b10000");
|
|
510
553
|
expect(() =>
|
|
511
|
-
|
|
554
|
+
TypeValidator.validateBitmapFieldLiteral(exprBad, 4, "nibble"),
|
|
512
555
|
).toThrow("Value 16 exceeds 4-bit field 'nibble' maximum of 15");
|
|
513
556
|
});
|
|
514
557
|
|
|
515
558
|
it("skips validation for non-literal expressions", () => {
|
|
516
|
-
|
|
559
|
+
setupState();
|
|
517
560
|
const expr = createMockExpression("someVariable");
|
|
518
561
|
expect(() =>
|
|
519
|
-
|
|
562
|
+
TypeValidator.validateBitmapFieldLiteral(expr, 1, "bit"),
|
|
520
563
|
).not.toThrow();
|
|
521
564
|
});
|
|
522
565
|
});
|
|
@@ -527,58 +570,64 @@ describe("TypeValidator", () => {
|
|
|
527
570
|
|
|
528
571
|
describe("checkArrayBounds", () => {
|
|
529
572
|
it("allows valid constant indices", () => {
|
|
530
|
-
|
|
573
|
+
setupState();
|
|
531
574
|
const indexExprs = [createMockExpression("0")];
|
|
532
575
|
const tryEval = vi.fn(() => 0);
|
|
533
576
|
expect(() =>
|
|
534
|
-
|
|
577
|
+
TypeValidator.checkArrayBounds("arr", [10], indexExprs, 1, tryEval),
|
|
535
578
|
).not.toThrow();
|
|
536
579
|
});
|
|
537
580
|
|
|
538
581
|
it("throws for negative indices", () => {
|
|
539
|
-
|
|
582
|
+
setupState();
|
|
540
583
|
const indexExprs = [createMockExpression("-1")];
|
|
541
584
|
const tryEval = vi.fn(() => -1);
|
|
542
585
|
expect(() =>
|
|
543
|
-
|
|
586
|
+
TypeValidator.checkArrayBounds("arr", [10], indexExprs, 5, tryEval),
|
|
544
587
|
).toThrow("Array index out of bounds: -1 is negative for 'arr'");
|
|
545
588
|
});
|
|
546
589
|
|
|
547
590
|
it("throws for index >= dimension", () => {
|
|
548
|
-
|
|
591
|
+
setupState();
|
|
549
592
|
const indexExprs = [createMockExpression("10")];
|
|
550
593
|
const tryEval = vi.fn(() => 10);
|
|
551
594
|
expect(() =>
|
|
552
|
-
|
|
595
|
+
TypeValidator.checkArrayBounds("arr", [10], indexExprs, 3, tryEval),
|
|
553
596
|
).toThrow("Array index out of bounds: 10 >= 10 for 'arr' dimension 1");
|
|
554
597
|
});
|
|
555
598
|
|
|
556
599
|
it("checks all dimensions for multi-dimensional arrays", () => {
|
|
557
|
-
|
|
600
|
+
setupState();
|
|
558
601
|
const indexExprs = [createMockExpression("0"), createMockExpression("5")];
|
|
559
602
|
let callIdx = 0;
|
|
560
603
|
const tryEval = vi.fn(() => (callIdx++ === 0 ? 0 : 5));
|
|
561
604
|
expect(() =>
|
|
562
|
-
|
|
605
|
+
TypeValidator.checkArrayBounds(
|
|
606
|
+
"matrix",
|
|
607
|
+
[3, 4],
|
|
608
|
+
indexExprs,
|
|
609
|
+
1,
|
|
610
|
+
tryEval,
|
|
611
|
+
),
|
|
563
612
|
).toThrow("Array index out of bounds: 5 >= 4 for 'matrix' dimension 2");
|
|
564
613
|
});
|
|
565
614
|
|
|
566
615
|
it("skips non-constant indices", () => {
|
|
567
|
-
|
|
616
|
+
setupState();
|
|
568
617
|
const indexExprs = [createMockExpression("i")];
|
|
569
618
|
const tryEval = vi.fn(() => undefined);
|
|
570
619
|
expect(() =>
|
|
571
|
-
|
|
620
|
+
TypeValidator.checkArrayBounds("arr", [10], indexExprs, 1, tryEval),
|
|
572
621
|
).not.toThrow();
|
|
573
622
|
});
|
|
574
623
|
|
|
575
624
|
it("skips upper bound check for unsized dimensions (Issue #547)", () => {
|
|
576
|
-
|
|
625
|
+
setupState();
|
|
577
626
|
const indexExprs = [createMockExpression("100")];
|
|
578
627
|
const tryEval = vi.fn(() => 100);
|
|
579
628
|
// Dimension 0 means unsized array
|
|
580
629
|
expect(() =>
|
|
581
|
-
|
|
630
|
+
TypeValidator.checkArrayBounds("unsized", [0], indexExprs, 1, tryEval),
|
|
582
631
|
).not.toThrow();
|
|
583
632
|
});
|
|
584
633
|
});
|
|
@@ -589,7 +638,7 @@ describe("TypeValidator", () => {
|
|
|
589
638
|
|
|
590
639
|
describe("callbackSignaturesMatch", () => {
|
|
591
640
|
it("returns true for matching signatures", () => {
|
|
592
|
-
|
|
641
|
+
setupState();
|
|
593
642
|
const a: ICallbackTypeInfo = {
|
|
594
643
|
functionName: "handler",
|
|
595
644
|
returnType: "void",
|
|
@@ -620,11 +669,11 @@ describe("TypeValidator", () => {
|
|
|
620
669
|
],
|
|
621
670
|
typedefName: "other_fp",
|
|
622
671
|
};
|
|
623
|
-
expect(
|
|
672
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(true);
|
|
624
673
|
});
|
|
625
674
|
|
|
626
675
|
it("returns false for different return types", () => {
|
|
627
|
-
|
|
676
|
+
setupState();
|
|
628
677
|
const a: ICallbackTypeInfo = {
|
|
629
678
|
functionName: "a",
|
|
630
679
|
returnType: "void",
|
|
@@ -637,11 +686,11 @@ describe("TypeValidator", () => {
|
|
|
637
686
|
parameters: [],
|
|
638
687
|
typedefName: "b_fp",
|
|
639
688
|
};
|
|
640
|
-
expect(
|
|
689
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
641
690
|
});
|
|
642
691
|
|
|
643
692
|
it("returns false for different parameter counts", () => {
|
|
644
|
-
|
|
693
|
+
setupState();
|
|
645
694
|
const a: ICallbackTypeInfo = {
|
|
646
695
|
functionName: "a",
|
|
647
696
|
returnType: "void",
|
|
@@ -663,11 +712,11 @@ describe("TypeValidator", () => {
|
|
|
663
712
|
parameters: [],
|
|
664
713
|
typedefName: "b_fp",
|
|
665
714
|
};
|
|
666
|
-
expect(
|
|
715
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
667
716
|
});
|
|
668
717
|
|
|
669
718
|
it("returns false for different parameter types", () => {
|
|
670
|
-
|
|
719
|
+
setupState();
|
|
671
720
|
const a: ICallbackTypeInfo = {
|
|
672
721
|
functionName: "a",
|
|
673
722
|
returnType: "void",
|
|
@@ -698,11 +747,11 @@ describe("TypeValidator", () => {
|
|
|
698
747
|
],
|
|
699
748
|
typedefName: "b_fp",
|
|
700
749
|
};
|
|
701
|
-
expect(
|
|
750
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
702
751
|
});
|
|
703
752
|
|
|
704
753
|
it("returns false for different const-ness", () => {
|
|
705
|
-
|
|
754
|
+
setupState();
|
|
706
755
|
const a: ICallbackTypeInfo = {
|
|
707
756
|
functionName: "a",
|
|
708
757
|
returnType: "void",
|
|
@@ -733,11 +782,11 @@ describe("TypeValidator", () => {
|
|
|
733
782
|
],
|
|
734
783
|
typedefName: "b_fp",
|
|
735
784
|
};
|
|
736
|
-
expect(
|
|
785
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
737
786
|
});
|
|
738
787
|
|
|
739
788
|
it("returns false for different pointer-ness", () => {
|
|
740
|
-
|
|
789
|
+
setupState();
|
|
741
790
|
const a: ICallbackTypeInfo = {
|
|
742
791
|
functionName: "a",
|
|
743
792
|
returnType: "void",
|
|
@@ -768,11 +817,11 @@ describe("TypeValidator", () => {
|
|
|
768
817
|
],
|
|
769
818
|
typedefName: "b_fp",
|
|
770
819
|
};
|
|
771
|
-
expect(
|
|
820
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
772
821
|
});
|
|
773
822
|
|
|
774
823
|
it("returns false for different array-ness", () => {
|
|
775
|
-
|
|
824
|
+
setupState();
|
|
776
825
|
const a: ICallbackTypeInfo = {
|
|
777
826
|
functionName: "a",
|
|
778
827
|
returnType: "void",
|
|
@@ -803,18 +852,16 @@ describe("TypeValidator", () => {
|
|
|
803
852
|
],
|
|
804
853
|
typedefName: "b_fp",
|
|
805
854
|
};
|
|
806
|
-
expect(
|
|
855
|
+
expect(TypeValidator.callbackSignaturesMatch(a, b)).toBe(false);
|
|
807
856
|
});
|
|
808
857
|
});
|
|
809
858
|
|
|
810
859
|
describe("validateCallbackAssignment", () => {
|
|
811
860
|
it("skips validation for non-function values", () => {
|
|
812
|
-
|
|
813
|
-
createMockDeps({ knownFunctions: new Set(["handler"]) }),
|
|
814
|
-
);
|
|
861
|
+
setupState({ knownFunctions: new Set(["handler"]) });
|
|
815
862
|
const expr = createMockExpression("notAFunction");
|
|
816
863
|
expect(() =>
|
|
817
|
-
|
|
864
|
+
TypeValidator.validateCallbackAssignment(
|
|
818
865
|
"Handler",
|
|
819
866
|
expr,
|
|
820
867
|
"callback",
|
|
@@ -824,12 +871,10 @@ describe("TypeValidator", () => {
|
|
|
824
871
|
});
|
|
825
872
|
|
|
826
873
|
it("skips validation when callback types are not found", () => {
|
|
827
|
-
|
|
828
|
-
createMockDeps({ knownFunctions: new Set(["handler"]) }),
|
|
829
|
-
);
|
|
874
|
+
setupState({ knownFunctions: new Set(["handler"]) });
|
|
830
875
|
const expr = createMockExpression("handler");
|
|
831
876
|
expect(() =>
|
|
832
|
-
|
|
877
|
+
TypeValidator.validateCallbackAssignment(
|
|
833
878
|
"Handler",
|
|
834
879
|
expr,
|
|
835
880
|
"callback",
|
|
@@ -859,15 +904,13 @@ describe("TypeValidator", () => {
|
|
|
859
904
|
},
|
|
860
905
|
],
|
|
861
906
|
]);
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
}),
|
|
867
|
-
);
|
|
907
|
+
setupState({
|
|
908
|
+
knownFunctions: new Set(["wrongFunc"]),
|
|
909
|
+
callbackTypes,
|
|
910
|
+
});
|
|
868
911
|
const expr = createMockExpression("wrongFunc");
|
|
869
912
|
expect(() =>
|
|
870
|
-
|
|
913
|
+
TypeValidator.validateCallbackAssignment(
|
|
871
914
|
"Handler",
|
|
872
915
|
expr,
|
|
873
916
|
"callback",
|
|
@@ -899,16 +942,14 @@ describe("TypeValidator", () => {
|
|
|
899
942
|
},
|
|
900
943
|
],
|
|
901
944
|
]);
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
}),
|
|
907
|
-
);
|
|
945
|
+
setupState({
|
|
946
|
+
knownFunctions: new Set(["TypeB"]),
|
|
947
|
+
callbackTypes,
|
|
948
|
+
});
|
|
908
949
|
const expr = createMockExpression("TypeB");
|
|
909
950
|
// TypeB is used as a field type, so nominal typing applies
|
|
910
951
|
expect(() =>
|
|
911
|
-
|
|
952
|
+
TypeValidator.validateCallbackAssignment(
|
|
912
953
|
"TypeA",
|
|
913
954
|
expr,
|
|
914
955
|
"handler",
|
|
@@ -938,15 +979,13 @@ describe("TypeValidator", () => {
|
|
|
938
979
|
},
|
|
939
980
|
],
|
|
940
981
|
]);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}),
|
|
946
|
-
);
|
|
982
|
+
setupState({
|
|
983
|
+
knownFunctions: new Set(["myHandler"]),
|
|
984
|
+
callbackTypes,
|
|
985
|
+
});
|
|
947
986
|
const expr = createMockExpression("myHandler");
|
|
948
987
|
expect(() =>
|
|
949
|
-
|
|
988
|
+
TypeValidator.validateCallbackAssignment(
|
|
950
989
|
"Handler",
|
|
951
990
|
expr,
|
|
952
991
|
"callback",
|
|
@@ -968,16 +1007,16 @@ describe("TypeValidator", () => {
|
|
|
968
1007
|
{ baseType: "u32", bitWidth: 32, isArray: false, isConst: false },
|
|
969
1008
|
],
|
|
970
1009
|
]);
|
|
971
|
-
|
|
972
|
-
expect(
|
|
1010
|
+
setupState({ typeRegistry });
|
|
1011
|
+
expect(TypeValidator.checkConstAssignment("x")).toBeNull();
|
|
973
1012
|
});
|
|
974
1013
|
|
|
975
1014
|
it("returns error for const variables", () => {
|
|
976
1015
|
const typeRegistry = new Map<string, TTypeInfo>([
|
|
977
1016
|
["x", { baseType: "u32", bitWidth: 32, isArray: false, isConst: true }],
|
|
978
1017
|
]);
|
|
979
|
-
|
|
980
|
-
expect(
|
|
1018
|
+
setupState({ typeRegistry });
|
|
1019
|
+
expect(TypeValidator.checkConstAssignment("x")).toContain(
|
|
981
1020
|
"cannot assign to const variable 'x'",
|
|
982
1021
|
);
|
|
983
1022
|
});
|
|
@@ -997,10 +1036,8 @@ describe("TypeValidator", () => {
|
|
|
997
1036
|
},
|
|
998
1037
|
],
|
|
999
1038
|
]);
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
);
|
|
1003
|
-
expect(validator.checkConstAssignment("param")).toContain(
|
|
1039
|
+
setupState({ currentParameters });
|
|
1040
|
+
expect(TypeValidator.checkConstAssignment("param")).toContain(
|
|
1004
1041
|
"cannot assign to const parameter 'param'",
|
|
1005
1042
|
);
|
|
1006
1043
|
});
|
|
@@ -1012,13 +1049,12 @@ describe("TypeValidator", () => {
|
|
|
1012
1049
|
{ baseType: "u32", bitWidth: 32, isArray: false, isConst: true },
|
|
1013
1050
|
],
|
|
1014
1051
|
]);
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
)
|
|
1021
|
-
expect(validator.checkConstAssignment("x")).toContain(
|
|
1052
|
+
setupState({
|
|
1053
|
+
typeRegistry,
|
|
1054
|
+
currentScope: "Scope",
|
|
1055
|
+
scopeMembers: new Map([["Scope", new Set(["x"])]]),
|
|
1056
|
+
});
|
|
1057
|
+
expect(TypeValidator.checkConstAssignment("x")).toContain(
|
|
1022
1058
|
"cannot assign to const variable",
|
|
1023
1059
|
);
|
|
1024
1060
|
});
|
|
@@ -1040,18 +1076,16 @@ describe("TypeValidator", () => {
|
|
|
1040
1076
|
},
|
|
1041
1077
|
],
|
|
1042
1078
|
]);
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
);
|
|
1046
|
-
expect(validator.isConstValue("param")).toBe(true);
|
|
1079
|
+
setupState({ currentParameters });
|
|
1080
|
+
expect(TypeValidator.isConstValue("param")).toBe(true);
|
|
1047
1081
|
});
|
|
1048
1082
|
|
|
1049
1083
|
it("returns true for const variables", () => {
|
|
1050
1084
|
const typeRegistry = new Map<string, TTypeInfo>([
|
|
1051
1085
|
["x", { baseType: "u32", bitWidth: 32, isArray: false, isConst: true }],
|
|
1052
1086
|
]);
|
|
1053
|
-
|
|
1054
|
-
expect(
|
|
1087
|
+
setupState({ typeRegistry });
|
|
1088
|
+
expect(TypeValidator.isConstValue("x")).toBe(true);
|
|
1055
1089
|
});
|
|
1056
1090
|
|
|
1057
1091
|
it("returns false for mutable variables", () => {
|
|
@@ -1061,13 +1095,13 @@ describe("TypeValidator", () => {
|
|
|
1061
1095
|
{ baseType: "u32", bitWidth: 32, isArray: false, isConst: false },
|
|
1062
1096
|
],
|
|
1063
1097
|
]);
|
|
1064
|
-
|
|
1065
|
-
expect(
|
|
1098
|
+
setupState({ typeRegistry });
|
|
1099
|
+
expect(TypeValidator.isConstValue("x")).toBe(false);
|
|
1066
1100
|
});
|
|
1067
1101
|
|
|
1068
1102
|
it("returns false for unknown identifiers", () => {
|
|
1069
|
-
|
|
1070
|
-
expect(
|
|
1103
|
+
setupState();
|
|
1104
|
+
expect(TypeValidator.isConstValue("unknown")).toBe(false);
|
|
1071
1105
|
});
|
|
1072
1106
|
});
|
|
1073
1107
|
|
|
@@ -1077,30 +1111,36 @@ describe("TypeValidator", () => {
|
|
|
1077
1111
|
|
|
1078
1112
|
describe("validateBareIdentifierInScope", () => {
|
|
1079
1113
|
it("does nothing outside a scope", () => {
|
|
1080
|
-
|
|
1081
|
-
createMockDeps({ currentScope: null }),
|
|
1082
|
-
);
|
|
1114
|
+
setupState({ currentScope: null });
|
|
1083
1115
|
expect(() =>
|
|
1084
|
-
|
|
1116
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1117
|
+
"anything",
|
|
1118
|
+
false,
|
|
1119
|
+
() => false,
|
|
1120
|
+
),
|
|
1085
1121
|
).not.toThrow();
|
|
1086
1122
|
});
|
|
1087
1123
|
|
|
1088
1124
|
it("allows local variables as bare identifiers", () => {
|
|
1089
|
-
|
|
1090
|
-
createMockDeps({ currentScope: "Motor" }),
|
|
1091
|
-
);
|
|
1125
|
+
setupState({ currentScope: "Motor" });
|
|
1092
1126
|
expect(() =>
|
|
1093
|
-
|
|
1127
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1128
|
+
"localVar",
|
|
1129
|
+
true,
|
|
1130
|
+
() => false,
|
|
1131
|
+
),
|
|
1094
1132
|
).not.toThrow();
|
|
1095
1133
|
});
|
|
1096
1134
|
|
|
1097
1135
|
it("throws for bare scope member access", () => {
|
|
1098
1136
|
const scopeMembers = new Map([["Motor", new Set(["speed"])]]);
|
|
1099
|
-
|
|
1100
|
-
createMockDeps({ currentScope: "Motor", scopeMembers }),
|
|
1101
|
-
);
|
|
1137
|
+
setupState({ currentScope: "Motor", scopeMembers });
|
|
1102
1138
|
expect(() =>
|
|
1103
|
-
|
|
1139
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1140
|
+
"speed",
|
|
1141
|
+
false,
|
|
1142
|
+
() => false,
|
|
1143
|
+
),
|
|
1104
1144
|
).toThrow(
|
|
1105
1145
|
"Use 'this.speed' to access scope member 'speed' inside scope 'Motor'",
|
|
1106
1146
|
);
|
|
@@ -1108,25 +1148,21 @@ describe("TypeValidator", () => {
|
|
|
1108
1148
|
|
|
1109
1149
|
it("throws for bare register access", () => {
|
|
1110
1150
|
const symbols = createMockSymbols({ knownRegisters: new Set(["GPIO"]) });
|
|
1111
|
-
|
|
1112
|
-
createMockDeps({ symbols, currentScope: "Motor" }),
|
|
1113
|
-
);
|
|
1151
|
+
setupState({ symbols, currentScope: "Motor" });
|
|
1114
1152
|
expect(() =>
|
|
1115
|
-
|
|
1153
|
+
TypeValidator.validateBareIdentifierInScope("GPIO", false, () => false),
|
|
1116
1154
|
).toThrow(
|
|
1117
1155
|
"Use 'global.GPIO' to access register 'GPIO' inside scope 'Motor'",
|
|
1118
1156
|
);
|
|
1119
1157
|
});
|
|
1120
1158
|
|
|
1121
1159
|
it("throws for bare global function access", () => {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
}),
|
|
1127
|
-
);
|
|
1160
|
+
setupState({
|
|
1161
|
+
currentScope: "Motor",
|
|
1162
|
+
knownFunctions: new Set(["globalFunc"]),
|
|
1163
|
+
});
|
|
1128
1164
|
expect(() =>
|
|
1129
|
-
|
|
1165
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1130
1166
|
"globalFunc",
|
|
1131
1167
|
false,
|
|
1132
1168
|
() => false,
|
|
@@ -1137,14 +1173,12 @@ describe("TypeValidator", () => {
|
|
|
1137
1173
|
});
|
|
1138
1174
|
|
|
1139
1175
|
it("allows scope-prefixed functions", () => {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
}),
|
|
1145
|
-
);
|
|
1176
|
+
setupState({
|
|
1177
|
+
currentScope: "Motor",
|
|
1178
|
+
knownFunctions: new Set(["Motor_helper"]),
|
|
1179
|
+
});
|
|
1146
1180
|
expect(() =>
|
|
1147
|
-
|
|
1181
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1148
1182
|
"Motor_helper",
|
|
1149
1183
|
false,
|
|
1150
1184
|
() => false,
|
|
@@ -1154,20 +1188,20 @@ describe("TypeValidator", () => {
|
|
|
1154
1188
|
|
|
1155
1189
|
it("throws for bare enum access", () => {
|
|
1156
1190
|
const symbols = createMockSymbols({ knownEnums: new Set(["State"]) });
|
|
1157
|
-
|
|
1158
|
-
createMockDeps({ symbols, currentScope: "Motor" }),
|
|
1159
|
-
);
|
|
1191
|
+
setupState({ symbols, currentScope: "Motor" });
|
|
1160
1192
|
expect(() =>
|
|
1161
|
-
|
|
1193
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1194
|
+
"State",
|
|
1195
|
+
false,
|
|
1196
|
+
() => false,
|
|
1197
|
+
),
|
|
1162
1198
|
).toThrow("Use 'global.State' to access global enum 'State'");
|
|
1163
1199
|
});
|
|
1164
1200
|
|
|
1165
1201
|
it("throws for bare struct access", () => {
|
|
1166
|
-
|
|
1167
|
-
createMockDeps({ currentScope: "Motor" }),
|
|
1168
|
-
);
|
|
1202
|
+
setupState({ currentScope: "Motor" });
|
|
1169
1203
|
expect(() =>
|
|
1170
|
-
|
|
1204
|
+
TypeValidator.validateBareIdentifierInScope("Point", false, () => true),
|
|
1171
1205
|
).toThrow("Use 'global.Point' to access global struct 'Point'");
|
|
1172
1206
|
});
|
|
1173
1207
|
|
|
@@ -1178,11 +1212,13 @@ describe("TypeValidator", () => {
|
|
|
1178
1212
|
{ baseType: "u32", bitWidth: 32, isArray: false, isConst: false },
|
|
1179
1213
|
],
|
|
1180
1214
|
]);
|
|
1181
|
-
|
|
1182
|
-
createMockDeps({ currentScope: "Motor", typeRegistry }),
|
|
1183
|
-
);
|
|
1215
|
+
setupState({ currentScope: "Motor", typeRegistry });
|
|
1184
1216
|
expect(() =>
|
|
1185
|
-
|
|
1217
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1218
|
+
"counter",
|
|
1219
|
+
false,
|
|
1220
|
+
() => false,
|
|
1221
|
+
),
|
|
1186
1222
|
).toThrow("Use 'global.counter' to access global variable 'counter'");
|
|
1187
1223
|
});
|
|
1188
1224
|
|
|
@@ -1193,11 +1229,9 @@ describe("TypeValidator", () => {
|
|
|
1193
1229
|
{ baseType: "u32", bitWidth: 32, isArray: false, isConst: false },
|
|
1194
1230
|
],
|
|
1195
1231
|
]);
|
|
1196
|
-
|
|
1197
|
-
createMockDeps({ currentScope: "Motor", typeRegistry }),
|
|
1198
|
-
);
|
|
1232
|
+
setupState({ currentScope: "Motor", typeRegistry });
|
|
1199
1233
|
expect(() =>
|
|
1200
|
-
|
|
1234
|
+
TypeValidator.validateBareIdentifierInScope(
|
|
1201
1235
|
"Motor_speed",
|
|
1202
1236
|
false,
|
|
1203
1237
|
() => false,
|
|
@@ -1212,36 +1246,36 @@ describe("TypeValidator", () => {
|
|
|
1212
1246
|
|
|
1213
1247
|
describe("validateNoEarlyExits", () => {
|
|
1214
1248
|
it("allows blocks without early exits", () => {
|
|
1215
|
-
|
|
1249
|
+
setupState();
|
|
1216
1250
|
const block = createMockBlock([
|
|
1217
1251
|
createMockStatement(),
|
|
1218
1252
|
createMockStatement(),
|
|
1219
1253
|
]);
|
|
1220
|
-
expect(() =>
|
|
1254
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).not.toThrow();
|
|
1221
1255
|
});
|
|
1222
1256
|
|
|
1223
1257
|
it("throws for return statement in critical block", () => {
|
|
1224
|
-
|
|
1258
|
+
setupState();
|
|
1225
1259
|
const block = createMockBlock([createMockStatement({ hasReturn: true })]);
|
|
1226
|
-
expect(() =>
|
|
1227
|
-
expect(() =>
|
|
1260
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1261
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow(
|
|
1228
1262
|
"Cannot use 'return' inside critical section",
|
|
1229
1263
|
);
|
|
1230
1264
|
});
|
|
1231
1265
|
|
|
1232
1266
|
it("throws for return in nested block", () => {
|
|
1233
|
-
|
|
1267
|
+
setupState();
|
|
1234
1268
|
const innerBlock = createMockBlock([
|
|
1235
1269
|
createMockStatement({ hasReturn: true }),
|
|
1236
1270
|
]);
|
|
1237
1271
|
const block = createMockBlock([
|
|
1238
1272
|
createMockStatement({ hasBlock: innerBlock }),
|
|
1239
1273
|
]);
|
|
1240
|
-
expect(() =>
|
|
1274
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1241
1275
|
});
|
|
1242
1276
|
|
|
1243
1277
|
it("throws for return in if statement", () => {
|
|
1244
|
-
|
|
1278
|
+
setupState();
|
|
1245
1279
|
const ifStmt = {
|
|
1246
1280
|
statement: () => [
|
|
1247
1281
|
{
|
|
@@ -1251,11 +1285,11 @@ describe("TypeValidator", () => {
|
|
|
1251
1285
|
],
|
|
1252
1286
|
} as Partial<Parser.IfStatementContext>;
|
|
1253
1287
|
const block = createMockBlock([createMockStatement({ hasIf: ifStmt })]);
|
|
1254
|
-
expect(() =>
|
|
1288
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1255
1289
|
});
|
|
1256
1290
|
|
|
1257
1291
|
it("throws for return in if statement's nested block", () => {
|
|
1258
|
-
|
|
1292
|
+
setupState();
|
|
1259
1293
|
const innerBlock = createMockBlock([
|
|
1260
1294
|
createMockStatement({ hasReturn: true }),
|
|
1261
1295
|
]);
|
|
@@ -1268,11 +1302,11 @@ describe("TypeValidator", () => {
|
|
|
1268
1302
|
],
|
|
1269
1303
|
} as Partial<Parser.IfStatementContext>;
|
|
1270
1304
|
const block = createMockBlock([createMockStatement({ hasIf: ifStmt })]);
|
|
1271
|
-
expect(() =>
|
|
1305
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1272
1306
|
});
|
|
1273
1307
|
|
|
1274
1308
|
it("throws for return in while loop", () => {
|
|
1275
|
-
|
|
1309
|
+
setupState();
|
|
1276
1310
|
const whileStmt = {
|
|
1277
1311
|
statement: () =>
|
|
1278
1312
|
({
|
|
@@ -1283,11 +1317,11 @@ describe("TypeValidator", () => {
|
|
|
1283
1317
|
const block = createMockBlock([
|
|
1284
1318
|
createMockStatement({ hasWhile: whileStmt }),
|
|
1285
1319
|
]);
|
|
1286
|
-
expect(() =>
|
|
1320
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1287
1321
|
});
|
|
1288
1322
|
|
|
1289
1323
|
it("throws for return in while loop's nested block", () => {
|
|
1290
|
-
|
|
1324
|
+
setupState();
|
|
1291
1325
|
const innerBlock = createMockBlock([
|
|
1292
1326
|
createMockStatement({ hasReturn: true }),
|
|
1293
1327
|
]);
|
|
@@ -1301,11 +1335,11 @@ describe("TypeValidator", () => {
|
|
|
1301
1335
|
const block = createMockBlock([
|
|
1302
1336
|
createMockStatement({ hasWhile: whileStmt }),
|
|
1303
1337
|
]);
|
|
1304
|
-
expect(() =>
|
|
1338
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1305
1339
|
});
|
|
1306
1340
|
|
|
1307
1341
|
it("throws for return in for loop", () => {
|
|
1308
|
-
|
|
1342
|
+
setupState();
|
|
1309
1343
|
const forStmt = {
|
|
1310
1344
|
statement: () =>
|
|
1311
1345
|
({
|
|
@@ -1314,11 +1348,11 @@ describe("TypeValidator", () => {
|
|
|
1314
1348
|
}) as unknown as Parser.StatementContext,
|
|
1315
1349
|
} as Partial<Parser.ForStatementContext>;
|
|
1316
1350
|
const block = createMockBlock([createMockStatement({ hasFor: forStmt })]);
|
|
1317
|
-
expect(() =>
|
|
1351
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1318
1352
|
});
|
|
1319
1353
|
|
|
1320
1354
|
it("throws for return in for loop's nested block", () => {
|
|
1321
|
-
|
|
1355
|
+
setupState();
|
|
1322
1356
|
const innerBlock = createMockBlock([
|
|
1323
1357
|
createMockStatement({ hasReturn: true }),
|
|
1324
1358
|
]);
|
|
@@ -1330,11 +1364,11 @@ describe("TypeValidator", () => {
|
|
|
1330
1364
|
}) as unknown as Parser.StatementContext,
|
|
1331
1365
|
} as Partial<Parser.ForStatementContext>;
|
|
1332
1366
|
const block = createMockBlock([createMockStatement({ hasFor: forStmt })]);
|
|
1333
|
-
expect(() =>
|
|
1367
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1334
1368
|
});
|
|
1335
1369
|
|
|
1336
1370
|
it("throws for return in do-while loop", () => {
|
|
1337
|
-
|
|
1371
|
+
setupState();
|
|
1338
1372
|
const innerBlock = createMockBlock([
|
|
1339
1373
|
createMockStatement({ hasReturn: true }),
|
|
1340
1374
|
]);
|
|
@@ -1344,7 +1378,7 @@ describe("TypeValidator", () => {
|
|
|
1344
1378
|
const block = createMockBlock([
|
|
1345
1379
|
createMockStatement({ hasDoWhile: doWhileStmt }),
|
|
1346
1380
|
]);
|
|
1347
|
-
expect(() =>
|
|
1381
|
+
expect(() => TypeValidator.validateNoEarlyExits(block)).toThrow("E0853");
|
|
1348
1382
|
});
|
|
1349
1383
|
});
|
|
1350
1384
|
|
|
@@ -1354,33 +1388,34 @@ describe("TypeValidator", () => {
|
|
|
1354
1388
|
|
|
1355
1389
|
describe("validateSwitchStatement", () => {
|
|
1356
1390
|
it("throws for boolean switch expression", () => {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
);
|
|
1391
|
+
setupState();
|
|
1392
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("bool");
|
|
1360
1393
|
const ctx = createMockSwitchStatement({
|
|
1361
1394
|
cases: [{} as Parser.SwitchCaseContext],
|
|
1362
1395
|
});
|
|
1363
1396
|
const expr = createMockExpression("flag");
|
|
1364
|
-
expect(() =>
|
|
1397
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1365
1398
|
"Cannot switch on boolean type (MISRA 16.7)",
|
|
1366
1399
|
);
|
|
1367
1400
|
});
|
|
1368
1401
|
|
|
1369
1402
|
it("throws for switch with less than 2 clauses", () => {
|
|
1370
|
-
|
|
1403
|
+
setupState();
|
|
1404
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue(null);
|
|
1371
1405
|
const ctx = createMockSwitchStatement({
|
|
1372
1406
|
cases: [
|
|
1373
1407
|
createMockSwitchCase([createMockCaseLabel({ intLiteral: "1" })]),
|
|
1374
1408
|
],
|
|
1375
1409
|
});
|
|
1376
1410
|
const expr = createMockExpression("x");
|
|
1377
|
-
expect(() =>
|
|
1411
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1378
1412
|
"Switch requires at least 2 clauses (MISRA 16.6)",
|
|
1379
1413
|
);
|
|
1380
1414
|
});
|
|
1381
1415
|
|
|
1382
1416
|
it("allows switch with exactly 2 cases", () => {
|
|
1383
|
-
|
|
1417
|
+
setupState();
|
|
1418
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue(null);
|
|
1384
1419
|
const ctx = createMockSwitchStatement({
|
|
1385
1420
|
cases: [
|
|
1386
1421
|
createMockSwitchCase([createMockCaseLabel({ intLiteral: "0" })]),
|
|
@@ -1388,11 +1423,14 @@ describe("TypeValidator", () => {
|
|
|
1388
1423
|
],
|
|
1389
1424
|
});
|
|
1390
1425
|
const expr = createMockExpression("x");
|
|
1391
|
-
expect(() =>
|
|
1426
|
+
expect(() =>
|
|
1427
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1428
|
+
).not.toThrow();
|
|
1392
1429
|
});
|
|
1393
1430
|
|
|
1394
1431
|
it("allows switch with 1 case + default", () => {
|
|
1395
|
-
|
|
1432
|
+
setupState();
|
|
1433
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue(null);
|
|
1396
1434
|
const ctx = createMockSwitchStatement({
|
|
1397
1435
|
cases: [
|
|
1398
1436
|
createMockSwitchCase([createMockCaseLabel({ intLiteral: "1" })]),
|
|
@@ -1400,11 +1438,14 @@ describe("TypeValidator", () => {
|
|
|
1400
1438
|
defaultCase: createMockDefaultCase(),
|
|
1401
1439
|
});
|
|
1402
1440
|
const expr = createMockExpression("x");
|
|
1403
|
-
expect(() =>
|
|
1441
|
+
expect(() =>
|
|
1442
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1443
|
+
).not.toThrow();
|
|
1404
1444
|
});
|
|
1405
1445
|
|
|
1406
1446
|
it("throws for duplicate case values", () => {
|
|
1407
|
-
|
|
1447
|
+
setupState();
|
|
1448
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue(null);
|
|
1408
1449
|
const ctx = createMockSwitchStatement({
|
|
1409
1450
|
cases: [
|
|
1410
1451
|
createMockSwitchCase([createMockCaseLabel({ intLiteral: "1" })]),
|
|
@@ -1412,13 +1453,14 @@ describe("TypeValidator", () => {
|
|
|
1412
1453
|
],
|
|
1413
1454
|
});
|
|
1414
1455
|
const expr = createMockExpression("x");
|
|
1415
|
-
expect(() =>
|
|
1456
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1416
1457
|
"Duplicate case value '1'",
|
|
1417
1458
|
);
|
|
1418
1459
|
});
|
|
1419
1460
|
|
|
1420
1461
|
it("throws for duplicate hex and integer case values (normalized)", () => {
|
|
1421
|
-
|
|
1462
|
+
setupState();
|
|
1463
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue(null);
|
|
1422
1464
|
const ctx = createMockSwitchStatement({
|
|
1423
1465
|
cases: [
|
|
1424
1466
|
createMockSwitchCase([createMockCaseLabel({ intLiteral: "255" })]),
|
|
@@ -1426,7 +1468,7 @@ describe("TypeValidator", () => {
|
|
|
1426
1468
|
],
|
|
1427
1469
|
});
|
|
1428
1470
|
const expr = createMockExpression("x");
|
|
1429
|
-
expect(() =>
|
|
1471
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1430
1472
|
"Duplicate case value '255'",
|
|
1431
1473
|
);
|
|
1432
1474
|
});
|
|
@@ -1447,9 +1489,8 @@ describe("TypeValidator", () => {
|
|
|
1447
1489
|
],
|
|
1448
1490
|
]),
|
|
1449
1491
|
});
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
);
|
|
1492
|
+
setupState({ symbols });
|
|
1493
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1453
1494
|
const ctx = createMockSwitchStatement({
|
|
1454
1495
|
cases: [
|
|
1455
1496
|
createMockSwitchCase([createMockCaseLabel({ identifier: "IDLE" })]),
|
|
@@ -1459,7 +1500,7 @@ describe("TypeValidator", () => {
|
|
|
1459
1500
|
],
|
|
1460
1501
|
});
|
|
1461
1502
|
const expr = createMockExpression("state");
|
|
1462
|
-
expect(() =>
|
|
1503
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1463
1504
|
"Non-exhaustive switch on State: covers 2 of 3 variants, missing 1",
|
|
1464
1505
|
);
|
|
1465
1506
|
});
|
|
@@ -1477,9 +1518,8 @@ describe("TypeValidator", () => {
|
|
|
1477
1518
|
],
|
|
1478
1519
|
]),
|
|
1479
1520
|
});
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
);
|
|
1521
|
+
setupState({ symbols });
|
|
1522
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1483
1523
|
const ctx = createMockSwitchStatement({
|
|
1484
1524
|
cases: [
|
|
1485
1525
|
createMockSwitchCase([createMockCaseLabel({ identifier: "A" })]),
|
|
@@ -1487,7 +1527,9 @@ describe("TypeValidator", () => {
|
|
|
1487
1527
|
],
|
|
1488
1528
|
});
|
|
1489
1529
|
const expr = createMockExpression("state");
|
|
1490
|
-
expect(() =>
|
|
1530
|
+
expect(() =>
|
|
1531
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1532
|
+
).not.toThrow();
|
|
1491
1533
|
});
|
|
1492
1534
|
|
|
1493
1535
|
it("allows non-exhaustive enum switch with plain default", () => {
|
|
@@ -1504,9 +1546,8 @@ describe("TypeValidator", () => {
|
|
|
1504
1546
|
],
|
|
1505
1547
|
]),
|
|
1506
1548
|
});
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
);
|
|
1549
|
+
setupState({ symbols });
|
|
1550
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1510
1551
|
const ctx = createMockSwitchStatement({
|
|
1511
1552
|
cases: [
|
|
1512
1553
|
createMockSwitchCase([createMockCaseLabel({ identifier: "A" })]),
|
|
@@ -1514,7 +1555,9 @@ describe("TypeValidator", () => {
|
|
|
1514
1555
|
defaultCase: createMockDefaultCase(),
|
|
1515
1556
|
});
|
|
1516
1557
|
const expr = createMockExpression("state");
|
|
1517
|
-
expect(() =>
|
|
1558
|
+
expect(() =>
|
|
1559
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1560
|
+
).not.toThrow();
|
|
1518
1561
|
});
|
|
1519
1562
|
|
|
1520
1563
|
it("validates default(n) count matches remaining variants", () => {
|
|
@@ -1531,9 +1574,8 @@ describe("TypeValidator", () => {
|
|
|
1531
1574
|
],
|
|
1532
1575
|
]),
|
|
1533
1576
|
});
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
);
|
|
1577
|
+
setupState({ symbols });
|
|
1578
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1537
1579
|
const ctx = createMockSwitchStatement({
|
|
1538
1580
|
cases: [
|
|
1539
1581
|
createMockSwitchCase([createMockCaseLabel({ identifier: "A" })]),
|
|
@@ -1541,7 +1583,9 @@ describe("TypeValidator", () => {
|
|
|
1541
1583
|
defaultCase: createMockDefaultCase("2"), // default(2) - correct!
|
|
1542
1584
|
});
|
|
1543
1585
|
const expr = createMockExpression("state");
|
|
1544
|
-
expect(() =>
|
|
1586
|
+
expect(() =>
|
|
1587
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1588
|
+
).not.toThrow();
|
|
1545
1589
|
});
|
|
1546
1590
|
|
|
1547
1591
|
it("throws when default(n) count is wrong", () => {
|
|
@@ -1558,9 +1602,8 @@ describe("TypeValidator", () => {
|
|
|
1558
1602
|
],
|
|
1559
1603
|
]),
|
|
1560
1604
|
});
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
);
|
|
1605
|
+
setupState({ symbols });
|
|
1606
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1564
1607
|
const ctx = createMockSwitchStatement({
|
|
1565
1608
|
cases: [
|
|
1566
1609
|
createMockSwitchCase([createMockCaseLabel({ identifier: "A" })]),
|
|
@@ -1568,7 +1611,7 @@ describe("TypeValidator", () => {
|
|
|
1568
1611
|
defaultCase: createMockDefaultCase("1"), // Wrong! Should be 2
|
|
1569
1612
|
});
|
|
1570
1613
|
const expr = createMockExpression("state");
|
|
1571
|
-
expect(() =>
|
|
1614
|
+
expect(() => TypeValidator.validateSwitchStatement(ctx, expr)).toThrow(
|
|
1572
1615
|
"switch covers 2 of 3 State variants (1 explicit + default(1)). Expected 3",
|
|
1573
1616
|
);
|
|
1574
1617
|
});
|
|
@@ -1587,9 +1630,8 @@ describe("TypeValidator", () => {
|
|
|
1587
1630
|
],
|
|
1588
1631
|
]),
|
|
1589
1632
|
});
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
);
|
|
1633
|
+
setupState({ symbols });
|
|
1634
|
+
vi.spyOn(TypeResolver, "getExpressionType").mockReturnValue("State");
|
|
1593
1635
|
// Case with 2 labels: A || B
|
|
1594
1636
|
const ctx = createMockSwitchStatement({
|
|
1595
1637
|
cases: [
|
|
@@ -1601,80 +1643,82 @@ describe("TypeValidator", () => {
|
|
|
1601
1643
|
],
|
|
1602
1644
|
});
|
|
1603
1645
|
const expr = createMockExpression("state");
|
|
1604
|
-
expect(() =>
|
|
1646
|
+
expect(() =>
|
|
1647
|
+
TypeValidator.validateSwitchStatement(ctx, expr),
|
|
1648
|
+
).not.toThrow();
|
|
1605
1649
|
});
|
|
1606
1650
|
});
|
|
1607
1651
|
|
|
1608
1652
|
describe("getDefaultCount", () => {
|
|
1609
1653
|
it("returns number for default(n)", () => {
|
|
1610
|
-
|
|
1654
|
+
setupState();
|
|
1611
1655
|
const ctx = createMockDefaultCase("5");
|
|
1612
|
-
expect(
|
|
1656
|
+
expect(TypeValidator.getDefaultCount(ctx)).toBe(5);
|
|
1613
1657
|
});
|
|
1614
1658
|
|
|
1615
1659
|
it("returns null for plain default", () => {
|
|
1616
|
-
|
|
1660
|
+
setupState();
|
|
1617
1661
|
const ctx = createMockDefaultCase();
|
|
1618
|
-
expect(
|
|
1662
|
+
expect(TypeValidator.getDefaultCount(ctx)).toBeNull();
|
|
1619
1663
|
});
|
|
1620
1664
|
});
|
|
1621
1665
|
|
|
1622
1666
|
describe("getCaseLabelValue", () => {
|
|
1623
1667
|
it("returns qualified type as dot-joined string", () => {
|
|
1624
|
-
|
|
1668
|
+
setupState();
|
|
1625
1669
|
const ctx = createMockCaseLabel({ qualifiedType: ["State", "IDLE"] });
|
|
1626
|
-
expect(
|
|
1670
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("State.IDLE");
|
|
1627
1671
|
});
|
|
1628
1672
|
|
|
1629
1673
|
it("returns identifier", () => {
|
|
1630
|
-
|
|
1674
|
+
setupState();
|
|
1631
1675
|
const ctx = createMockCaseLabel({ identifier: "MAX_VALUE" });
|
|
1632
|
-
expect(
|
|
1676
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("MAX_VALUE");
|
|
1633
1677
|
});
|
|
1634
1678
|
|
|
1635
1679
|
it("returns integer literal", () => {
|
|
1636
|
-
|
|
1680
|
+
setupState();
|
|
1637
1681
|
const ctx = createMockCaseLabel({ intLiteral: "42" });
|
|
1638
|
-
expect(
|
|
1682
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("42");
|
|
1639
1683
|
});
|
|
1640
1684
|
|
|
1641
1685
|
it("returns negative integer literal", () => {
|
|
1642
|
-
|
|
1686
|
+
setupState();
|
|
1643
1687
|
const ctx = createMockCaseLabel({ intLiteral: "5", hasNegative: true });
|
|
1644
|
-
expect(
|
|
1688
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("-5");
|
|
1645
1689
|
});
|
|
1646
1690
|
|
|
1647
1691
|
it("normalizes hex to decimal", () => {
|
|
1648
|
-
|
|
1692
|
+
setupState();
|
|
1649
1693
|
const ctx = createMockCaseLabel({ hexLiteral: "0xFF" });
|
|
1650
|
-
expect(
|
|
1694
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("255");
|
|
1651
1695
|
});
|
|
1652
1696
|
|
|
1653
1697
|
it("handles negative hex", () => {
|
|
1654
|
-
|
|
1698
|
+
setupState();
|
|
1655
1699
|
const ctx = createMockCaseLabel({
|
|
1656
1700
|
hexLiteral: "0x10",
|
|
1657
1701
|
hasNegative: true,
|
|
1658
1702
|
});
|
|
1659
|
-
expect(
|
|
1703
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("-16");
|
|
1660
1704
|
});
|
|
1661
1705
|
|
|
1662
1706
|
it("normalizes binary to decimal", () => {
|
|
1663
|
-
|
|
1707
|
+
setupState();
|
|
1664
1708
|
const ctx = createMockCaseLabel({ binaryLiteral: "0b1010" });
|
|
1665
|
-
expect(
|
|
1709
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("10");
|
|
1666
1710
|
});
|
|
1667
1711
|
|
|
1668
1712
|
it("returns char literal as-is", () => {
|
|
1669
|
-
|
|
1713
|
+
setupState();
|
|
1670
1714
|
const ctx = createMockCaseLabel({ charLiteral: "'A'" });
|
|
1671
|
-
expect(
|
|
1715
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("'A'");
|
|
1672
1716
|
});
|
|
1673
1717
|
|
|
1674
1718
|
it("returns empty string for unknown label type", () => {
|
|
1675
|
-
|
|
1719
|
+
setupState();
|
|
1676
1720
|
const ctx = createMockCaseLabel();
|
|
1677
|
-
expect(
|
|
1721
|
+
expect(TypeValidator.getCaseLabelValue(ctx)).toBe("");
|
|
1678
1722
|
});
|
|
1679
1723
|
});
|
|
1680
1724
|
|
|
@@ -1684,69 +1728,69 @@ describe("TypeValidator", () => {
|
|
|
1684
1728
|
|
|
1685
1729
|
describe("validateTernaryCondition", () => {
|
|
1686
1730
|
it("allows conditions with || operator", () => {
|
|
1687
|
-
|
|
1731
|
+
setupState();
|
|
1688
1732
|
const ctx = createMockOrExpression("a || b", { hasOr: true });
|
|
1689
|
-
expect(() =>
|
|
1733
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
|
|
1690
1734
|
});
|
|
1691
1735
|
|
|
1692
1736
|
it("allows conditions with && operator", () => {
|
|
1693
|
-
|
|
1737
|
+
setupState();
|
|
1694
1738
|
const ctx = createMockOrExpression("a && b", { hasAnd: true });
|
|
1695
|
-
expect(() =>
|
|
1739
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
|
|
1696
1740
|
});
|
|
1697
1741
|
|
|
1698
1742
|
it("allows conditions with = operator", () => {
|
|
1699
|
-
|
|
1743
|
+
setupState();
|
|
1700
1744
|
const ctx = createMockOrExpression("a = b", { hasEquality: true });
|
|
1701
|
-
expect(() =>
|
|
1745
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
|
|
1702
1746
|
});
|
|
1703
1747
|
|
|
1704
1748
|
it("allows conditions with relational operators", () => {
|
|
1705
|
-
|
|
1749
|
+
setupState();
|
|
1706
1750
|
const ctx = createMockOrExpression("a < b", { hasRelational: true });
|
|
1707
|
-
expect(() =>
|
|
1751
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
|
|
1708
1752
|
});
|
|
1709
1753
|
|
|
1710
1754
|
it("throws for bare value condition", () => {
|
|
1711
|
-
|
|
1755
|
+
setupState();
|
|
1712
1756
|
const ctx = createMockOrExpression("flag");
|
|
1713
|
-
expect(() =>
|
|
1757
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
|
|
1714
1758
|
"Ternary condition must be a boolean expression",
|
|
1715
1759
|
);
|
|
1716
1760
|
});
|
|
1717
1761
|
|
|
1718
1762
|
it("throws when no andExpression", () => {
|
|
1719
|
-
|
|
1763
|
+
setupState();
|
|
1720
1764
|
const ctx = {
|
|
1721
1765
|
getText: () => "bad",
|
|
1722
1766
|
andExpression: antlrArray([]),
|
|
1723
1767
|
} as unknown as Parser.OrExpressionContext;
|
|
1724
|
-
expect(() =>
|
|
1768
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
|
|
1725
1769
|
"Ternary condition must be a boolean expression",
|
|
1726
1770
|
);
|
|
1727
1771
|
});
|
|
1728
1772
|
|
|
1729
1773
|
it("throws when no equalityExpression", () => {
|
|
1730
|
-
|
|
1774
|
+
setupState();
|
|
1731
1775
|
const andExpr = { equalityExpression: antlrArray([]) };
|
|
1732
1776
|
const ctx = {
|
|
1733
1777
|
getText: () => "bad",
|
|
1734
1778
|
andExpression: antlrArray([andExpr]),
|
|
1735
1779
|
} as unknown as Parser.OrExpressionContext;
|
|
1736
|
-
expect(() =>
|
|
1780
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
|
|
1737
1781
|
"Ternary condition must be a boolean expression",
|
|
1738
1782
|
);
|
|
1739
1783
|
});
|
|
1740
1784
|
|
|
1741
1785
|
it("throws when no relationalExpression", () => {
|
|
1742
|
-
|
|
1786
|
+
setupState();
|
|
1743
1787
|
const equalityExpr = { relationalExpression: antlrArray([]) };
|
|
1744
1788
|
const andExpr = { equalityExpression: antlrArray([equalityExpr]) };
|
|
1745
1789
|
const ctx = {
|
|
1746
1790
|
getText: () => "bad",
|
|
1747
1791
|
andExpression: antlrArray([andExpr]),
|
|
1748
1792
|
} as unknown as Parser.OrExpressionContext;
|
|
1749
|
-
expect(() =>
|
|
1793
|
+
expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
|
|
1750
1794
|
"Ternary condition must be a boolean expression",
|
|
1751
1795
|
);
|
|
1752
1796
|
});
|
|
@@ -1754,18 +1798,18 @@ describe("TypeValidator", () => {
|
|
|
1754
1798
|
|
|
1755
1799
|
describe("validateNoNestedTernary", () => {
|
|
1756
1800
|
it("allows non-ternary expressions", () => {
|
|
1757
|
-
|
|
1801
|
+
setupState();
|
|
1758
1802
|
const ctx = createMockOrExpression("x + 1");
|
|
1759
1803
|
expect(() =>
|
|
1760
|
-
|
|
1804
|
+
TypeValidator.validateNoNestedTernary(ctx, "true branch"),
|
|
1761
1805
|
).not.toThrow();
|
|
1762
1806
|
});
|
|
1763
1807
|
|
|
1764
1808
|
it("throws for nested ternary", () => {
|
|
1765
|
-
|
|
1809
|
+
setupState();
|
|
1766
1810
|
const ctx = createMockOrExpression("a ? b : c");
|
|
1767
1811
|
expect(() =>
|
|
1768
|
-
|
|
1812
|
+
TypeValidator.validateNoNestedTernary(ctx, "true branch"),
|
|
1769
1813
|
).toThrow("Nested ternary not allowed in true branch");
|
|
1770
1814
|
});
|
|
1771
1815
|
});
|
|
@@ -1824,53 +1868,57 @@ describe("TypeValidator", () => {
|
|
|
1824
1868
|
}
|
|
1825
1869
|
|
|
1826
1870
|
it("allows conditions with comparison operators", () => {
|
|
1827
|
-
|
|
1871
|
+
setupState();
|
|
1828
1872
|
const ctx = createFullDoWhileExpression("x < 10", {
|
|
1829
1873
|
hasRelational: true,
|
|
1830
1874
|
});
|
|
1831
|
-
expect(() =>
|
|
1875
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1832
1876
|
});
|
|
1833
1877
|
|
|
1834
1878
|
it("allows conditions with equality operators", () => {
|
|
1835
|
-
|
|
1879
|
+
setupState();
|
|
1836
1880
|
const ctx = createFullDoWhileExpression("x = 0", { hasEquality: true });
|
|
1837
|
-
expect(() =>
|
|
1881
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1838
1882
|
});
|
|
1839
1883
|
|
|
1840
1884
|
it("allows conditions with && operator", () => {
|
|
1841
|
-
|
|
1885
|
+
setupState();
|
|
1842
1886
|
const ctx = createFullDoWhileExpression("a && b", { hasAnd: true });
|
|
1843
|
-
expect(() =>
|
|
1887
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1844
1888
|
});
|
|
1845
1889
|
|
|
1846
1890
|
it("allows conditions with || operator", () => {
|
|
1847
|
-
|
|
1891
|
+
setupState();
|
|
1848
1892
|
const ctx = createFullDoWhileExpression("a || b", { hasOr: true });
|
|
1849
|
-
expect(() =>
|
|
1893
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1850
1894
|
});
|
|
1851
1895
|
|
|
1852
1896
|
it("throws for bare value condition", () => {
|
|
1853
|
-
|
|
1897
|
+
setupState();
|
|
1854
1898
|
const ctx = createFullDoWhileExpression("count");
|
|
1855
|
-
expect(() =>
|
|
1856
|
-
|
|
1899
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
1900
|
+
"E0701",
|
|
1901
|
+
);
|
|
1902
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
1857
1903
|
"do-while condition must be a boolean expression",
|
|
1858
1904
|
);
|
|
1859
1905
|
});
|
|
1860
1906
|
|
|
1861
1907
|
it("throws for ternary in do-while condition", () => {
|
|
1862
|
-
|
|
1908
|
+
setupState();
|
|
1863
1909
|
const ctx = {
|
|
1864
1910
|
getText: () => "a ? b : c",
|
|
1865
1911
|
ternaryExpression: () => ({
|
|
1866
1912
|
orExpression: () => [{}, {}], // Multiple orExpressions = ternary
|
|
1867
1913
|
}),
|
|
1868
1914
|
} as unknown as Parser.ExpressionContext;
|
|
1869
|
-
expect(() =>
|
|
1915
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
1916
|
+
"E0701",
|
|
1917
|
+
);
|
|
1870
1918
|
});
|
|
1871
1919
|
|
|
1872
1920
|
it("allows boolean literals", () => {
|
|
1873
|
-
|
|
1921
|
+
setupState();
|
|
1874
1922
|
// Create full mock expression tree where getText returns "true"
|
|
1875
1923
|
const bitwiseOrExpr = {
|
|
1876
1924
|
bitwiseXorExpression: antlrArray([]),
|
|
@@ -1898,11 +1946,11 @@ describe("TypeValidator", () => {
|
|
|
1898
1946
|
orExpression: antlrArray([orExpr]),
|
|
1899
1947
|
}),
|
|
1900
1948
|
} as unknown as Parser.ExpressionContext;
|
|
1901
|
-
expect(() =>
|
|
1949
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1902
1950
|
});
|
|
1903
1951
|
|
|
1904
1952
|
it("allows negation expressions", () => {
|
|
1905
|
-
|
|
1953
|
+
setupState();
|
|
1906
1954
|
const bitwiseOrExpr = {
|
|
1907
1955
|
bitwiseXorExpression: antlrArray([]),
|
|
1908
1956
|
getText: () => "!flag",
|
|
@@ -1929,7 +1977,7 @@ describe("TypeValidator", () => {
|
|
|
1929
1977
|
orExpression: antlrArray([orExpr]),
|
|
1930
1978
|
}),
|
|
1931
1979
|
} as unknown as Parser.ExpressionContext;
|
|
1932
|
-
expect(() =>
|
|
1980
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1933
1981
|
});
|
|
1934
1982
|
|
|
1935
1983
|
it("allows bool type variables", () => {
|
|
@@ -1939,7 +1987,7 @@ describe("TypeValidator", () => {
|
|
|
1939
1987
|
{ baseType: "bool", bitWidth: 8, isArray: false, isConst: false },
|
|
1940
1988
|
],
|
|
1941
1989
|
]);
|
|
1942
|
-
|
|
1990
|
+
setupState({ typeRegistry });
|
|
1943
1991
|
const bitwiseOrExpr = {
|
|
1944
1992
|
bitwiseXorExpression: antlrArray([]),
|
|
1945
1993
|
getText: () => "isReady",
|
|
@@ -1966,19 +2014,19 @@ describe("TypeValidator", () => {
|
|
|
1966
2014
|
orExpression: antlrArray([orExpr]),
|
|
1967
2015
|
}),
|
|
1968
2016
|
} as unknown as Parser.ExpressionContext;
|
|
1969
|
-
expect(() =>
|
|
2017
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
|
|
1970
2018
|
});
|
|
1971
2019
|
|
|
1972
2020
|
it("shows help message in error", () => {
|
|
1973
|
-
|
|
2021
|
+
setupState();
|
|
1974
2022
|
const ctx = createFullDoWhileExpression("count");
|
|
1975
|
-
expect(() =>
|
|
2023
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
1976
2024
|
"help: use explicit comparison: count > 0 or count != 0",
|
|
1977
2025
|
);
|
|
1978
2026
|
});
|
|
1979
2027
|
|
|
1980
2028
|
it("throws when no andExpression", () => {
|
|
1981
|
-
|
|
2029
|
+
setupState();
|
|
1982
2030
|
const orExpr = {
|
|
1983
2031
|
getText: () => "bad",
|
|
1984
2032
|
andExpression: antlrArray([]),
|
|
@@ -1989,11 +2037,13 @@ describe("TypeValidator", () => {
|
|
|
1989
2037
|
orExpression: antlrArray([orExpr]),
|
|
1990
2038
|
}),
|
|
1991
2039
|
} as unknown as Parser.ExpressionContext;
|
|
1992
|
-
expect(() =>
|
|
2040
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
2041
|
+
"E0701",
|
|
2042
|
+
);
|
|
1993
2043
|
});
|
|
1994
2044
|
|
|
1995
2045
|
it("throws when no equalityExpression", () => {
|
|
1996
|
-
|
|
2046
|
+
setupState();
|
|
1997
2047
|
const andExpr = {
|
|
1998
2048
|
equalityExpression: antlrArray([]),
|
|
1999
2049
|
};
|
|
@@ -2007,11 +2057,13 @@ describe("TypeValidator", () => {
|
|
|
2007
2057
|
orExpression: antlrArray([orExpr]),
|
|
2008
2058
|
}),
|
|
2009
2059
|
} as unknown as Parser.ExpressionContext;
|
|
2010
|
-
expect(() =>
|
|
2060
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
2061
|
+
"E0701",
|
|
2062
|
+
);
|
|
2011
2063
|
});
|
|
2012
2064
|
|
|
2013
2065
|
it("throws when no relationalExpression", () => {
|
|
2014
|
-
|
|
2066
|
+
setupState();
|
|
2015
2067
|
const equalityExpr = {
|
|
2016
2068
|
relationalExpression: antlrArray([]),
|
|
2017
2069
|
};
|
|
@@ -2028,7 +2080,9 @@ describe("TypeValidator", () => {
|
|
|
2028
2080
|
orExpression: antlrArray([orExpr]),
|
|
2029
2081
|
}),
|
|
2030
2082
|
} as unknown as Parser.ExpressionContext;
|
|
2031
|
-
expect(() =>
|
|
2083
|
+
expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
|
|
2084
|
+
"E0701",
|
|
2085
|
+
);
|
|
2032
2086
|
});
|
|
2033
2087
|
});
|
|
2034
2088
|
|
|
@@ -2072,35 +2126,35 @@ describe("TypeValidator", () => {
|
|
|
2072
2126
|
}
|
|
2073
2127
|
|
|
2074
2128
|
it("allows conditions without function calls", () => {
|
|
2075
|
-
|
|
2129
|
+
setupState();
|
|
2076
2130
|
const ctx = createExpressionWithFunctionCall("x > 0", false);
|
|
2077
2131
|
expect(() =>
|
|
2078
|
-
|
|
2132
|
+
TypeValidator.validateConditionNoFunctionCall(ctx, "if"),
|
|
2079
2133
|
).not.toThrow();
|
|
2080
2134
|
});
|
|
2081
2135
|
|
|
2082
2136
|
it("throws for conditions with function calls", () => {
|
|
2083
|
-
|
|
2137
|
+
setupState();
|
|
2084
2138
|
const ctx = createExpressionWithFunctionCall("isReady()", true);
|
|
2085
2139
|
expect(() =>
|
|
2086
|
-
|
|
2140
|
+
TypeValidator.validateConditionNoFunctionCall(ctx, "if"),
|
|
2087
2141
|
).toThrow("E0702");
|
|
2088
2142
|
expect(() =>
|
|
2089
|
-
|
|
2143
|
+
TypeValidator.validateConditionNoFunctionCall(ctx, "if"),
|
|
2090
2144
|
).toThrow("Function call in 'if' condition is not allowed");
|
|
2091
2145
|
expect(() =>
|
|
2092
|
-
|
|
2146
|
+
TypeValidator.validateConditionNoFunctionCall(ctx, "if"),
|
|
2093
2147
|
).toThrow("MISRA C:2012 Rule 13.5");
|
|
2094
2148
|
});
|
|
2095
2149
|
|
|
2096
2150
|
it("returns when ternaryExpression is null", () => {
|
|
2097
|
-
|
|
2151
|
+
setupState();
|
|
2098
2152
|
const ctx = {
|
|
2099
2153
|
getText: () => "x",
|
|
2100
2154
|
ternaryExpression: () => null,
|
|
2101
2155
|
} as unknown as Parser.ExpressionContext;
|
|
2102
2156
|
expect(() =>
|
|
2103
|
-
|
|
2157
|
+
TypeValidator.validateConditionNoFunctionCall(ctx, "if"),
|
|
2104
2158
|
).not.toThrow();
|
|
2105
2159
|
});
|
|
2106
2160
|
});
|
|
@@ -2138,28 +2192,28 @@ describe("TypeValidator", () => {
|
|
|
2138
2192
|
}
|
|
2139
2193
|
|
|
2140
2194
|
it("allows ternary conditions without function calls", () => {
|
|
2141
|
-
|
|
2195
|
+
setupState();
|
|
2142
2196
|
const ctx = createOrExprWithFunctionCall("x > 0", false);
|
|
2143
2197
|
expect(() =>
|
|
2144
|
-
|
|
2198
|
+
TypeValidator.validateTernaryConditionNoFunctionCall(ctx),
|
|
2145
2199
|
).not.toThrow();
|
|
2146
2200
|
});
|
|
2147
2201
|
|
|
2148
2202
|
it("throws for ternary conditions with function calls", () => {
|
|
2149
|
-
|
|
2203
|
+
setupState();
|
|
2150
2204
|
const ctx = createOrExprWithFunctionCall("check()", true);
|
|
2151
2205
|
expect(() =>
|
|
2152
|
-
|
|
2206
|
+
TypeValidator.validateTernaryConditionNoFunctionCall(ctx),
|
|
2153
2207
|
).toThrow("E0702");
|
|
2154
2208
|
expect(() =>
|
|
2155
|
-
|
|
2209
|
+
TypeValidator.validateTernaryConditionNoFunctionCall(ctx),
|
|
2156
2210
|
).toThrow("ternary");
|
|
2157
2211
|
});
|
|
2158
2212
|
});
|
|
2159
2213
|
|
|
2160
2214
|
describe("hasPostfixFunctionCallInUnary - nested unary", () => {
|
|
2161
2215
|
it("handles nested unary operators (Issue #366)", () => {
|
|
2162
|
-
|
|
2216
|
+
setupState();
|
|
2163
2217
|
// Create nested unary: !!isReady()
|
|
2164
2218
|
const innerPostfixOp = [
|
|
2165
2219
|
{ argumentList: () => ({}), getText: () => "()" },
|
|
@@ -2196,7 +2250,7 @@ describe("TypeValidator", () => {
|
|
|
2196
2250
|
} as unknown as Parser.OrExpressionContext;
|
|
2197
2251
|
|
|
2198
2252
|
expect(() =>
|
|
2199
|
-
|
|
2253
|
+
TypeValidator.validateTernaryConditionNoFunctionCall(ctx),
|
|
2200
2254
|
).toThrow("E0702");
|
|
2201
2255
|
});
|
|
2202
2256
|
});
|
|
@@ -2245,34 +2299,34 @@ describe("TypeValidator", () => {
|
|
|
2245
2299
|
}
|
|
2246
2300
|
|
|
2247
2301
|
it("allows valid shift amounts", () => {
|
|
2248
|
-
|
|
2302
|
+
setupState();
|
|
2249
2303
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2250
2304
|
"u8",
|
|
2251
2305
|
"7",
|
|
2252
2306
|
"<<",
|
|
2253
2307
|
);
|
|
2254
2308
|
expect(() =>
|
|
2255
|
-
|
|
2309
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2256
2310
|
).not.toThrow();
|
|
2257
2311
|
});
|
|
2258
2312
|
|
|
2259
2313
|
it("throws for shift amount >= type width", () => {
|
|
2260
|
-
|
|
2314
|
+
setupState();
|
|
2261
2315
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2262
2316
|
"u8",
|
|
2263
2317
|
"8",
|
|
2264
2318
|
"<<",
|
|
2265
2319
|
);
|
|
2266
2320
|
expect(() =>
|
|
2267
|
-
|
|
2321
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2268
2322
|
).toThrow("Shift amount (8) exceeds type width (8 bits)");
|
|
2269
2323
|
expect(() =>
|
|
2270
|
-
|
|
2324
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2271
2325
|
).toThrow("MISRA C:2012 Rule 12.2");
|
|
2272
2326
|
});
|
|
2273
2327
|
|
|
2274
2328
|
it("throws for negative shift amounts", () => {
|
|
2275
|
-
|
|
2329
|
+
setupState();
|
|
2276
2330
|
// Create expression with negative value
|
|
2277
2331
|
const literal = { getText: () => "5" };
|
|
2278
2332
|
const primaryExpr = { literal: () => literal };
|
|
@@ -2294,17 +2348,17 @@ describe("TypeValidator", () => {
|
|
|
2294
2348
|
} as unknown as Parser.ShiftExpressionContext;
|
|
2295
2349
|
|
|
2296
2350
|
expect(() =>
|
|
2297
|
-
|
|
2351
|
+
TypeValidator.validateShiftAmount("u32", addExpr, "<<", ctx),
|
|
2298
2352
|
).toThrow("Negative shift amount (-5) is undefined behavior");
|
|
2299
2353
|
});
|
|
2300
2354
|
|
|
2301
2355
|
it("validates different type widths", () => {
|
|
2302
|
-
|
|
2356
|
+
setupState();
|
|
2303
2357
|
|
|
2304
2358
|
// u16 - max shift is 15
|
|
2305
2359
|
const shift15 = createShiftExpression("u16", "15", ">>");
|
|
2306
2360
|
expect(() =>
|
|
2307
|
-
|
|
2361
|
+
TypeValidator.validateShiftAmount(
|
|
2308
2362
|
shift15.leftType,
|
|
2309
2363
|
shift15.rightExpr,
|
|
2310
2364
|
shift15.op,
|
|
@@ -2314,7 +2368,7 @@ describe("TypeValidator", () => {
|
|
|
2314
2368
|
|
|
2315
2369
|
const shift16 = createShiftExpression("u16", "16", ">>");
|
|
2316
2370
|
expect(() =>
|
|
2317
|
-
|
|
2371
|
+
TypeValidator.validateShiftAmount(
|
|
2318
2372
|
shift16.leftType,
|
|
2319
2373
|
shift16.rightExpr,
|
|
2320
2374
|
shift16.op,
|
|
@@ -2325,7 +2379,7 @@ describe("TypeValidator", () => {
|
|
|
2325
2379
|
// u32 - max shift is 31
|
|
2326
2380
|
const shift31 = createShiftExpression("u32", "31", "<<");
|
|
2327
2381
|
expect(() =>
|
|
2328
|
-
|
|
2382
|
+
TypeValidator.validateShiftAmount(
|
|
2329
2383
|
shift31.leftType,
|
|
2330
2384
|
shift31.rightExpr,
|
|
2331
2385
|
shift31.op,
|
|
@@ -2335,7 +2389,7 @@ describe("TypeValidator", () => {
|
|
|
2335
2389
|
|
|
2336
2390
|
const shift32 = createShiftExpression("u32", "32", "<<");
|
|
2337
2391
|
expect(() =>
|
|
2338
|
-
|
|
2392
|
+
TypeValidator.validateShiftAmount(
|
|
2339
2393
|
shift32.leftType,
|
|
2340
2394
|
shift32.rightExpr,
|
|
2341
2395
|
shift32.op,
|
|
@@ -2346,7 +2400,7 @@ describe("TypeValidator", () => {
|
|
|
2346
2400
|
// u64 - max shift is 63
|
|
2347
2401
|
const shift63 = createShiftExpression("u64", "63", "<<");
|
|
2348
2402
|
expect(() =>
|
|
2349
|
-
|
|
2403
|
+
TypeValidator.validateShiftAmount(
|
|
2350
2404
|
shift63.leftType,
|
|
2351
2405
|
shift63.rightExpr,
|
|
2352
2406
|
shift63.op,
|
|
@@ -2356,7 +2410,7 @@ describe("TypeValidator", () => {
|
|
|
2356
2410
|
|
|
2357
2411
|
const shift64 = createShiftExpression("u64", "64", "<<");
|
|
2358
2412
|
expect(() =>
|
|
2359
|
-
|
|
2413
|
+
TypeValidator.validateShiftAmount(
|
|
2360
2414
|
shift64.leftType,
|
|
2361
2415
|
shift64.rightExpr,
|
|
2362
2416
|
shift64.op,
|
|
@@ -2366,31 +2420,31 @@ describe("TypeValidator", () => {
|
|
|
2366
2420
|
});
|
|
2367
2421
|
|
|
2368
2422
|
it("handles signed types", () => {
|
|
2369
|
-
|
|
2423
|
+
setupState();
|
|
2370
2424
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2371
2425
|
"i8",
|
|
2372
2426
|
"8",
|
|
2373
2427
|
"<<",
|
|
2374
2428
|
);
|
|
2375
2429
|
expect(() =>
|
|
2376
|
-
|
|
2430
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2377
2431
|
).toThrow("Shift amount (8) exceeds type width (8 bits)");
|
|
2378
2432
|
});
|
|
2379
2433
|
|
|
2380
2434
|
it("skips validation for unknown types", () => {
|
|
2381
|
-
|
|
2435
|
+
setupState();
|
|
2382
2436
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2383
2437
|
"CustomType",
|
|
2384
2438
|
"100",
|
|
2385
2439
|
"<<",
|
|
2386
2440
|
);
|
|
2387
2441
|
expect(() =>
|
|
2388
|
-
|
|
2442
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2389
2443
|
).not.toThrow();
|
|
2390
2444
|
});
|
|
2391
2445
|
|
|
2392
2446
|
it("skips validation for non-constant shift amounts", () => {
|
|
2393
|
-
|
|
2447
|
+
setupState();
|
|
2394
2448
|
// Create expression without literal
|
|
2395
2449
|
const primaryExpr = { literal: () => null };
|
|
2396
2450
|
const postfixExpr = {
|
|
@@ -2411,36 +2465,36 @@ describe("TypeValidator", () => {
|
|
|
2411
2465
|
} as unknown as Parser.ShiftExpressionContext;
|
|
2412
2466
|
|
|
2413
2467
|
expect(() =>
|
|
2414
|
-
|
|
2468
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2415
2469
|
).not.toThrow();
|
|
2416
2470
|
});
|
|
2417
2471
|
|
|
2418
2472
|
it("handles hex shift amounts", () => {
|
|
2419
|
-
|
|
2473
|
+
setupState();
|
|
2420
2474
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2421
2475
|
"u8",
|
|
2422
2476
|
"0x08",
|
|
2423
2477
|
"<<",
|
|
2424
2478
|
);
|
|
2425
2479
|
expect(() =>
|
|
2426
|
-
|
|
2480
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2427
2481
|
).toThrow("Shift amount (8) exceeds type width (8 bits)");
|
|
2428
2482
|
});
|
|
2429
2483
|
|
|
2430
2484
|
it("handles binary shift amounts", () => {
|
|
2431
|
-
|
|
2485
|
+
setupState();
|
|
2432
2486
|
const { leftType, rightExpr, op, ctx } = createShiftExpression(
|
|
2433
2487
|
"u8",
|
|
2434
2488
|
"0b1000",
|
|
2435
2489
|
"<<",
|
|
2436
2490
|
);
|
|
2437
2491
|
expect(() =>
|
|
2438
|
-
|
|
2492
|
+
TypeValidator.validateShiftAmount(leftType, rightExpr, op, ctx),
|
|
2439
2493
|
).toThrow("Shift amount (8) exceeds type width (8 bits)");
|
|
2440
2494
|
});
|
|
2441
2495
|
|
|
2442
2496
|
it("handles complex multiplicative expressions gracefully", () => {
|
|
2443
|
-
|
|
2497
|
+
setupState();
|
|
2444
2498
|
// Create expression with multiple multiplicative terms
|
|
2445
2499
|
const multExpr1 = { unaryExpression: () => [] };
|
|
2446
2500
|
const multExpr2 = { unaryExpression: () => [] };
|
|
@@ -2452,12 +2506,12 @@ describe("TypeValidator", () => {
|
|
|
2452
2506
|
} as unknown as Parser.ShiftExpressionContext;
|
|
2453
2507
|
|
|
2454
2508
|
expect(() =>
|
|
2455
|
-
|
|
2509
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2456
2510
|
).not.toThrow();
|
|
2457
2511
|
});
|
|
2458
2512
|
|
|
2459
2513
|
it("handles nested unary expressions", () => {
|
|
2460
|
-
|
|
2514
|
+
setupState();
|
|
2461
2515
|
// Create nested unary: -(-5) = 5
|
|
2462
2516
|
const innerLiteral = { getText: () => "5" };
|
|
2463
2517
|
const innerPrimaryExpr = { literal: () => innerLiteral };
|
|
@@ -2490,12 +2544,12 @@ describe("TypeValidator", () => {
|
|
|
2490
2544
|
|
|
2491
2545
|
// This should evaluate to 5, which is valid for u8
|
|
2492
2546
|
expect(() =>
|
|
2493
|
-
|
|
2547
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2494
2548
|
).not.toThrow();
|
|
2495
2549
|
});
|
|
2496
2550
|
|
|
2497
2551
|
it("handles multiple unary expressions gracefully", () => {
|
|
2498
|
-
|
|
2552
|
+
setupState();
|
|
2499
2553
|
const unaryExpr1 = {
|
|
2500
2554
|
postfixExpression: () => null,
|
|
2501
2555
|
unaryExpression: () => null,
|
|
@@ -2515,12 +2569,12 @@ describe("TypeValidator", () => {
|
|
|
2515
2569
|
} as unknown as Parser.ShiftExpressionContext;
|
|
2516
2570
|
|
|
2517
2571
|
expect(() =>
|
|
2518
|
-
|
|
2572
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2519
2573
|
).not.toThrow();
|
|
2520
2574
|
});
|
|
2521
2575
|
|
|
2522
2576
|
it("handles binary literal in nested unary (coverage for evaluateUnaryExpression)", () => {
|
|
2523
|
-
|
|
2577
|
+
setupState();
|
|
2524
2578
|
// Create nested unary with binary literal: -0b100 = -4
|
|
2525
2579
|
const innerLiteral = { getText: () => "0b100" }; // binary for 4
|
|
2526
2580
|
const innerPrimaryExpr = { literal: () => innerLiteral };
|
|
@@ -2548,12 +2602,12 @@ describe("TypeValidator", () => {
|
|
|
2548
2602
|
|
|
2549
2603
|
// -4 is negative, should throw
|
|
2550
2604
|
expect(() =>
|
|
2551
|
-
|
|
2605
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2552
2606
|
).toThrow("Negative shift amount (-4) is undefined behavior");
|
|
2553
2607
|
});
|
|
2554
2608
|
|
|
2555
2609
|
it("handles nested unary returning null in evaluateUnaryExpression", () => {
|
|
2556
|
-
|
|
2610
|
+
setupState();
|
|
2557
2611
|
// Create nested unary that returns null (no literal)
|
|
2558
2612
|
const innerPrimaryExpr = { literal: () => null };
|
|
2559
2613
|
const innerPostfixExpr = {
|
|
@@ -2580,12 +2634,12 @@ describe("TypeValidator", () => {
|
|
|
2580
2634
|
|
|
2581
2635
|
// Cannot evaluate, should skip validation
|
|
2582
2636
|
expect(() =>
|
|
2583
|
-
|
|
2637
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2584
2638
|
).not.toThrow();
|
|
2585
2639
|
});
|
|
2586
2640
|
|
|
2587
2641
|
it("handles fallthrough case in evaluateUnaryExpression when nothing returns value", () => {
|
|
2588
|
-
|
|
2642
|
+
setupState();
|
|
2589
2643
|
// Create unary expression that has neither postfix nor nested unary
|
|
2590
2644
|
const unaryExpr = {
|
|
2591
2645
|
postfixExpression: () => null,
|
|
@@ -2602,12 +2656,12 @@ describe("TypeValidator", () => {
|
|
|
2602
2656
|
|
|
2603
2657
|
// Cannot evaluate, should skip validation
|
|
2604
2658
|
expect(() =>
|
|
2605
|
-
|
|
2659
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2606
2660
|
).not.toThrow();
|
|
2607
2661
|
});
|
|
2608
2662
|
|
|
2609
2663
|
it("handles hex literal in evaluateUnaryExpression via nested unary path", () => {
|
|
2610
|
-
|
|
2664
|
+
setupState();
|
|
2611
2665
|
// Create nested unary with hex literal: -0x08 = -8
|
|
2612
2666
|
const innerLiteral = { getText: () => "0x08" }; // hex for 8
|
|
2613
2667
|
const innerPrimaryExpr = { literal: () => innerLiteral };
|
|
@@ -2635,12 +2689,12 @@ describe("TypeValidator", () => {
|
|
|
2635
2689
|
|
|
2636
2690
|
// -8 is negative, should throw
|
|
2637
2691
|
expect(() =>
|
|
2638
|
-
|
|
2692
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2639
2693
|
).toThrow("Negative shift amount (-8) is undefined behavior");
|
|
2640
2694
|
});
|
|
2641
2695
|
|
|
2642
2696
|
it("handles negative value in evaluateUnaryExpression when inner has negative getText", () => {
|
|
2643
|
-
|
|
2697
|
+
setupState();
|
|
2644
2698
|
// Create unary with postfixExpr where getText starts with "-"
|
|
2645
2699
|
const innerLiteral = { getText: () => "5" };
|
|
2646
2700
|
const innerPrimaryExpr = { literal: () => innerLiteral };
|
|
@@ -2670,12 +2724,12 @@ describe("TypeValidator", () => {
|
|
|
2670
2724
|
|
|
2671
2725
|
// --5 should evaluate to 5 (double negative)
|
|
2672
2726
|
expect(() =>
|
|
2673
|
-
|
|
2727
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2674
2728
|
).not.toThrow();
|
|
2675
2729
|
});
|
|
2676
2730
|
|
|
2677
2731
|
it("handles case where numMatch fails in evaluateUnaryExpression", () => {
|
|
2678
|
-
|
|
2732
|
+
setupState();
|
|
2679
2733
|
// Create unary with a literal that doesn't match any pattern
|
|
2680
2734
|
const innerLiteral = { getText: () => "abc" }; // Not a number
|
|
2681
2735
|
const innerPrimaryExpr = { literal: () => innerLiteral };
|
|
@@ -2703,12 +2757,12 @@ describe("TypeValidator", () => {
|
|
|
2703
2757
|
|
|
2704
2758
|
// Cannot evaluate, should skip validation (value stays null)
|
|
2705
2759
|
expect(() =>
|
|
2706
|
-
|
|
2760
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2707
2761
|
).not.toThrow();
|
|
2708
2762
|
});
|
|
2709
2763
|
|
|
2710
2764
|
it("hits line 1168 in evaluateUnaryExpression (double-nested null return)", () => {
|
|
2711
|
-
|
|
2765
|
+
setupState();
|
|
2712
2766
|
// Create deeply nested unary where inner has no postfix and no nested
|
|
2713
2767
|
// This forces evaluateUnaryExpression to reach the final return null
|
|
2714
2768
|
const innerInnerUnaryExpr = {
|
|
@@ -2736,7 +2790,7 @@ describe("TypeValidator", () => {
|
|
|
2736
2790
|
|
|
2737
2791
|
// Cannot evaluate, should skip validation
|
|
2738
2792
|
expect(() =>
|
|
2739
|
-
|
|
2793
|
+
TypeValidator.validateShiftAmount("u8", addExpr, "<<", ctx),
|
|
2740
2794
|
).not.toThrow();
|
|
2741
2795
|
});
|
|
2742
2796
|
});
|
|
@@ -2748,10 +2802,8 @@ describe("TypeValidator", () => {
|
|
|
2748
2802
|
describe("resolveBareIdentifier - outside scope coverage", () => {
|
|
2749
2803
|
it("returns null for enum identifier when outside scope", () => {
|
|
2750
2804
|
const symbols = createMockSymbols({ knownEnums: new Set(["State"]) });
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
);
|
|
2754
|
-
const result = validator.resolveBareIdentifier(
|
|
2805
|
+
setupState({ symbols, currentScope: null });
|
|
2806
|
+
const result = TypeValidator.resolveBareIdentifier(
|
|
2755
2807
|
"State",
|
|
2756
2808
|
false,
|
|
2757
2809
|
() => false,
|
|
@@ -2760,10 +2812,8 @@ describe("TypeValidator", () => {
|
|
|
2760
2812
|
});
|
|
2761
2813
|
|
|
2762
2814
|
it("returns null for struct identifier when outside scope", () => {
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
);
|
|
2766
|
-
const result = validator.resolveBareIdentifier(
|
|
2815
|
+
setupState({ currentScope: null });
|
|
2816
|
+
const result = TypeValidator.resolveBareIdentifier(
|
|
2767
2817
|
"Point",
|
|
2768
2818
|
false,
|
|
2769
2819
|
() => true,
|
|
@@ -2773,10 +2823,8 @@ describe("TypeValidator", () => {
|
|
|
2773
2823
|
|
|
2774
2824
|
it("returns null for register identifier when outside scope", () => {
|
|
2775
2825
|
const symbols = createMockSymbols({ knownRegisters: new Set(["GPIO"]) });
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
);
|
|
2779
|
-
const result = validator.resolveBareIdentifier(
|
|
2826
|
+
setupState({ symbols, currentScope: null });
|
|
2827
|
+
const result = TypeValidator.resolveBareIdentifier(
|
|
2780
2828
|
"GPIO",
|
|
2781
2829
|
false,
|
|
2782
2830
|
() => false,
|
|
@@ -2790,114 +2838,90 @@ describe("TypeValidator", () => {
|
|
|
2790
2838
|
// ========================================================================
|
|
2791
2839
|
|
|
2792
2840
|
describe("validateIntegerAssignment", () => {
|
|
2793
|
-
function createValidatorWithMockResolver(mockResolver: {
|
|
2794
|
-
isIntegerType?: (type: string) => boolean;
|
|
2795
|
-
validateLiteralFitsType?: (literal: string, type: string) => void;
|
|
2796
|
-
validateTypeConversion?: (target: string, source: string | null) => void;
|
|
2797
|
-
}) {
|
|
2798
|
-
const deps = createMockDeps();
|
|
2799
|
-
const fullMockResolver = {
|
|
2800
|
-
isIntegerType: mockResolver.isIntegerType ?? (() => true),
|
|
2801
|
-
validateLiteralFitsType:
|
|
2802
|
-
mockResolver.validateLiteralFitsType ?? vi.fn(),
|
|
2803
|
-
validateTypeConversion: mockResolver.validateTypeConversion ?? vi.fn(),
|
|
2804
|
-
};
|
|
2805
|
-
return new TypeValidator({
|
|
2806
|
-
...deps,
|
|
2807
|
-
typeResolver: fullMockResolver as never,
|
|
2808
|
-
});
|
|
2809
|
-
}
|
|
2810
|
-
|
|
2811
2841
|
it("skips validation for compound assignments", () => {
|
|
2812
|
-
|
|
2813
|
-
const
|
|
2814
|
-
const
|
|
2815
|
-
validateLiteralFitsType,
|
|
2816
|
-
validateTypeConversion,
|
|
2817
|
-
});
|
|
2842
|
+
setupState();
|
|
2843
|
+
const literalSpy = vi.spyOn(TypeResolver, "validateLiteralFitsType");
|
|
2844
|
+
const conversionSpy = vi.spyOn(TypeResolver, "validateTypeConversion");
|
|
2818
2845
|
|
|
2819
|
-
|
|
2846
|
+
TypeValidator.validateIntegerAssignment("u8", "10", null, true);
|
|
2820
2847
|
|
|
2821
|
-
expect(
|
|
2822
|
-
expect(
|
|
2848
|
+
expect(literalSpy).not.toHaveBeenCalled();
|
|
2849
|
+
expect(conversionSpy).not.toHaveBeenCalled();
|
|
2823
2850
|
});
|
|
2824
2851
|
|
|
2825
2852
|
it("skips validation for non-integer types", () => {
|
|
2826
|
-
|
|
2827
|
-
const
|
|
2828
|
-
isIntegerType: () => false,
|
|
2829
|
-
validateLiteralFitsType,
|
|
2830
|
-
});
|
|
2853
|
+
setupState();
|
|
2854
|
+
const spy = vi.spyOn(TypeResolver, "validateLiteralFitsType");
|
|
2831
2855
|
|
|
2832
|
-
|
|
2856
|
+
TypeValidator.validateIntegerAssignment("f32", "10", null, false);
|
|
2833
2857
|
|
|
2834
|
-
expect(
|
|
2858
|
+
expect(spy).not.toHaveBeenCalled();
|
|
2835
2859
|
});
|
|
2836
2860
|
|
|
2837
2861
|
it("validates decimal literal fits in target type", () => {
|
|
2838
|
-
|
|
2839
|
-
const
|
|
2840
|
-
validateLiteralFitsType
|
|
2841
|
-
|
|
2862
|
+
setupState();
|
|
2863
|
+
const spy = vi
|
|
2864
|
+
.spyOn(TypeResolver, "validateLiteralFitsType")
|
|
2865
|
+
.mockImplementation(() => {});
|
|
2842
2866
|
|
|
2843
|
-
|
|
2867
|
+
TypeValidator.validateIntegerAssignment("u8", "100", null, false);
|
|
2844
2868
|
|
|
2845
|
-
expect(
|
|
2869
|
+
expect(spy).toHaveBeenCalledWith("100", "u8");
|
|
2846
2870
|
});
|
|
2847
2871
|
|
|
2848
2872
|
it("validates negative decimal literal fits in target type", () => {
|
|
2849
|
-
|
|
2850
|
-
const
|
|
2851
|
-
validateLiteralFitsType
|
|
2852
|
-
|
|
2873
|
+
setupState();
|
|
2874
|
+
const spy = vi
|
|
2875
|
+
.spyOn(TypeResolver, "validateLiteralFitsType")
|
|
2876
|
+
.mockImplementation(() => {});
|
|
2853
2877
|
|
|
2854
|
-
|
|
2878
|
+
TypeValidator.validateIntegerAssignment("i8", "-50", null, false);
|
|
2855
2879
|
|
|
2856
|
-
expect(
|
|
2880
|
+
expect(spy).toHaveBeenCalledWith("-50", "i8");
|
|
2857
2881
|
});
|
|
2858
2882
|
|
|
2859
2883
|
it("validates hex literal fits in target type", () => {
|
|
2860
|
-
|
|
2861
|
-
const
|
|
2862
|
-
validateLiteralFitsType
|
|
2863
|
-
|
|
2884
|
+
setupState();
|
|
2885
|
+
const spy = vi
|
|
2886
|
+
.spyOn(TypeResolver, "validateLiteralFitsType")
|
|
2887
|
+
.mockImplementation(() => {});
|
|
2864
2888
|
|
|
2865
|
-
|
|
2889
|
+
TypeValidator.validateIntegerAssignment("u8", "0xFF", null, false);
|
|
2866
2890
|
|
|
2867
|
-
expect(
|
|
2891
|
+
expect(spy).toHaveBeenCalledWith("0xFF", "u8");
|
|
2868
2892
|
});
|
|
2869
2893
|
|
|
2870
2894
|
it("validates binary literal fits in target type", () => {
|
|
2871
|
-
|
|
2872
|
-
const
|
|
2873
|
-
validateLiteralFitsType
|
|
2874
|
-
|
|
2895
|
+
setupState();
|
|
2896
|
+
const spy = vi
|
|
2897
|
+
.spyOn(TypeResolver, "validateLiteralFitsType")
|
|
2898
|
+
.mockImplementation(() => {});
|
|
2875
2899
|
|
|
2876
|
-
|
|
2900
|
+
TypeValidator.validateIntegerAssignment("u8", "0b11111111", null, false);
|
|
2877
2901
|
|
|
2878
|
-
expect(
|
|
2902
|
+
expect(spy).toHaveBeenCalledWith("0b11111111", "u8");
|
|
2879
2903
|
});
|
|
2880
2904
|
|
|
2881
2905
|
it("validates type conversion for non-literal expressions", () => {
|
|
2882
|
-
|
|
2883
|
-
const
|
|
2884
|
-
validateTypeConversion
|
|
2885
|
-
|
|
2906
|
+
setupState();
|
|
2907
|
+
const spy = vi
|
|
2908
|
+
.spyOn(TypeResolver, "validateTypeConversion")
|
|
2909
|
+
.mockImplementation(() => {});
|
|
2886
2910
|
|
|
2887
|
-
|
|
2911
|
+
TypeValidator.validateIntegerAssignment("u8", "myVariable", "u16", false);
|
|
2888
2912
|
|
|
2889
|
-
expect(
|
|
2913
|
+
expect(spy).toHaveBeenCalledWith("u8", "u16");
|
|
2890
2914
|
});
|
|
2891
2915
|
|
|
2892
2916
|
it("trims whitespace from expression text", () => {
|
|
2893
|
-
|
|
2894
|
-
const
|
|
2895
|
-
validateLiteralFitsType
|
|
2896
|
-
|
|
2917
|
+
setupState();
|
|
2918
|
+
const spy = vi
|
|
2919
|
+
.spyOn(TypeResolver, "validateLiteralFitsType")
|
|
2920
|
+
.mockImplementation(() => {});
|
|
2897
2921
|
|
|
2898
|
-
|
|
2922
|
+
TypeValidator.validateIntegerAssignment("u8", " 100 ", null, false);
|
|
2899
2923
|
|
|
2900
|
-
expect(
|
|
2924
|
+
expect(spy).toHaveBeenCalledWith("100", "u8");
|
|
2901
2925
|
});
|
|
2902
2926
|
});
|
|
2903
2927
|
});
|