c-next 0.2.10 → 0.2.12

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.
Files changed (30) hide show
  1. package/dist/index.js +6228 -5365
  2. package/dist/index.js.map +3 -3
  3. package/grammar/C.g4 +8 -1
  4. package/package.json +3 -2
  5. package/src/transpiler/Transpiler.ts +16 -3
  6. package/src/transpiler/logic/parser/c/grammar/C.interp +15 -3
  7. package/src/transpiler/logic/parser/c/grammar/C.tokens +219 -207
  8. package/src/transpiler/logic/parser/c/grammar/CLexer.interp +21 -3
  9. package/src/transpiler/logic/parser/c/grammar/CLexer.tokens +219 -207
  10. package/src/transpiler/logic/parser/c/grammar/CLexer.ts +643 -606
  11. package/src/transpiler/logic/parser/c/grammar/CParser.ts +1076 -1038
  12. package/src/transpiler/logic/symbols/SymbolTable.ts +162 -34
  13. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +122 -2
  14. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +3 -3
  15. package/src/transpiler/logic/symbols/c/__tests__/PointerTypedef.test.ts +47 -0
  16. package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +56 -43
  17. package/src/transpiler/logic/symbols/c/index.ts +25 -3
  18. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +17 -0
  19. package/src/transpiler/output/codegen/CodeGenerator.ts +18 -0
  20. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +6 -0
  21. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +9 -3
  22. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +2 -0
  23. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +15 -3
  24. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +14 -6
  25. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +1 -0
  26. package/src/transpiler/output/codegen/types/IFunctionContextCallbacks.ts +2 -0
  27. package/src/transpiler/state/CodeGenState.ts +9 -0
  28. package/src/transpiler/types/ICachedFileEntry.ts +6 -0
  29. package/src/utils/cache/CacheManager.ts +34 -13
  30. package/src/utils/cache/__tests__/CacheManager.test.ts +6 -4
