c-next 0.2.16 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/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__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -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/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/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 +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- 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/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- 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__/AssignmentExpectedTypeResolver.test.ts +19 -0
- 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 +322 -9
- 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/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/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 +140 -24
- 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 +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -16,6 +16,22 @@ vi.mock("../../../TypeValidator", () => ({
|
|
|
16
16
|
},
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
|
+
// Slice codegen resolves the source value's type (Issue #1081 review) — mock it
|
|
20
|
+
// so slice tests can control the source type independently of a real parse tree.
|
|
21
|
+
const { mockGetExpressionType, mockGetIntegerExpressionType } = vi.hoisted(
|
|
22
|
+
() => ({
|
|
23
|
+
mockGetExpressionType: vi.fn(),
|
|
24
|
+
mockGetIntegerExpressionType: vi.fn(),
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
vi.mock("../../../TypeResolver", () => ({
|
|
29
|
+
default: {
|
|
30
|
+
getExpressionType: mockGetExpressionType,
|
|
31
|
+
getIntegerExpressionType: mockGetIntegerExpressionType,
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
|
|
19
35
|
import arrayHandlers from "../ArrayHandlers";
|
|
20
36
|
import AssignmentKind from "../../AssignmentKind";
|
|
21
37
|
import IAssignmentContext from "../../IAssignmentContext";
|
|
@@ -42,6 +58,9 @@ function createMockContext(
|
|
|
42
58
|
cOp: "=",
|
|
43
59
|
generatedValue: "value",
|
|
44
60
|
targetCtx: {} as never,
|
|
61
|
+
// Truthy so slice codegen resolves the source type via the mocked
|
|
62
|
+
// TypeResolver.getExpressionType (Issue #1081 review).
|
|
63
|
+
valueCtx: {} as never,
|
|
45
64
|
hasThis: false,
|
|
46
65
|
hasGlobal: false,
|
|
47
66
|
hasMemberAccess: false,
|
|
@@ -63,6 +82,9 @@ describe("ArrayHandlers", () => {
|
|
|
63
82
|
CodeGenState.reset();
|
|
64
83
|
HandlerTestUtils.setupMockGenerator();
|
|
65
84
|
HandlerTestUtils.setupMockSymbols();
|
|
85
|
+
// Default: a source whose direct type is null is also unresolved as a
|
|
86
|
+
// composite. Tests that exercise composite resolution override this.
|
|
87
|
+
mockGetIntegerExpressionType.mockReturnValue(null);
|
|
66
88
|
});
|
|
67
89
|
|
|
68
90
|
describe("handler registration", () => {
|
|
@@ -241,32 +263,231 @@ describe("ArrayHandlers", () => {
|
|
|
241
263
|
const getHandler = () =>
|
|
242
264
|
arrayHandlers.find(([kind]) => kind === AssignmentKind.ARRAY_SLICE)?.[1];
|
|
243
265
|
|
|
244
|
-
|
|
266
|
+
// Every unrolled slice copy is prefixed with a comment naming the MISRA
|
|
267
|
+
// rule it satisfies and why the codegen looks the way it does (Issue #1081).
|
|
268
|
+
// Mirror production sliceUnrollComment: the rule is cited ONLY when an
|
|
269
|
+
// equivalent memcpy would pass incompatible pointer types.
|
|
270
|
+
const sliceComment = (destCType: string, srcCType: string) =>
|
|
271
|
+
"/* MISRA C:2012 Rule 21.15: slice copy unrolled to per-element writes " +
|
|
272
|
+
`(memcpy would pass incompatible pointer types: ${destCType}* vs ${srcCType}*). */`;
|
|
273
|
+
|
|
274
|
+
// Default the source type to a wide unsigned; tests override per case.
|
|
275
|
+
beforeEach(() => mockGetExpressionType.mockReturnValue("u64"));
|
|
276
|
+
|
|
277
|
+
// Issue #1081: slice assignment lowers to per-element little-endian writes
|
|
278
|
+
// (no memcpy), keeping MISRA C:2012 Rule 21.15 (compatible memcpy pointers)
|
|
279
|
+
// satisfied because no incompatible pointer punning is emitted.
|
|
280
|
+
it("generates unrolled byte writes for a u8 slice (no memcpy/string.h)", () => {
|
|
281
|
+
mockGetExpressionType.mockReturnValue("u32");
|
|
245
282
|
HandlerTestUtils.setupMockTypeRegistry([
|
|
246
|
-
["buffer", { arrayDimensions: [100], baseType: "u8" }],
|
|
283
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
247
284
|
]);
|
|
248
285
|
HandlerTestUtils.setupMockGenerator({
|
|
249
286
|
tryEvaluateConstant: vi
|
|
250
287
|
.fn()
|
|
251
288
|
.mockReturnValueOnce(0)
|
|
252
|
-
.mockReturnValueOnce(
|
|
289
|
+
.mockReturnValueOnce(4),
|
|
253
290
|
});
|
|
254
291
|
const ctx = createMockContext({
|
|
255
292
|
identifiers: ["buffer"],
|
|
256
293
|
subscripts: [
|
|
257
294
|
{ mockValue: "0", start: { line: 1 } } as never,
|
|
258
|
-
{ mockValue: "
|
|
295
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
259
296
|
],
|
|
260
297
|
generatedValue: "source",
|
|
261
298
|
});
|
|
262
299
|
|
|
263
300
|
const result = getHandler()!(ctx);
|
|
264
301
|
|
|
265
|
-
|
|
266
|
-
|
|
302
|
+
// The source is materialized into a single unsigned temp so it is read
|
|
303
|
+
// exactly once (Issue #1085 review, Finding 2).
|
|
304
|
+
expect(result).toBe(
|
|
305
|
+
`${sliceComment("uint8_t", "uint32_t")}\n` +
|
|
306
|
+
"const uint32_t _tmp0 = (uint32_t)(source);\n" +
|
|
307
|
+
"buffer[0] = (uint8_t)(_tmp0);\n" +
|
|
308
|
+
"buffer[1] = (uint8_t)(_tmp0 >> 8U);\n" +
|
|
309
|
+
"buffer[2] = (uint8_t)(_tmp0 >> 16U);\n" +
|
|
310
|
+
"buffer[3] = (uint8_t)(_tmp0 >> 24U);",
|
|
311
|
+
);
|
|
312
|
+
// No memcpy means <string.h> is not required.
|
|
313
|
+
expect(CodeGenState.needsString).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("writes at element granularity for a u16 slice (offset = element index)", () => {
|
|
317
|
+
mockGetExpressionType.mockReturnValue("u64");
|
|
318
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
319
|
+
["arr16", { arrayDimensions: [16], baseType: "u16", bitWidth: 16 }],
|
|
320
|
+
]);
|
|
321
|
+
HandlerTestUtils.setupMockGenerator({
|
|
322
|
+
tryEvaluateConstant: vi
|
|
323
|
+
.fn()
|
|
324
|
+
.mockReturnValueOnce(0)
|
|
325
|
+
.mockReturnValueOnce(8),
|
|
326
|
+
});
|
|
327
|
+
const ctx = createMockContext({
|
|
328
|
+
identifiers: ["arr16"],
|
|
329
|
+
subscripts: [
|
|
330
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
331
|
+
{ mockValue: "8", start: { line: 1 } } as never,
|
|
332
|
+
],
|
|
333
|
+
generatedValue: "value",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const result = getHandler()!(ctx);
|
|
337
|
+
|
|
338
|
+
expect(result).toBe(
|
|
339
|
+
`${sliceComment("uint16_t", "uint64_t")}\n` +
|
|
340
|
+
"const uint64_t _tmp0 = (uint64_t)(value);\n" +
|
|
341
|
+
"arr16[0] = (uint16_t)(_tmp0);\n" +
|
|
342
|
+
"arr16[1] = (uint16_t)(_tmp0 >> 16U);\n" +
|
|
343
|
+
"arr16[2] = (uint16_t)(_tmp0 >> 32U);\n" +
|
|
344
|
+
"arr16[3] = (uint16_t)(_tmp0 >> 48U);",
|
|
345
|
+
);
|
|
267
346
|
});
|
|
268
347
|
|
|
269
|
-
it("
|
|
348
|
+
it("omits the rule citation when source and element types match (no 21.15)", () => {
|
|
349
|
+
mockGetExpressionType.mockReturnValue("u32");
|
|
350
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
351
|
+
["arr32", { arrayDimensions: [16], baseType: "u32", bitWidth: 32 }],
|
|
352
|
+
]);
|
|
353
|
+
HandlerTestUtils.setupMockGenerator({
|
|
354
|
+
tryEvaluateConstant: vi
|
|
355
|
+
.fn()
|
|
356
|
+
.mockReturnValueOnce(0)
|
|
357
|
+
.mockReturnValueOnce(4),
|
|
358
|
+
});
|
|
359
|
+
const ctx = createMockContext({
|
|
360
|
+
identifiers: ["arr32"],
|
|
361
|
+
subscripts: [
|
|
362
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
363
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
364
|
+
],
|
|
365
|
+
generatedValue: "value",
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const result = getHandler()!(ctx);
|
|
369
|
+
|
|
370
|
+
// u32[] <- u32: an equivalent memcpy would be compliant, so no comment.
|
|
371
|
+
expect(result).toBe("arr32[0] = (uint32_t)(value);");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("accepts an in-bounds wide-element slice at a non-zero offset (Finding 1)", () => {
|
|
375
|
+
mockGetExpressionType.mockReturnValue("u64");
|
|
376
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
377
|
+
["arr64", { arrayDimensions: [8], baseType: "u64", bitWidth: 64 }],
|
|
378
|
+
]);
|
|
379
|
+
HandlerTestUtils.setupMockGenerator({
|
|
380
|
+
tryEvaluateConstant: vi
|
|
381
|
+
.fn()
|
|
382
|
+
.mockReturnValueOnce(4)
|
|
383
|
+
.mockReturnValueOnce(8),
|
|
384
|
+
});
|
|
385
|
+
const ctx = createMockContext({
|
|
386
|
+
identifiers: ["arr64"],
|
|
387
|
+
subscripts: [
|
|
388
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
389
|
+
{ mockValue: "8", start: { line: 1 } } as never,
|
|
390
|
+
],
|
|
391
|
+
generatedValue: "value",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const result = getHandler()!(ctx);
|
|
395
|
+
|
|
396
|
+
// Element span is offset(4) + 1 element = 5 <= capacity 8. The old check
|
|
397
|
+
// compared byte length against element capacity (4 + 8 = 12 > 8) and
|
|
398
|
+
// wrongly rejected this in-bounds slice. Single element → no temp;
|
|
399
|
+
// same-type u64[] <- u64 → no rule citation.
|
|
400
|
+
expect(result).toBe("arr64[4] = (uint64_t)(value);");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("reports the element span (not bytes) in the out-of-bounds message", () => {
|
|
404
|
+
mockGetExpressionType.mockReturnValue("u64");
|
|
405
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
406
|
+
["arr16", { arrayDimensions: [4], baseType: "u16", bitWidth: 16 }],
|
|
407
|
+
]);
|
|
408
|
+
HandlerTestUtils.setupMockGenerator({
|
|
409
|
+
tryEvaluateConstant: vi
|
|
410
|
+
.fn()
|
|
411
|
+
.mockReturnValueOnce(2)
|
|
412
|
+
.mockReturnValueOnce(8),
|
|
413
|
+
});
|
|
414
|
+
const ctx = createMockContext({
|
|
415
|
+
identifiers: ["arr16"],
|
|
416
|
+
subscripts: [
|
|
417
|
+
{ mockValue: "2", start: { line: 7 } } as never,
|
|
418
|
+
{ mockValue: "8", start: { line: 7 } } as never,
|
|
419
|
+
],
|
|
420
|
+
generatedValue: "value",
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// offset 2 + (8 bytes / 2) = 6 elements > capacity 4.
|
|
424
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
425
|
+
"offset(2) + 4 element(s) = 6 exceeds buffer capacity(4)",
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("casts a signed source to unsigned once in the temp (MISRA 10.1)", () => {
|
|
430
|
+
mockGetExpressionType.mockReturnValue("i32");
|
|
431
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
432
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
433
|
+
]);
|
|
434
|
+
HandlerTestUtils.setupMockGenerator({
|
|
435
|
+
tryEvaluateConstant: vi
|
|
436
|
+
.fn()
|
|
437
|
+
.mockReturnValueOnce(0)
|
|
438
|
+
.mockReturnValueOnce(4),
|
|
439
|
+
});
|
|
440
|
+
const ctx = createMockContext({
|
|
441
|
+
identifiers: ["buffer"],
|
|
442
|
+
subscripts: [
|
|
443
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
444
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
445
|
+
],
|
|
446
|
+
generatedValue: "value",
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const result = getHandler()!(ctx);
|
|
450
|
+
|
|
451
|
+
// The signed source is cast to its same-width unsigned type once, in the
|
|
452
|
+
// temp declaration, so every shift on the temp is MISRA Rule 10.1-clean.
|
|
453
|
+
expect(result).toBe(
|
|
454
|
+
`${sliceComment("uint8_t", "int32_t")}\n` +
|
|
455
|
+
"const uint32_t _tmp0 = (uint32_t)(value);\n" +
|
|
456
|
+
"buffer[0] = (uint8_t)(_tmp0);\n" +
|
|
457
|
+
"buffer[1] = (uint8_t)(_tmp0 >> 8U);\n" +
|
|
458
|
+
"buffer[2] = (uint8_t)(_tmp0 >> 16U);\n" +
|
|
459
|
+
"buffer[3] = (uint8_t)(_tmp0 >> 24U);",
|
|
460
|
+
);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("uses the signed-element double-cast for a signed destination (MISRA 10.8)", () => {
|
|
464
|
+
mockGetExpressionType.mockReturnValue("i32");
|
|
465
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
466
|
+
["arrI", { arrayDimensions: [16], baseType: "i32", bitWidth: 32 }],
|
|
467
|
+
]);
|
|
468
|
+
HandlerTestUtils.setupMockGenerator({
|
|
469
|
+
tryEvaluateConstant: vi
|
|
470
|
+
.fn()
|
|
471
|
+
.mockReturnValueOnce(0)
|
|
472
|
+
.mockReturnValueOnce(4),
|
|
473
|
+
});
|
|
474
|
+
const ctx = createMockContext({
|
|
475
|
+
identifiers: ["arrI"],
|
|
476
|
+
subscripts: [
|
|
477
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
478
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
479
|
+
],
|
|
480
|
+
generatedValue: "value",
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const result = getHandler()!(ctx);
|
|
484
|
+
|
|
485
|
+
// i32[] <- i32: same type, so no rule citation; dest cast still applies.
|
|
486
|
+
expect(result).toBe("arrI[0] = (int32_t)(uint32_t)(value);");
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("generates double-cast char writes for a string slice (MISRA 10.8)", () => {
|
|
490
|
+
mockGetExpressionType.mockReturnValue("u16");
|
|
270
491
|
HandlerTestUtils.setupMockTypeRegistry([
|
|
271
492
|
[
|
|
272
493
|
"str",
|
|
@@ -282,20 +503,267 @@ describe("ArrayHandlers", () => {
|
|
|
282
503
|
tryEvaluateConstant: vi
|
|
283
504
|
.fn()
|
|
284
505
|
.mockReturnValueOnce(5)
|
|
285
|
-
.mockReturnValueOnce(
|
|
506
|
+
.mockReturnValueOnce(2),
|
|
286
507
|
});
|
|
287
508
|
const ctx = createMockContext({
|
|
288
509
|
identifiers: ["str"],
|
|
289
510
|
subscripts: [
|
|
290
511
|
{ mockValue: "5", start: { line: 1 } } as never,
|
|
291
|
-
{ mockValue: "
|
|
512
|
+
{ mockValue: "2", start: { line: 1 } } as never,
|
|
292
513
|
],
|
|
293
514
|
generatedValue: "data",
|
|
294
515
|
});
|
|
295
516
|
|
|
296
517
|
const result = getHandler()!(ctx);
|
|
297
518
|
|
|
298
|
-
expect(result).toBe(
|
|
519
|
+
expect(result).toBe(
|
|
520
|
+
`${sliceComment("char", "uint16_t")}\n` +
|
|
521
|
+
"const uint16_t _tmp0 = (uint16_t)(data);\n" +
|
|
522
|
+
"str[5] = (char)(uint8_t)(_tmp0);\n" +
|
|
523
|
+
"str[6] = (char)(uint8_t)(_tmp0 >> 8U);",
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it("sizes the temp to the slice length for an unresolved source type", () => {
|
|
528
|
+
mockGetExpressionType.mockReturnValue(null); // e.g. a computed expression
|
|
529
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
530
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
531
|
+
]);
|
|
532
|
+
HandlerTestUtils.setupMockGenerator({
|
|
533
|
+
tryEvaluateConstant: vi
|
|
534
|
+
.fn()
|
|
535
|
+
.mockReturnValueOnce(0)
|
|
536
|
+
.mockReturnValueOnce(4),
|
|
537
|
+
});
|
|
538
|
+
const ctx = createMockContext({
|
|
539
|
+
identifiers: ["buffer"],
|
|
540
|
+
subscripts: [
|
|
541
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
542
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
543
|
+
],
|
|
544
|
+
generatedValue: "expr",
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
const result = getHandler()!(ctx);
|
|
548
|
+
|
|
549
|
+
// Unknown source: NO Rule 21.15 citation — we cannot prove the source type
|
|
550
|
+
// is incompatible with the destination element, so asserting "incompatible
|
|
551
|
+
// pointer types" would be false (Issue #1085 review, Finding #1). The temp
|
|
552
|
+
// is still sized to the 4-byte slice length (uint32_t), not the widest
|
|
553
|
+
// type — so the cast does not widen a composite expression like `a + b`
|
|
554
|
+
// (MISRA Rule 10.8), while every shift on the temp stays unsigned (10.1)
|
|
555
|
+
// and in range (Finding 3).
|
|
556
|
+
expect(result).toBe(
|
|
557
|
+
"const uint32_t _tmp0 = (uint32_t)(expr);\n" +
|
|
558
|
+
"buffer[0] = (uint8_t)(_tmp0);\n" +
|
|
559
|
+
"buffer[1] = (uint8_t)(_tmp0 >> 8U);\n" +
|
|
560
|
+
"buffer[2] = (uint8_t)(_tmp0 >> 16U);\n" +
|
|
561
|
+
"buffer[3] = (uint8_t)(_tmp0 >> 24U);",
|
|
562
|
+
);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("two-steps a composite signed source so no composite is cast (Rule 10.8)", () => {
|
|
566
|
+
// Direct type unresolved (composite), but resolvable as a signed i32 via
|
|
567
|
+
// Rule 10.4's same-category guarantee. The source must be bound to its
|
|
568
|
+
// own signed type first, then reinterpreted — the unsigned cast lands on
|
|
569
|
+
// the simple temp, never on the `a + b` composite.
|
|
570
|
+
mockGetExpressionType.mockReturnValue(null);
|
|
571
|
+
mockGetIntegerExpressionType.mockReturnValue("i32");
|
|
572
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
573
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
574
|
+
]);
|
|
575
|
+
HandlerTestUtils.setupMockGenerator({
|
|
576
|
+
tryEvaluateConstant: vi
|
|
577
|
+
.fn()
|
|
578
|
+
.mockReturnValueOnce(0)
|
|
579
|
+
.mockReturnValueOnce(4),
|
|
580
|
+
});
|
|
581
|
+
const ctx = createMockContext({
|
|
582
|
+
identifiers: ["buffer"],
|
|
583
|
+
subscripts: [
|
|
584
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
585
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
586
|
+
],
|
|
587
|
+
generatedValue: "a + b",
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const result = getHandler()!(ctx);
|
|
591
|
+
|
|
592
|
+
expect(result).toBe(
|
|
593
|
+
"/* MISRA C:2012 Rule 21.15: slice copy unrolled to per-element writes " +
|
|
594
|
+
"(memcpy would pass incompatible pointer types: uint8_t* vs int32_t*). */\n" +
|
|
595
|
+
"const int32_t _tmp0 = a + b;\n" +
|
|
596
|
+
"const uint32_t _tmp1 = (uint32_t)_tmp0;\n" +
|
|
597
|
+
"buffer[0] = (uint8_t)(_tmp1);\n" +
|
|
598
|
+
"buffer[1] = (uint8_t)(_tmp1 >> 8U);\n" +
|
|
599
|
+
"buffer[2] = (uint8_t)(_tmp1 >> 16U);\n" +
|
|
600
|
+
"buffer[3] = (uint8_t)(_tmp1 >> 24U);",
|
|
601
|
+
);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it("throws when slice length is not a multiple of the element size", () => {
|
|
605
|
+
mockGetExpressionType.mockReturnValue("u64");
|
|
606
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
607
|
+
["arr16", { arrayDimensions: [16], baseType: "u16", bitWidth: 16 }],
|
|
608
|
+
]);
|
|
609
|
+
HandlerTestUtils.setupMockGenerator({
|
|
610
|
+
tryEvaluateConstant: vi
|
|
611
|
+
.fn()
|
|
612
|
+
.mockReturnValueOnce(0)
|
|
613
|
+
.mockReturnValueOnce(3),
|
|
614
|
+
});
|
|
615
|
+
const ctx = createMockContext({
|
|
616
|
+
identifiers: ["arr16"],
|
|
617
|
+
subscripts: [
|
|
618
|
+
{ mockValue: "0", start: { line: 7 } } as never,
|
|
619
|
+
{ mockValue: "3", start: { line: 7 } } as never,
|
|
620
|
+
],
|
|
621
|
+
generatedValue: "value",
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
625
|
+
"must be a multiple of the element size",
|
|
626
|
+
);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it("throws when slice length exceeds the source value width", () => {
|
|
630
|
+
mockGetExpressionType.mockReturnValue("u32"); // 4-byte source
|
|
631
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
632
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
633
|
+
]);
|
|
634
|
+
HandlerTestUtils.setupMockGenerator({
|
|
635
|
+
tryEvaluateConstant: vi
|
|
636
|
+
.fn()
|
|
637
|
+
.mockReturnValueOnce(0)
|
|
638
|
+
.mockReturnValueOnce(8), // copying 8 bytes from a 4-byte value
|
|
639
|
+
});
|
|
640
|
+
const ctx = createMockContext({
|
|
641
|
+
identifiers: ["buffer"],
|
|
642
|
+
subscripts: [
|
|
643
|
+
{ mockValue: "0", start: { line: 8 } } as never,
|
|
644
|
+
{ mockValue: "8", start: { line: 8 } } as never,
|
|
645
|
+
],
|
|
646
|
+
generatedValue: "value",
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
650
|
+
"exceeds the source value width",
|
|
651
|
+
);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("throws on a non-integer (float) slice source", () => {
|
|
655
|
+
mockGetExpressionType.mockReturnValue("f32");
|
|
656
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
657
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
658
|
+
]);
|
|
659
|
+
HandlerTestUtils.setupMockGenerator({
|
|
660
|
+
tryEvaluateConstant: vi
|
|
661
|
+
.fn()
|
|
662
|
+
.mockReturnValueOnce(0)
|
|
663
|
+
.mockReturnValueOnce(4),
|
|
664
|
+
});
|
|
665
|
+
const ctx = createMockContext({
|
|
666
|
+
identifiers: ["buffer"],
|
|
667
|
+
subscripts: [
|
|
668
|
+
{ mockValue: "0", start: { line: 9 } } as never,
|
|
669
|
+
{ mockValue: "4", start: { line: 9 } } as never,
|
|
670
|
+
],
|
|
671
|
+
generatedValue: "fval",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
675
|
+
"source must be an integer value",
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it("throws on slice assignment into a float array", () => {
|
|
680
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
681
|
+
["arrF", { arrayDimensions: [16], baseType: "f32", bitWidth: 32 }],
|
|
682
|
+
]);
|
|
683
|
+
HandlerTestUtils.setupMockGenerator({
|
|
684
|
+
tryEvaluateConstant: vi
|
|
685
|
+
.fn()
|
|
686
|
+
.mockReturnValueOnce(0)
|
|
687
|
+
.mockReturnValueOnce(4),
|
|
688
|
+
});
|
|
689
|
+
const ctx = createMockContext({
|
|
690
|
+
identifiers: ["arrF"],
|
|
691
|
+
subscripts: [
|
|
692
|
+
{ mockValue: "0", start: { line: 9 } } as never,
|
|
693
|
+
{ mockValue: "4", start: { line: 9 } } as never,
|
|
694
|
+
],
|
|
695
|
+
generatedValue: "value",
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
699
|
+
"Slice assignment is not supported for element type",
|
|
700
|
+
);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("rejects a negative literal that does not fit the slice (mirror of the positive overflow check)", () => {
|
|
704
|
+
// A negative literal types as null via getExpressionType (unary minus), but
|
|
705
|
+
// is still a compile-time constant. It must be fit-checked like a positive
|
|
706
|
+
// one: buf[0,1] <- -300 cannot fit a 1-byte slice (-300 < -128) and must be
|
|
707
|
+
// rejected, not silently truncated to (uint8_t)(-300) (Issue #1085 review).
|
|
708
|
+
mockGetExpressionType.mockReturnValue(null);
|
|
709
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
710
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
711
|
+
]);
|
|
712
|
+
HandlerTestUtils.setupMockGenerator({
|
|
713
|
+
tryEvaluateConstant: vi
|
|
714
|
+
.fn()
|
|
715
|
+
.mockReturnValueOnce(0) // offset
|
|
716
|
+
.mockReturnValueOnce(1) // length (1-byte slice)
|
|
717
|
+
.mockReturnValue(-300), // source value (folded; re-read by resolveLiteralSliceSource)
|
|
718
|
+
});
|
|
719
|
+
const ctx = createMockContext({
|
|
720
|
+
identifiers: ["buffer"],
|
|
721
|
+
subscripts: [
|
|
722
|
+
{ mockValue: "0", start: { line: 7 } } as never,
|
|
723
|
+
{ mockValue: "1", start: { line: 7 } } as never,
|
|
724
|
+
],
|
|
725
|
+
generatedValue: "-300",
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
expect(() => getHandler()!(ctx)).toThrow(
|
|
729
|
+
"Slice assignment literal value (-300) does not fit in the 1-byte slice",
|
|
730
|
+
);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it("serializes an in-range negative literal as two's-complement little-endian bytes", () => {
|
|
734
|
+
// -1 fits a 4-byte slice (-2^31 <= -1 < 2^32), so it is accepted and written
|
|
735
|
+
// as 0xFFFFFFFF little-endian. The literal types to the slice width (u32),
|
|
736
|
+
// so an equivalent memcpy would be incompatible -> Rule 21.15 is cited.
|
|
737
|
+
mockGetExpressionType.mockReturnValue(null);
|
|
738
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
739
|
+
["buffer", { arrayDimensions: [100], baseType: "u8", bitWidth: 8 }],
|
|
740
|
+
]);
|
|
741
|
+
HandlerTestUtils.setupMockGenerator({
|
|
742
|
+
tryEvaluateConstant: vi
|
|
743
|
+
.fn()
|
|
744
|
+
.mockReturnValueOnce(0) // offset
|
|
745
|
+
.mockReturnValueOnce(4) // length
|
|
746
|
+
.mockReturnValue(-1), // source value
|
|
747
|
+
});
|
|
748
|
+
const ctx = createMockContext({
|
|
749
|
+
identifiers: ["buffer"],
|
|
750
|
+
subscripts: [
|
|
751
|
+
{ mockValue: "0", start: { line: 1 } } as never,
|
|
752
|
+
{ mockValue: "4", start: { line: 1 } } as never,
|
|
753
|
+
],
|
|
754
|
+
generatedValue: "-1",
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const result = getHandler()!(ctx);
|
|
758
|
+
|
|
759
|
+
expect(result).toBe(
|
|
760
|
+
`${sliceComment("uint8_t", "uint32_t")}\n` +
|
|
761
|
+
"const uint32_t _tmp0 = (uint32_t)(-1);\n" +
|
|
762
|
+
"buffer[0] = (uint8_t)(_tmp0);\n" +
|
|
763
|
+
"buffer[1] = (uint8_t)(_tmp0 >> 8U);\n" +
|
|
764
|
+
"buffer[2] = (uint8_t)(_tmp0 >> 16U);\n" +
|
|
765
|
+
"buffer[3] = (uint8_t)(_tmp0 >> 24U);",
|
|
766
|
+
);
|
|
299
767
|
});
|
|
300
768
|
|
|
301
769
|
it("throws on compound assignment", () => {
|
|
@@ -382,7 +850,7 @@ describe("ArrayHandlers", () => {
|
|
|
382
850
|
|
|
383
851
|
it("throws on out of bounds access", () => {
|
|
384
852
|
HandlerTestUtils.setupMockTypeRegistry([
|
|
385
|
-
["buffer", { arrayDimensions: [50], baseType: "u8" }],
|
|
853
|
+
["buffer", { arrayDimensions: [50], baseType: "u8", bitWidth: 8 }],
|
|
386
854
|
]);
|
|
387
855
|
HandlerTestUtils.setupMockGenerator({
|
|
388
856
|
tryEvaluateConstant: vi
|
|
@@ -214,6 +214,9 @@ interface IOrchestrator {
|
|
|
214
214
|
conditionType: string,
|
|
215
215
|
): void;
|
|
216
216
|
|
|
217
|
+
/** Reject an always-true literal loop condition (ADR-113 / #1075, E0707) */
|
|
218
|
+
validateLoopConditionNotAlwaysTrue(ctx: Parser.ExpressionContext): void;
|
|
219
|
+
|
|
217
220
|
/** Validate no function calls in condition (Issue #254, E0702) */
|
|
218
221
|
validateConditionNoFunctionCall(
|
|
219
222
|
ctx: Parser.ExpressionContext,
|
|
@@ -26,6 +26,7 @@ import ArrayDimensionUtils from "./ArrayDimensionUtils";
|
|
|
26
26
|
import QualifiedNameGenerator from "../../utils/QualifiedNameGenerator";
|
|
27
27
|
import CodeGenState from "../../../../state/CodeGenState";
|
|
28
28
|
import ScopeUtils from "../../../../../utils/ScopeUtils";
|
|
29
|
+
import VariableModifierBuilder from "../../helpers/VariableModifierBuilder";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Generate initializer expression for a variable declaration.
|
|
@@ -38,10 +39,12 @@ function generateInitializer(
|
|
|
38
39
|
): string {
|
|
39
40
|
if (varDecl.expression()) {
|
|
40
41
|
// Issue #872: Set expectedType for MISRA 7.2 U suffix compliance
|
|
42
|
+
// Issue #992: withDeclarationInit suppresses compound literals at file scope (GCC 9-12 compat)
|
|
41
43
|
const typeName = orchestrator.generateType(varDecl.type());
|
|
42
|
-
return CodeGenState.withExpectedType(
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
return CodeGenState.withExpectedType(typeName, () =>
|
|
45
|
+
CodeGenState.withDeclarationInit(
|
|
46
|
+
() => ` = ${orchestrator.generateExpression(varDecl.expression()!)}`,
|
|
47
|
+
),
|
|
45
48
|
);
|
|
46
49
|
}
|
|
47
50
|
// ADR-015: Zero initialization for uninitialized scope variables
|
|
@@ -204,12 +207,23 @@ function generateRegularVariable(
|
|
|
204
207
|
orchestrator.markOpaqueScopeVariable(fullName);
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
// Issue #
|
|
210
|
+
// Issue #998: Use VariableModifierBuilder for consistent modifier handling
|
|
211
|
+
// This handles const, atomic, volatile, and validates mutual exclusion
|
|
212
|
+
// Scope variables are always at file scope (not in function body), no initializer for modifier purposes
|
|
213
|
+
const modifiers = VariableModifierBuilder.build(
|
|
214
|
+
varDecl,
|
|
215
|
+
false, // inFunctionBody - scope vars are file scope
|
|
216
|
+
false, // hasInitializer - doesn't affect volatile/atomic handling
|
|
217
|
+
false, // cppMode - doesn't affect volatile/atomic handling
|
|
218
|
+
);
|
|
219
|
+
// For scope variables: static for private, no modifier for public
|
|
220
|
+
// Then add volatile (from atomic or volatile keyword), then const
|
|
221
|
+
const staticPrefix = isPrivate ? "static " : "";
|
|
222
|
+
const volatilePrefix = modifiers.atomic || modifiers.volatile;
|
|
208
223
|
const constPrefix = isConst ? "const " : "";
|
|
209
|
-
const prefix = isPrivate ? "static " : "";
|
|
210
224
|
|
|
211
225
|
// Build declaration with all dimensions
|
|
212
|
-
let decl = `${
|
|
226
|
+
let decl = `${staticPrefix}${volatilePrefix}${constPrefix}${type} ${fullName}`;
|
|
213
227
|
decl += ArrayDimensionUtils.generateArrayTypeDimension(
|
|
214
228
|
arrayTypeCtx,
|
|
215
229
|
orchestrator,
|
|
@@ -225,7 +239,12 @@ function generateRegularVariable(
|
|
|
225
239
|
|
|
226
240
|
// Issue #948: Opaque types use NULL initialization instead of {0}
|
|
227
241
|
// Issue #958: External typedef struct types also use NULL initialization
|
|
228
|
-
|
|
242
|
+
// Issue #996: ...but only for SCALAR handles, which are single pointers. An
|
|
243
|
+
// *array* of opaque handles needs a brace initializer ({0}), not a scalar
|
|
244
|
+
// NULL. Route arrays through generateInitializer, which uses
|
|
245
|
+
// getZeroInitializer(type, isArray) — the single source of truth for
|
|
246
|
+
// zero-initialization (ADR-015).
|
|
247
|
+
if ((isOpaque || isExternalStruct) && !isArray) {
|
|
229
248
|
decl += " = NULL";
|
|
230
249
|
} else {
|
|
231
250
|
decl += generateInitializer(varDecl, isArray, orchestrator);
|