@@ -179,14 +179,24 @@ class CResolver {
179
179
  ? CResolver.extractTypedefName(decl, declSpecs)
180
180
  : undefined;
181
181
 
182
+ // Issue #957: Check if the typedef's declarator has a pointer prefix.
183
+ // For "typedef struct X *Y", Y is already a pointer type, NOT an opaque struct.
184
+ // Don't mark pointer typedefs as opaque.
185
+ const isPointerTypedef = ctx.isTypedef
186
+ ? CResolver.isPointerTypedef(decl)
187
+ : false;
188
+
182
189
  const structSymbol = StructCollector.collect(
183
190
  structSpec,
184
191
  ctx.sourceFile,
185
192
  ctx.line,
186
193
  symbolTable,
187
- typedefName,
188
- ctx.isTypedef,
189
- warnings,
194
+ {
195
+ typedefName,
196
+ isTypedef: ctx.isTypedef,
197
+ warnings,
198
+ isPointerTypedef,
199
+ },
190
200
  );
191
201
  if (structSymbol) {
192
202
  ctx.symbols.push(structSymbol);
@@ -209,6 +219,18 @@ class CResolver {
209
219
  return DeclaratorUtils.extractTypedefNameFromSpecs(declSpecs);
210
220
  }
211
221
 
222
+ /**
223
+ * Check if a typedef declaration has a pointer declarator.
224
+ * For "typedef struct X *Y", the declarator is "*Y" which has a pointer.
225
+ * Used for Issue #957 to distinguish pointer typedefs from opaque struct typedefs.
226
+ */
227
+ private static isPointerTypedef(decl: DeclarationContext): boolean {
228
+ const initDeclList = decl.initDeclaratorList();
229
+ if (!initDeclList) return false;
230
+
231
+ return DeclaratorUtils.firstDeclaratorHasPointer(initDeclList);
232
+ }
233
+
212
234
  /**
213
235
  * Collect enum symbols from an enum specifier.
214
236
  * Extracted to reduce cognitive complexity of collectDeclaration().
@@ -379,6 +379,23 @@ class DeclaratorUtils {
379
379
 
380
380
  return DeclaratorUtils.extractDeclaratorName(firstDeclarator) ?? undefined;
381
381
  }
382
+
383
+ /**
384
+ * Check if the first declarator in an init-declarator-list has a pointer.
385
+ * For "typedef struct X *handle_t;", the declarator is "*handle_t" which has a pointer.
386
+ * Used for Issue #957 to distinguish pointer typedefs from opaque struct typedefs.
387
+ */
388
+ static firstDeclaratorHasPointer(
389
+ initDeclList: InitDeclaratorListContext,
390
+ ): boolean {
391
+ const initDeclarators = initDeclList.initDeclarator?.();
392
+ if (!initDeclarators || initDeclarators.length === 0) return false;
393
+
394
+ const firstDeclarator = initDeclarators[0].declarator?.();
395
+ if (!firstDeclarator) return false;
396
+
397
+ return Boolean(firstDeclarator.pointer?.());
398
+ }
382
399
  }
383
400
 
384
401
  export default DeclaratorUtils;
@@ -1927,6 +1927,15 @@ export default class CodeGenerator implements IOrchestrator {
1927
1927
  return CodeGenState.isOpaqueType(typeName);
1928
1928
  }
1929
1929
 
1930
+ /**
1931
+ * Issue #958: Check if a type is an external typedef struct type.
1932
+ * Used for scope variables which should always be pointers for external struct types.
1933
+ * Part of IOrchestrator interface.
1934
+ */
1935
+ isTypedefStructType(typeName: string): boolean {
1936
+ return CodeGenState.isTypedefStructType(typeName);
1937
+ }
1938
+
1930
1939
  /**
1931
1940
  * Issue #948: Mark a scope variable as having an opaque type.
1932
1941
  * These variables are generated as pointers with NULL initialization.
@@ -3666,6 +3675,8 @@ export default class CodeGenerator implements IOrchestrator {
3666
3675
  isStructType: (typeName: string) => this.isStructType(typeName),
3667
3676
  resolveQualifiedType: (identifiers: string[]) =>
3668
3677
  this.resolveQualifiedType(identifiers),
3678
+ isTypedefStructType: (t: string) =>
3679
+ CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
3669
3680
  };
3670
3681
  }
3671
3682
 
@@ -3757,6 +3768,8 @@ export default class CodeGenerator implements IOrchestrator {
3757
3768
  isCallbackCompatible,
3758
3769
  forcePassByReference,
3759
3770
  forceConst,
3771
+ isTypedefStructType: (t) =>
3772
+ CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
3760
3773
  });
3761
3774
 
3762
3775
  // Use shared builder with C/C++ mode
@@ -3883,6 +3896,11 @@ export default class CodeGenerator implements IOrchestrator {
3883
3896
  ): string {
3884
3897
  const type = this.generateType(ctx.type());
3885
3898
 
3899
+ // Issue #958: C-header typedef struct types always need pointer semantics
3900
+ if (CodeGenState.symbolTable?.isTypedefStructType(type)) {
3901
+ return `${type}*`;
3902
+ }
3903
+
3886
3904
  if (!ctx.expression()) {
3887
3905
  return type;
3888
3906
  }
@@ -399,6 +399,12 @@ interface IOrchestrator {
399
399
  */
400
400
  isOpaqueType(typeName: string): boolean;
401
401
 
402
+ /**
403
+ * Issue #958: Check if a type is an external typedef struct type.
404
+ * Used for scope variables which should always be pointers for external struct types.
405
+ */
406
+ isTypedefStructType(typeName: string): boolean;
407
+
402
408
  /**
403
409
  * Mark a scope variable as having an opaque type.
404
410
  * These variables are generated as pointers with NULL initialization.
@@ -191,10 +191,15 @@ function generateRegularVariable(
191
191
  const fullName = QualifiedNameGenerator.forMember(scopeName, varName);
192
192
 
193
193
  // Issue #948: Check if this is an opaque (forward-declared) struct type
194
- // Opaque types must be declared as pointers with NULL initialization
194
+ // Issue #958: Also check for external typedef struct types (complete definitions)
195
+ // Both opaque and external typedef struct types must be declared as pointers
195
196
  const isOpaque = orchestrator.isOpaqueType(type);
196
- if (isOpaque) {
197
+ const isExternalStruct = orchestrator.isTypedefStructType(type);
198
+ if (isOpaque || isExternalStruct) {
197
199
  type = `${type}*`;
200
+ // Mark as "opaque" scope variable so CallExprGenerator knows this is already
201
+ // a pointer and doesn't add '&' when passing to functions. The name is historical
202
+ // but the tracking applies to any scope variable declared as a pointer type.
198
203
  orchestrator.markOpaqueScopeVariable(fullName);
199
204
  }
200
205
 
@@ -218,7 +223,8 @@ function generateRegularVariable(
218
223
  decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
219
224
 
220
225
  // Issue #948: Opaque types use NULL initialization instead of {0}
221
- if (isOpaque) {
226
+ // Issue #958: External typedef struct types also use NULL initialization
227
+ if (isOpaque || isExternalStruct) {
222
228
  decl += " = NULL";
223
229
  } else {
224
230
  decl += generateInitializer(varDecl, isArray, orchestrator);
@@ -468,6 +468,8 @@ function createMockOrchestrator(
468
468
  tryEvaluateConstant: vi.fn(() => undefined),
469
469
  // Issue #948: Opaque type helpers
470
470
  isOpaqueType: vi.fn(() => false),
471
+ // Issue #958: Typedef struct type helper
472
+ isTypedefStructType: vi.fn(() => false),
471
473
  markOpaqueScopeVariable: vi.fn(),
472
474
  ...overrides,
473
475
  } as unknown as IOrchestrator;
@@ -160,19 +160,27 @@ class FunctionContextManager {
160
160
  const isCallbackPointerParam =
161
161
  callbackTypedefInfo?.shouldBePointer ?? false;
162
162
 
163
+ // Issue #958: Check if type is a typedef'd struct from C headers
164
+ const isTypedefStruct =
165
+ callbacks.isTypedefStructType?.(typeInfo.typeName) ?? false;
166
+
163
167
  // Determine isStruct: for callback-compatible params, both typedef AND type info matter
164
168
  // - If typedef says pointer AND it's actually a struct, use -> access (isStruct=true)
165
169
  // - If typedef says pointer BUT it's a primitive (like u8), don't treat as struct
166
170
  // (primitives use forcePointerSemantics for dereference instead)
171
+ // Issue #958: C-header typedef struct types are always treated as struct (pointer semantics)
167
172
  const isStruct = callbackTypedefInfo
168
173
  ? isCallbackPointerParam && typeInfo.isStruct
169
- : typeInfo.isStruct;
174
+ : typeInfo.isStruct || isTypedefStruct;
170
175
 
171
176
  // Issue #895: Primitive types that become pointers need dereferencing when used as values
172
177
  // e.g., "u8 buf" becoming "uint8_t* buf" requires "*buf" when accessing the value
173
178
  const isCallbackPointerPrimitive =
174
179
  isCallbackPointerParam && !typeInfo.isStruct && !isArray;
175
180
 
181
+ // Issue #958: typedef struct params need pointer semantics (like callback pointer params)
182
+ const forcePointerSemantics = isCallbackPointerParam || isTypedefStruct;
183
+
176
184
  // Register in currentParameters
177
185
  const paramInfo = {
178
186
  name,
@@ -183,8 +191,8 @@ class FunctionContextManager {
183
191
  isCallback: typeInfo.isCallback,
184
192
  isString: typeInfo.isString,
185
193
  isCallbackPointerPrimitive,
186
- // Issue #895: Callback-compatible params need pointer semantics even in C++ mode
187
- forcePointerSemantics: isCallbackPointerParam,
194
+ // Issue #895/#958: Force pointer semantics for callback-compatible and typedef struct params
195
+ forcePointerSemantics,
188
196
  };
189
197
  CodeGenState.currentParameters.set(name, paramInfo);
190
198
 
@@ -195,6 +203,7 @@ class FunctionContextManager {
195
203
  param,
196
204
  isArray,
197
205
  isConst,
206
+ isTypedefStruct,
198
207
  );
199
208
  }
200
209
 
@@ -320,6 +329,7 @@ class FunctionContextManager {
320
329
  param: Parser.ParameterContext,
321
330
  isArray: boolean,
322
331
  isConst: boolean,
332
+ isTypedefStruct = false,
323
333
  ): void {
324
334
  const { typeName, isString } = typeInfo;
325
335
  const typeCtx = param.type();
@@ -358,6 +368,8 @@ class FunctionContextManager {
358
368
  isString,
359
369
  stringCapacity,
360
370
  isParameter: true,
371
+ // Issue #958: typedef struct params are already pointers — prevent &arg in call sites
372
+ ...(isTypedefStruct && { isPointer: true }),
361
373
  };
362
374
  CodeGenState.setVariableTypeInfo(name, registeredType);
363
375
  }
@@ -52,6 +52,9 @@ interface IFromASTDeps {
52
52
  */
53
53
  forcePassByReference?: boolean;
54
54
 
55
+ /** Issue #958: Check if a type name is a typedef'd struct from C headers */
56
+ isTypedefStructType: (typeName: string) => boolean;
57
+
55
58
  /**
56
59
  * Issue #895: Force const qualifier from callback typedef signature.
57
60
  * When the C typedef has `const T*`, this preserves const on the generated param.
@@ -137,15 +140,19 @@ class ParameterInputAdapter {
137
140
  // Determine classification for non-array, non-string types
138
141
  const isKnownStruct = deps.isKnownStruct(typeName);
139
142
  const isKnownPrimitive = !!deps.typeMap[typeName];
143
+ // Issue #958: C-header typedef struct types need pointer semantics
144
+ const isTypedefStruct = deps.isTypedefStructType(typeName);
140
145
  // Issue #895: Don't add auto-const for callback-compatible functions
141
146
  // because it would change the signature and break typedef compatibility
142
147
  const isAutoConst =
143
148
  !deps.isCallbackCompatible && !deps.isModified && !isConst;
144
149
 
145
- // Issue #895: For callback-compatible functions, force pass-by-reference
146
- // when the typedef signature requires a pointer (e.g., opaque types)
150
+ // Issue #895/#958: Force pass-by-reference for callback or typedef struct types
147
151
  const isPassByReference =
148
- deps.forcePassByReference || isKnownStruct || isKnownPrimitive;
152
+ deps.forcePassByReference ||
153
+ isKnownStruct ||
154
+ isKnownPrimitive ||
155
+ isTypedefStruct;
149
156
 
150
157
  return {
151
158
  name,
@@ -158,9 +165,10 @@ class ParameterInputAdapter {
158
165
  isString: false,
159
166
  isPassByValue: deps.isPassByValue,
160
167
  isPassByReference,
161
- // Issue #895: Force pointer syntax in C++ mode for callback-compatible functions
162
- // because C callback typedefs expect pointers, not C++ references
163
- forcePointerSyntax: deps.forcePassByReference,
168
+ // Issue #895/#958: Force pointer syntax in C++ mode for callback-compatible
169
+ // and typedef struct params (C types expect pointers, not C++ references)
170
+ forcePointerSyntax:
171
+ deps.forcePassByReference || isTypedefStruct || undefined,
164
172
  // Issue #895: Preserve const from callback typedef signature
165
173
  forceConst: deps.forceConst,
166
174
  };
@@ -62,6 +62,7 @@ function createDefaultASTDeps(overrides?: {
62
62
  isModified: overrides?.isModified ?? false,
63
63
  isPassByValue: overrides?.isPassByValue ?? false,
64
64
  isCallbackCompatible: false,
65
+ isTypedefStructType: () => false,
65
66
  };
66
67
  }
67
68
 
@@ -7,6 +7,8 @@ interface IFunctionContextCallbacks {
7
7
  isStructType: (typeName: string) => boolean;
8
8
  /** Resolve qualified type identifiers to a type name */
9
9
  resolveQualifiedType: (identifiers: string[]) => string;
10
+ /** Issue #958: Check if a type name is a typedef'd struct from C headers */
11
+ isTypedefStructType?: (typeName: string) => boolean;
10
12
  }
11
13
 
12
14
  export default IFunctionContextCallbacks;
@@ -528,6 +528,15 @@ export default class CodeGenState {
528
528
  return this.symbols?.opaqueTypes.has(typeName) ?? false;
529
529
  }
530
530
 
531
+ /**
532
+ * Issue #958: Check if a type name is an external typedef struct type.
533
+ * External typedef struct types should use pointer semantics for scope variables.
534
+ * Unlike isOpaqueType, this returns true for both forward-declared and complete structs.
535
+ */
536
+ static isTypedefStructType(typeName: string): boolean {
537
+ return this.symbolTable?.isTypedefStructType(typeName) ?? false;
538
+ }
539
+
531
540
  /**
532
541
  * Get type info for a variable.
533
542
  * Checks local typeRegistry first, then falls back to SymbolTable
@@ -20,6 +20,12 @@ interface ICachedFileEntry {
20
20
  enumBitWidth?: Record<string, number>;
21
21
  /** Issue #948: Opaque types (forward-declared struct types) */
22
22
  opaqueTypes?: string[];
23
+ /** Issue #958: Typedef struct types with source files ([typeName, sourceFile] pairs) */
24
+ typedefStructTypes?: Array<[string, string]>;
25
+ /** Issue #958: Struct tag → typedef name aliases ([structTag, typedefName] pairs) */
26
+ structTagAliases?: Array<[string, string]>;
27
+ /** Issue #958: Struct tags that have full definitions (bodies) */
28
+ structTagsWithBodies?: string[];
23
29
  }
24
30
 
25
31
  export default ICachedFileEntry;
@@ -32,7 +32,7 @@ import ESourceLanguage from "../types/ESourceLanguage";
32
32
  const defaultFs = NodeFileSystem.instance;
33
33
 
34
34
  /** Current cache format version - increment when serialization format changes */
35
- const CACHE_VERSION = 5; // Issue #948: Add opaqueTypes to cache
35
+ const CACHE_VERSION = 7; // Issue #958: Add structTagAliases, structTagsWithBodies to cache
36
36
 
37
37
  const TRANSPILER_VERSION = packageJson.version;
38
38
 
@@ -135,6 +135,9 @@ class CacheManager {
135
135
  needsStructKeyword: string[];
136
136
  enumBitWidth: Map<string, number>;
137
137
  opaqueTypes: string[];
138
+ typedefStructTypes: Array<[string, string]>;
139
+ structTagAliases: Array<[string, string]>;
140
+ structTagsWithBodies: string[];
138
141
  } | null {
139
142
  if (!this.cache) return null;
140
143
 
@@ -175,6 +178,9 @@ class CacheManager {
175
178
  needsStructKeyword: cachedEntry.needsStructKeyword ?? [],
176
179
  enumBitWidth,
177
180
  opaqueTypes: cachedEntry.opaqueTypes ?? [],
181
+ typedefStructTypes: cachedEntry.typedefStructTypes ?? [],
182
+ structTagAliases: cachedEntry.structTagAliases ?? [],
183
+ structTagsWithBodies: cachedEntry.structTagsWithBodies ?? [],
178
184
  };
179
185
  }
180
186
 
@@ -186,9 +192,14 @@ class CacheManager {
186
192
  filePath: string,
187
193
  symbols: ISerializedSymbol[],
188
194
  structFields: Map<string, Map<string, IStructFieldInfo>>,
189
- needsStructKeyword?: string[],
190
- enumBitWidth?: Map<string, number>,
191
- opaqueTypes?: string[],
195
+ options?: {
196
+ needsStructKeyword?: string[];
197
+ enumBitWidth?: Map<string, number>;
198
+ opaqueTypes?: string[];
199
+ typedefStructTypes?: Array<[string, string]>;
200
+ structTagAliases?: Array<[string, string]>;
201
+ structTagsWithBodies?: string[];
202
+ },
192
203
  ): void {
193
204
  if (!this.cache) return;
194
205
 
@@ -218,8 +229,8 @@ class CacheManager {
218
229
 
219
230
  // Issue #208: Convert enum bit widths from Map to plain object
220
231
  const serializedEnumBitWidth: Record<string, number> = {};
221
- if (enumBitWidth) {
222
- for (const [enumName, width] of enumBitWidth) {
232
+ if (options?.enumBitWidth) {
233
+ for (const [enumName, width] of options.enumBitWidth) {
223
234
  serializedEnumBitWidth[enumName] = width;
224
235
  }
225
236
  }
@@ -230,9 +241,12 @@ class CacheManager {
230
241
  cacheKey,
231
242
  symbols: serializedSymbols,
232
243
  structFields: serializedFields,
233
- needsStructKeyword,
244
+ needsStructKeyword: options?.needsStructKeyword,
234
245
  enumBitWidth: serializedEnumBitWidth,
235
- opaqueTypes,
246
+ opaqueTypes: options?.opaqueTypes,
247
+ typedefStructTypes: options?.typedefStructTypes,
248
+ structTagAliases: options?.structTagAliases,
249
+ structTagsWithBodies: options?.structTagsWithBodies,
236
250
  };
237
251
 
238
252
  this.cache.setKey(filePath, entry);
@@ -273,15 +287,22 @@ class CacheManager {
273
287
  // Issue #948: Extract opaque types (forward-declared structs)
274
288
  const opaqueTypes = symbolTable.getAllOpaqueTypes();
275
289
 
290
+ // Issue #958: Extract typedef struct types (all typedef'd structs)
291
+ const typedefStructTypes = symbolTable.getAllTypedefStructTypes();
292
+
293
+ // Issue #958: Extract struct tag aliases and body tracking
294
+ const structTagAliases = symbolTable.getAllStructTagAliases();
295
+ const structTagsWithBodies = symbolTable.getAllStructTagsWithBodies();
296
+
276
297
  // Delegate to existing setSymbols method
277
- this.setSymbols(
278
- filePath,
279
- symbols,
280
- structFields,
298
+ this.setSymbols(filePath, symbols, structFields, {
281
299
  needsStructKeyword,
282
300
  enumBitWidth,
283
301
  opaqueTypes,
284
- );
302
+ typedefStructTypes,
303
+ structTagAliases,
304
+ structTagsWithBodies,
305
+ });
285
306
  }
286
307
 
287
308
  /**
@@ -361,7 +361,9 @@ describe("CacheManager", () => {
361
361
  const testFile = join(testDir, "test.h");
362
362
  writeFileSync(testFile, "// test");
363
363
 
364
- cacheManager.setSymbols(testFile, [], new Map(), ["Point", "Rectangle"]);
364
+ cacheManager.setSymbols(testFile, [], new Map(), {
365
+ needsStructKeyword: ["Point", "Rectangle"],
366
+ });
365
367
 
366
368
  const cached = cacheManager.getSymbols(testFile);
367
369
  expect(cached!.needsStructKeyword).toEqual(["Point", "Rectangle"]);
@@ -391,7 +393,7 @@ describe("CacheManager", () => {
391
393
  enumBitWidth.set("Status", 8);
392
394
  enumBitWidth.set("Mode", 16);
393
395
 
394
- cacheManager.setSymbols(testFile, [], new Map(), undefined, enumBitWidth);
396
+ cacheManager.setSymbols(testFile, [], new Map(), { enumBitWidth });
395
397
 
396
398
  const cached = cacheManager.getSymbols(testFile);
397
399
  expect(cached!.enumBitWidth.get("Status")).toBe(8);
@@ -405,7 +407,7 @@ describe("CacheManager", () => {
405
407
  const enumBitWidth = new Map<string, number>();
406
408
  enumBitWidth.set("Priority", 32);
407
409
 
408
- cacheManager.setSymbols(testFile, [], new Map(), undefined, enumBitWidth);
410
+ cacheManager.setSymbols(testFile, [], new Map(), { enumBitWidth });
409
411
  await cacheManager.flush();
410
412
 
411
413
  // Reload
@@ -1492,7 +1494,7 @@ describe("CacheManager", () => {
1492
1494
  const content = mockFs.getWrittenContent("/project/.cnx/config.json");
1493
1495
  expect(content).toBeDefined();
1494
1496
  const newConfig = JSON.parse(content!);
1495
- expect(newConfig.version).toBe(5); // Current CACHE_VERSION (Issue #948 opaqueTypes)
1497
+ expect(newConfig.version).toBe(7); // Current CACHE_VERSION (Issue #958 structTagAliases, structTagsWithBodies)
1496
1498
  });
1497
1499
 
1498
1500
  it("should not cache files that do not exist in IFileSystem", async () => {