c-next 0.2.11 → 0.2.13
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 +80 -649
- package/dist/index.js +8273 -6357
- package/dist/index.js.map +4 -4
- package/grammar/C.g4 +17 -4
- package/package.json +3 -3
- package/src/__tests__/index.test.ts +1 -1
- package/src/cli/CleanCommand.ts +8 -12
- package/src/cli/Cli.ts +29 -6
- package/src/cli/Runner.ts +42 -62
- package/src/cli/__tests__/CleanCommand.test.ts +10 -10
- package/src/cli/__tests__/Cli.test.ts +59 -7
- package/src/cli/__tests__/ConfigPrinter.test.ts +12 -12
- package/src/cli/__tests__/PathNormalizer.test.ts +5 -5
- package/src/cli/__tests__/Runner.test.ts +108 -82
- package/src/cli/serve/ServeCommand.ts +1 -1
- package/src/cli/types/ICliConfig.ts +2 -2
- package/src/lib/parseWithSymbols.ts +21 -21
- package/src/transpiler/Transpiler.ts +99 -46
- package/src/transpiler/__tests__/DualCodePaths.test.ts +29 -29
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +244 -72
- package/src/transpiler/__tests__/Transpiler.test.ts +32 -72
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +30 -28
- package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +1 -1
- package/src/transpiler/data/CNextMarkerDetector.ts +34 -0
- package/src/transpiler/data/CppEntryPointScanner.ts +174 -0
- package/src/transpiler/data/FileDiscovery.ts +2 -105
- package/src/transpiler/data/InputExpansion.ts +37 -81
- package/src/transpiler/data/__tests__/CNextMarkerDetector.test.ts +62 -0
- package/src/transpiler/data/__tests__/CppEntryPointScanner.test.ts +239 -0
- package/src/transpiler/data/__tests__/FileDiscovery.test.ts +45 -191
- package/src/transpiler/data/__tests__/InputExpansion.test.ts +36 -204
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +2 -2
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +4 -5
- package/src/transpiler/logic/parser/c/grammar/C.interp +33 -3
- package/src/transpiler/logic/parser/c/grammar/C.tokens +237 -207
- package/src/transpiler/logic/parser/c/grammar/CLexer.interp +48 -3
- package/src/transpiler/logic/parser/c/grammar/CLexer.tokens +237 -207
- package/src/transpiler/logic/parser/c/grammar/CLexer.ts +702 -611
- package/src/transpiler/logic/parser/c/grammar/CParser.ts +1221 -1107
- package/src/transpiler/logic/symbols/SymbolTable.ts +147 -73
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +157 -14
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +3 -3
- package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +7 -37
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +6 -6
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +28 -27
- package/src/transpiler/logic/symbols/cnext/index.ts +4 -4
- package/src/transpiler/logic/symbols/cnext/utils/SymbolNameUtils.ts +5 -5
- package/src/transpiler/output/codegen/CodeGenerator.ts +16 -1
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +15 -0
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +14 -14
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +15 -3
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +14 -6
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +1 -0
- package/src/transpiler/output/codegen/types/IFunctionContextCallbacks.ts +2 -0
- package/src/transpiler/output/codegen/utils/QualifiedNameGenerator.ts +7 -7
- package/src/transpiler/output/codegen/utils/__tests__/QualifiedNameGenerator.test.ts +3 -3
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +10 -1
- package/src/transpiler/output/headers/HeaderGenerator.ts +3 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +6 -2
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +16 -0
- package/src/transpiler/output/headers/adapters/HeaderSymbolAdapter.ts +19 -19
- package/src/transpiler/output/headers/adapters/__tests__/HeaderSymbolAdapter.test.ts +5 -5
- package/src/transpiler/state/SymbolRegistry.ts +10 -12
- package/src/transpiler/state/__tests__/SymbolRegistry.test.ts +11 -13
- package/src/transpiler/types/ICachedFileEntry.ts +4 -0
- package/src/transpiler/types/IPipelineFile.ts +3 -0
- package/src/transpiler/types/ITranspilerConfig.ts +2 -2
- package/src/transpiler/types/symbols/IScopeSymbol.ts +1 -1
- package/src/utils/FunctionUtils.ts +3 -3
- package/src/utils/__tests__/FunctionUtils.test.ts +6 -4
- package/src/utils/cache/CacheManager.ts +28 -15
- package/src/utils/cache/__tests__/CacheManager.test.ts +6 -4
- package/src/transpiler/data/types/IDiscoveryOptions.ts +0 -15
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - TCppSymbol: C++ header symbols (string types)
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { produce, enableMapSet } from "immer";
|
|
11
12
|
import ESourceLanguage from "../../../utils/types/ESourceLanguage";
|
|
12
13
|
import LiteralUtils from "../../../utils/LiteralUtils";
|
|
13
14
|
import IConflict from "../../types/IConflict";
|
|
@@ -23,6 +24,38 @@ import IVariableSymbol from "../../types/symbols/IVariableSymbol";
|
|
|
23
24
|
import TypeResolver from "../../../utils/TypeResolver";
|
|
24
25
|
import SymbolNameUtils from "./cnext/utils/SymbolNameUtils";
|
|
25
26
|
|
|
27
|
+
// Enable immer support for Map and Set (must be called once at module scope)
|
|
28
|
+
enableMapSet();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Issue #958: Immutable struct symbol state managed via immer produce().
|
|
32
|
+
* All mutations are additive-only — no unmark/delete operations.
|
|
33
|
+
* Resolution (e.g., "is this type truly opaque?") happens at query time.
|
|
34
|
+
*/
|
|
35
|
+
interface IStructSymbolState {
|
|
36
|
+
/** Typedef names declared with forward-declared structs (additive only) */
|
|
37
|
+
opaqueTypes: Set<string>;
|
|
38
|
+
/** ALL typedef struct types from C headers: name → sourceFile (additive only) */
|
|
39
|
+
typedefStructTypes: Map<string, string>;
|
|
40
|
+
/** Struct tag → typedef name (e.g., "_widget_t" → "widget_t") */
|
|
41
|
+
structTagAliases: Map<string, string>;
|
|
42
|
+
/** Typedef name → struct tag (reverse of structTagAliases) */
|
|
43
|
+
typedefToTag: Map<string, string>;
|
|
44
|
+
/** Struct tags that have full definitions (bodies) */
|
|
45
|
+
structTagsWithBodies: Set<string>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Create a fresh initial struct symbol state */
|
|
49
|
+
function createInitialStructState(): IStructSymbolState {
|
|
50
|
+
return {
|
|
51
|
+
opaqueTypes: new Set(),
|
|
52
|
+
typedefStructTypes: new Map(),
|
|
53
|
+
structTagAliases: new Map(),
|
|
54
|
+
typedefToTag: new Map(),
|
|
55
|
+
structTagsWithBodies: new Set(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
26
59
|
/**
|
|
27
60
|
* Central symbol table for cross-language interoperability
|
|
28
61
|
*
|
|
@@ -78,27 +111,10 @@ class SymbolTable {
|
|
|
78
111
|
private readonly needsStructKeyword: Set<string> = new Set();
|
|
79
112
|
|
|
80
113
|
/**
|
|
81
|
-
* Issue #
|
|
82
|
-
*
|
|
114
|
+
* Issue #958: Immutable struct symbol state — additive only, query-time resolution.
|
|
115
|
+
* Replaces separate opaqueTypes, typedefStructTypes, structTagAliases fields.
|
|
83
116
|
*/
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Issue #958: Track typedef struct type names with their source files.
|
|
88
|
-
* Maps typeName -> sourceFile. Used for scope variables which should be pointers
|
|
89
|
-
* when the struct definition comes from a different file than the typedef.
|
|
90
|
-
* If definition is in the same file as typedef, the entry is removed (value type).
|
|
91
|
-
* If definition is in a different file, the entry remains (pointer type).
|
|
92
|
-
*/
|
|
93
|
-
private readonly typedefStructTypes: Map<string, string> = new Map();
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Issue #948: Track struct tag -> typedef name relationships.
|
|
97
|
-
* When a typedef declares an alias for a struct tag (e.g., typedef struct _foo foo_t),
|
|
98
|
-
* we record structTagAliases["_foo"] = "foo_t". This allows us to unmark the typedef
|
|
99
|
-
* as opaque when the full struct definition is later found.
|
|
100
|
-
*/
|
|
101
|
-
private readonly structTagAliases: Map<string, string> = new Map();
|
|
117
|
+
private structState: IStructSymbolState = createInitialStructState();
|
|
102
118
|
|
|
103
119
|
/**
|
|
104
120
|
* Issue #208: Track enum backing type bit widths
|
|
@@ -141,7 +157,7 @@ class SymbolTable {
|
|
|
141
157
|
* Called automatically when adding struct symbols.
|
|
142
158
|
*/
|
|
143
159
|
private registerStructFields(struct: IStructSymbol): void {
|
|
144
|
-
const
|
|
160
|
+
const cName = SymbolNameUtils.getTranspiledCName(struct);
|
|
145
161
|
|
|
146
162
|
for (const [fieldName, fieldInfo] of struct.fields) {
|
|
147
163
|
// Convert TType to string for structFields map
|
|
@@ -153,7 +169,7 @@ class SymbolTable {
|
|
|
153
169
|
);
|
|
154
170
|
|
|
155
171
|
this.addStructField(
|
|
156
|
-
|
|
172
|
+
cName,
|
|
157
173
|
fieldName,
|
|
158
174
|
typeString,
|
|
159
175
|
numericDims && numericDims.length > 0 ? numericDims : undefined,
|
|
@@ -635,17 +651,29 @@ class SymbolTable {
|
|
|
635
651
|
(s) => s.sourceLanguage === ESourceLanguage.Cpp,
|
|
636
652
|
);
|
|
637
653
|
|
|
638
|
-
|
|
639
|
-
|
|
654
|
+
// Issue #967: Only global-scope C-Next symbols can conflict with C/C++ symbols.
|
|
655
|
+
// Scoped symbols (e.g., Touch.read) live in a namespace and don't compete
|
|
656
|
+
// with C's global symbols (e.g., POSIX read()).
|
|
657
|
+
const conflictingCnextDefs = cnextDefs.filter((s) => {
|
|
658
|
+
const tSymbol = s as TSymbol;
|
|
659
|
+
return tSymbol.scope.name === "";
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
if (
|
|
663
|
+
conflictingCnextDefs.length > 0 &&
|
|
664
|
+
(cDefs.length > 0 || cppDefs.length > 0)
|
|
665
|
+
) {
|
|
666
|
+
const conflictingDefs = [...conflictingCnextDefs, ...cDefs, ...cppDefs];
|
|
667
|
+
const locations = conflictingDefs.map(
|
|
640
668
|
(s) =>
|
|
641
669
|
`${s.sourceLanguage.toUpperCase()} (${s.sourceFile}:${s.sourceLine})`,
|
|
642
670
|
);
|
|
643
671
|
|
|
644
672
|
return {
|
|
645
|
-
symbolName:
|
|
646
|
-
definitions:
|
|
673
|
+
symbolName: conflictingDefs[0].name,
|
|
674
|
+
definitions: conflictingDefs,
|
|
647
675
|
severity: "error",
|
|
648
|
-
message: `Symbol conflict: '${
|
|
676
|
+
message: `Symbol conflict: '${conflictingDefs[0].name}' is defined in multiple languages:\n ${locations.join("\n ")}\nRename the C-Next symbol to resolve.`,
|
|
649
677
|
};
|
|
650
678
|
}
|
|
651
679
|
|
|
@@ -879,7 +907,7 @@ class SymbolTable {
|
|
|
879
907
|
}
|
|
880
908
|
|
|
881
909
|
// ========================================================================
|
|
882
|
-
//
|
|
910
|
+
// Struct Symbol State (Issue #948, #958) — immer-managed, additive only
|
|
883
911
|
// ========================================================================
|
|
884
912
|
|
|
885
913
|
/**
|
|
@@ -887,33 +915,32 @@ class SymbolTable {
|
|
|
887
915
|
* @param typeName Typedef name (e.g., "widget_t")
|
|
888
916
|
*/
|
|
889
917
|
markOpaqueType(typeName: string): void {
|
|
890
|
-
this.
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
/**
|
|
894
|
-
* Issue #948: Unmark a typedef when full struct definition is found.
|
|
895
|
-
* Handles edge case: typedef before definition.
|
|
896
|
-
* @param typeName Typedef name
|
|
897
|
-
*/
|
|
898
|
-
unmarkOpaqueType(typeName: string): void {
|
|
899
|
-
this.opaqueTypes.delete(typeName);
|
|
918
|
+
this.structState = produce(this.structState, (draft) => {
|
|
919
|
+
draft.opaqueTypes.add(typeName);
|
|
920
|
+
});
|
|
900
921
|
}
|
|
901
922
|
|
|
902
923
|
/**
|
|
903
|
-
* Issue #948: Check if a typedef aliases
|
|
924
|
+
* Issue #948/#958: Check if a typedef aliases a truly opaque struct type.
|
|
925
|
+
* Query-time resolution: if the underlying struct tag has a body, it's not opaque.
|
|
904
926
|
* @param typeName Typedef name
|
|
905
|
-
* @returns true if the type is opaque (forward-declared)
|
|
927
|
+
* @returns true if the type is opaque (forward-declared with no body found)
|
|
906
928
|
*/
|
|
907
929
|
isOpaqueType(typeName: string): boolean {
|
|
908
|
-
|
|
930
|
+
if (!this.structState.opaqueTypes.has(typeName)) return false;
|
|
931
|
+
// Resolve: if the underlying struct tag has a body, it's not truly opaque
|
|
932
|
+
const tag = this.structState.typedefToTag.get(typeName);
|
|
933
|
+
if (tag && this.structState.structTagsWithBodies.has(tag)) return false;
|
|
934
|
+
return true;
|
|
909
935
|
}
|
|
910
936
|
|
|
911
937
|
/**
|
|
912
938
|
* Issue #948: Get all opaque type names for cache serialization.
|
|
939
|
+
* Returns the raw set — resolution happens at query time via isOpaqueType().
|
|
913
940
|
* @returns Array of opaque typedef names
|
|
914
941
|
*/
|
|
915
942
|
getAllOpaqueTypes(): string[] {
|
|
916
|
-
return Array.from(this.opaqueTypes);
|
|
943
|
+
return Array.from(this.structState.opaqueTypes);
|
|
917
944
|
}
|
|
918
945
|
|
|
919
946
|
/**
|
|
@@ -921,20 +948,25 @@ class SymbolTable {
|
|
|
921
948
|
* @param typeNames Array of opaque typedef names
|
|
922
949
|
*/
|
|
923
950
|
restoreOpaqueTypes(typeNames: string[]): void {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
951
|
+
this.structState = produce(this.structState, (draft) => {
|
|
952
|
+
for (const name of typeNames) {
|
|
953
|
+
draft.opaqueTypes.add(name);
|
|
954
|
+
}
|
|
955
|
+
});
|
|
927
956
|
}
|
|
928
957
|
|
|
929
958
|
/**
|
|
930
959
|
* Issue #948: Register a struct tag -> typedef name relationship.
|
|
931
960
|
* Called when processing: typedef struct _foo foo_t;
|
|
932
|
-
*
|
|
961
|
+
* Populates both forward (tag→typedef) and reverse (typedef→tag) maps.
|
|
933
962
|
* @param structTag The struct tag name (e.g., "_foo")
|
|
934
963
|
* @param typedefName The typedef alias name (e.g., "foo_t")
|
|
935
964
|
*/
|
|
936
965
|
registerStructTagAlias(structTag: string, typedefName: string): void {
|
|
937
|
-
this.
|
|
966
|
+
this.structState = produce(this.structState, (draft) => {
|
|
967
|
+
draft.structTagAliases.set(structTag, typedefName);
|
|
968
|
+
draft.typedefToTag.set(typedefName, structTag);
|
|
969
|
+
});
|
|
938
970
|
}
|
|
939
971
|
|
|
940
972
|
/**
|
|
@@ -943,7 +975,59 @@ class SymbolTable {
|
|
|
943
975
|
* @returns The typedef alias name, or undefined if none registered
|
|
944
976
|
*/
|
|
945
977
|
getStructTagAlias(structTag: string): string | undefined {
|
|
946
|
-
return this.structTagAliases.get(structTag);
|
|
978
|
+
return this.structState.structTagAliases.get(structTag);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Issue #958: Record that a struct tag has a full definition (body).
|
|
983
|
+
* Used by query-time resolution: opaque types with bodies are not truly opaque.
|
|
984
|
+
* @param structTag The struct tag name (e.g., "_widget_t")
|
|
985
|
+
*/
|
|
986
|
+
markStructTagHasBody(structTag: string): void {
|
|
987
|
+
this.structState = produce(this.structState, (draft) => {
|
|
988
|
+
draft.structTagsWithBodies.add(structTag);
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Issue #958: Get all struct tags with bodies for cache serialization.
|
|
994
|
+
* @returns Array of struct tag names
|
|
995
|
+
*/
|
|
996
|
+
getAllStructTagsWithBodies(): string[] {
|
|
997
|
+
return Array.from(this.structState.structTagsWithBodies);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Issue #958: Restore struct tags with bodies from cache.
|
|
1002
|
+
* @param tags Array of struct tag names
|
|
1003
|
+
*/
|
|
1004
|
+
restoreStructTagsWithBodies(tags: string[]): void {
|
|
1005
|
+
this.structState = produce(this.structState, (draft) => {
|
|
1006
|
+
for (const tag of tags) {
|
|
1007
|
+
draft.structTagsWithBodies.add(tag);
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Issue #958: Get all struct tag aliases for cache serialization.
|
|
1014
|
+
* @returns Array of [structTag, typedefName] pairs
|
|
1015
|
+
*/
|
|
1016
|
+
getAllStructTagAliases(): Array<[string, string]> {
|
|
1017
|
+
return Array.from(this.structState.structTagAliases.entries());
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Issue #958: Restore struct tag aliases from cache.
|
|
1022
|
+
* @param entries Array of [structTag, typedefName] pairs
|
|
1023
|
+
*/
|
|
1024
|
+
restoreStructTagAliases(entries: Array<[string, string]>): void {
|
|
1025
|
+
this.structState = produce(this.structState, (draft) => {
|
|
1026
|
+
for (const [tag, typedefName] of entries) {
|
|
1027
|
+
draft.structTagAliases.set(tag, typedefName);
|
|
1028
|
+
draft.typedefToTag.set(typedefName, tag);
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
947
1031
|
}
|
|
948
1032
|
|
|
949
1033
|
// ========================================================================
|
|
@@ -952,36 +1036,26 @@ class SymbolTable {
|
|
|
952
1036
|
|
|
953
1037
|
/**
|
|
954
1038
|
* Issue #958: Mark a typedef as aliasing a struct type.
|
|
955
|
-
* Records the source file
|
|
1039
|
+
* Records the source file. Additive only — never removed.
|
|
956
1040
|
* @param typedefName The typedef name (e.g., "widget_t")
|
|
957
1041
|
* @param sourceFile The file where the typedef was declared
|
|
958
1042
|
*/
|
|
959
1043
|
markTypedefStructType(typedefName: string, sourceFile: string): void {
|
|
960
|
-
this.
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
/**
|
|
964
|
-
* Issue #958: Unmark a typedef struct type when full definition is found.
|
|
965
|
-
* Only unmarks if the definition is in the SAME file as the typedef declaration.
|
|
966
|
-
* Cross-file definitions keep the typedef marked (pointer semantics).
|
|
967
|
-
* @param typeName The typedef name to unmark
|
|
968
|
-
* @param sourceFile The file where the definition was found
|
|
969
|
-
*/
|
|
970
|
-
unmarkTypedefStructType(typeName: string, sourceFile: string): void {
|
|
971
|
-
const typedefFile = this.typedefStructTypes.get(typeName);
|
|
972
|
-
if (typedefFile === sourceFile) {
|
|
973
|
-
this.typedefStructTypes.delete(typeName);
|
|
974
|
-
}
|
|
1044
|
+
this.structState = produce(this.structState, (draft) => {
|
|
1045
|
+
draft.typedefStructTypes.set(typedefName, sourceFile);
|
|
1046
|
+
});
|
|
975
1047
|
}
|
|
976
1048
|
|
|
977
1049
|
/**
|
|
978
1050
|
* Issue #958: Check if a typedef aliases a struct type.
|
|
979
|
-
* Used for scope variables
|
|
1051
|
+
* Used for scope variables, function parameters, and local variables
|
|
1052
|
+
* which should be pointers for C-header struct types.
|
|
980
1053
|
* @param typeName The type name to check
|
|
981
1054
|
* @returns true if this is a typedef'd struct type from C headers
|
|
982
1055
|
*/
|
|
983
1056
|
isTypedefStructType(typeName: string): boolean {
|
|
984
|
-
|
|
1057
|
+
const result = this.structState.typedefStructTypes.has(typeName);
|
|
1058
|
+
return result;
|
|
985
1059
|
}
|
|
986
1060
|
|
|
987
1061
|
/**
|
|
@@ -989,7 +1063,7 @@ class SymbolTable {
|
|
|
989
1063
|
* @returns Map entries as [typeName, sourceFile] pairs
|
|
990
1064
|
*/
|
|
991
1065
|
getAllTypedefStructTypes(): Array<[string, string]> {
|
|
992
|
-
return Array.from(this.typedefStructTypes.entries());
|
|
1066
|
+
return Array.from(this.structState.typedefStructTypes.entries());
|
|
993
1067
|
}
|
|
994
1068
|
|
|
995
1069
|
/**
|
|
@@ -997,9 +1071,11 @@ class SymbolTable {
|
|
|
997
1071
|
* @param entries Array of [typeName, sourceFile] pairs
|
|
998
1072
|
*/
|
|
999
1073
|
restoreTypedefStructTypes(entries: Array<[string, string]>): void {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1074
|
+
this.structState = produce(this.structState, (draft) => {
|
|
1075
|
+
for (const [name, sourceFile] of entries) {
|
|
1076
|
+
draft.typedefStructTypes.set(name, sourceFile);
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1003
1079
|
}
|
|
1004
1080
|
|
|
1005
1081
|
// ========================================================================
|
|
@@ -1151,10 +1227,8 @@ class SymbolTable {
|
|
|
1151
1227
|
// Auxiliary
|
|
1152
1228
|
this.structFields.clear();
|
|
1153
1229
|
this.needsStructKeyword.clear();
|
|
1154
|
-
this.
|
|
1155
|
-
this.structTagAliases.clear();
|
|
1230
|
+
this.structState = createInitialStructState();
|
|
1156
1231
|
this.enumBitWidth.clear();
|
|
1157
|
-
this.typedefStructTypes.clear();
|
|
1158
1232
|
}
|
|
1159
1233
|
}
|
|
1160
1234
|
|
|
@@ -478,6 +478,84 @@ describe("SymbolTable", () => {
|
|
|
478
478
|
// Two globals with same name IS a conflict
|
|
479
479
|
expect(symbolTable.hasConflict("globalVar")).toBe(true);
|
|
480
480
|
});
|
|
481
|
+
|
|
482
|
+
// Issue #967: Scoped C-Next symbols live in a namespace and don't conflict
|
|
483
|
+
// with C's global symbols. Only global-scope C-Next symbols can conflict.
|
|
484
|
+
it("should NOT detect conflict for scoped C-Next method vs C function with same bare name", () => {
|
|
485
|
+
const globalScope = TestScopeUtils.createMockGlobalScope();
|
|
486
|
+
const touchScope = TestScopeUtils.createMockScope("Touch", globalScope);
|
|
487
|
+
|
|
488
|
+
// Add C-Next scoped function 'read' in scope 'Touch'
|
|
489
|
+
// This transpiles to Touch_read()
|
|
490
|
+
symbolTable.addTSymbol({
|
|
491
|
+
kind: "function",
|
|
492
|
+
name: "read",
|
|
493
|
+
sourceFile: "touch.cnx",
|
|
494
|
+
sourceLine: 28,
|
|
495
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
496
|
+
isExported: true,
|
|
497
|
+
returnType: TTypeUtils.createPrimitive("u8"),
|
|
498
|
+
parameters: [],
|
|
499
|
+
scope: touchScope,
|
|
500
|
+
visibility: "public",
|
|
501
|
+
body: null,
|
|
502
|
+
} as IFunctionSymbol);
|
|
503
|
+
|
|
504
|
+
// Add C function 'read' from POSIX headers
|
|
505
|
+
// This stays as read()
|
|
506
|
+
symbolTable.addCSymbol({
|
|
507
|
+
kind: "function",
|
|
508
|
+
name: "read",
|
|
509
|
+
sourceFile: "lv_pthread.h",
|
|
510
|
+
sourceLine: 80,
|
|
511
|
+
sourceLanguage: ESourceLanguage.C,
|
|
512
|
+
isExported: true,
|
|
513
|
+
type: "ssize_t",
|
|
514
|
+
parameters: [
|
|
515
|
+
{ name: "fd", type: "int", isConst: false, isArray: false },
|
|
516
|
+
{ name: "buf", type: "void*", isConst: false, isArray: false },
|
|
517
|
+
{ name: "count", type: "size_t", isConst: false, isArray: false },
|
|
518
|
+
],
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Touch.read() is in a namespace — does NOT conflict with C's global read()
|
|
522
|
+
expect(symbolTable.hasConflict("read")).toBe(false);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Issue #967: Global C-Next functions SHOULD still conflict with C functions
|
|
526
|
+
it("should detect conflict for global C-Next function vs C function", () => {
|
|
527
|
+
const globalScope = TestScopeUtils.createMockGlobalScope();
|
|
528
|
+
|
|
529
|
+
// Add global C-Next function 'read'
|
|
530
|
+
symbolTable.addTSymbol({
|
|
531
|
+
kind: "function",
|
|
532
|
+
name: "read",
|
|
533
|
+
sourceFile: "utils.cnx",
|
|
534
|
+
sourceLine: 5,
|
|
535
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
536
|
+
isExported: true,
|
|
537
|
+
returnType: TTypeUtils.createPrimitive("u8"),
|
|
538
|
+
parameters: [],
|
|
539
|
+
scope: globalScope,
|
|
540
|
+
visibility: "public",
|
|
541
|
+
body: null,
|
|
542
|
+
} as IFunctionSymbol);
|
|
543
|
+
|
|
544
|
+
// Add C function 'read'
|
|
545
|
+
symbolTable.addCSymbol({
|
|
546
|
+
kind: "function",
|
|
547
|
+
name: "read",
|
|
548
|
+
sourceFile: "unistd.h",
|
|
549
|
+
sourceLine: 100,
|
|
550
|
+
sourceLanguage: ESourceLanguage.C,
|
|
551
|
+
isExported: true,
|
|
552
|
+
type: "ssize_t",
|
|
553
|
+
parameters: [],
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Global C-Next read() DOES conflict with C's read()
|
|
557
|
+
expect(symbolTable.hasConflict("read")).toBe(true);
|
|
558
|
+
});
|
|
481
559
|
});
|
|
482
560
|
|
|
483
561
|
// ========================================================================
|
|
@@ -621,13 +699,25 @@ describe("SymbolTable", () => {
|
|
|
621
699
|
expect(symbolTable.isOpaqueType("other_t")).toBe(false);
|
|
622
700
|
});
|
|
623
701
|
|
|
624
|
-
it("should
|
|
702
|
+
it("should resolve opaque type as non-opaque when struct tag has body", () => {
|
|
703
|
+
// typedef struct _point point_t; → mark opaque + register alias
|
|
625
704
|
symbolTable.markOpaqueType("point_t");
|
|
705
|
+
symbolTable.registerStructTagAlias("_point", "point_t");
|
|
626
706
|
expect(symbolTable.isOpaqueType("point_t")).toBe(true);
|
|
627
|
-
|
|
707
|
+
|
|
708
|
+
// struct _point { int x; int y; }; → mark body
|
|
709
|
+
symbolTable.markStructTagHasBody("_point");
|
|
710
|
+
// Query-time resolution: no longer opaque
|
|
628
711
|
expect(symbolTable.isOpaqueType("point_t")).toBe(false);
|
|
629
712
|
});
|
|
630
713
|
|
|
714
|
+
it("should keep opaque type when no body found for struct tag", () => {
|
|
715
|
+
symbolTable.markOpaqueType("handle_t");
|
|
716
|
+
symbolTable.registerStructTagAlias("_handle", "handle_t");
|
|
717
|
+
// No markStructTagHasBody call → stays opaque
|
|
718
|
+
expect(symbolTable.isOpaqueType("handle_t")).toBe(true);
|
|
719
|
+
});
|
|
720
|
+
|
|
631
721
|
it("should get all opaque types", () => {
|
|
632
722
|
symbolTable.markOpaqueType("handle_t");
|
|
633
723
|
symbolTable.markOpaqueType("context_t");
|
|
@@ -662,20 +752,14 @@ describe("SymbolTable", () => {
|
|
|
662
752
|
expect(symbolTable.isTypedefStructType("other_t")).toBe(false);
|
|
663
753
|
});
|
|
664
754
|
|
|
665
|
-
it("should
|
|
755
|
+
it("should always return true for typedef struct types (additive only)", () => {
|
|
756
|
+
// Issue #958: typedef struct types are never unmarked
|
|
666
757
|
symbolTable.markTypedefStructType("point_t", "point.h");
|
|
667
758
|
expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
|
|
668
|
-
//
|
|
669
|
-
symbolTable.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
it("should retain typedef struct type when definition is in different file", () => {
|
|
674
|
-
symbolTable.markTypedefStructType("widget_t", "widget_types.h");
|
|
675
|
-
expect(symbolTable.isTypedefStructType("widget_t")).toBe(true);
|
|
676
|
-
// Different file - should NOT unmark (cross-file pattern)
|
|
677
|
-
symbolTable.unmarkTypedefStructType("widget_t", "widget_private.h");
|
|
678
|
-
expect(symbolTable.isTypedefStructType("widget_t")).toBe(true);
|
|
759
|
+
// Even after marking the body, typedef struct type stays marked
|
|
760
|
+
symbolTable.registerStructTagAlias("_point", "point_t");
|
|
761
|
+
symbolTable.markStructTagHasBody("_point");
|
|
762
|
+
expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
|
|
679
763
|
});
|
|
680
764
|
|
|
681
765
|
it("should get all typedef struct types", () => {
|
|
@@ -704,6 +788,65 @@ describe("SymbolTable", () => {
|
|
|
704
788
|
});
|
|
705
789
|
});
|
|
706
790
|
|
|
791
|
+
// ========================================================================
|
|
792
|
+
// Struct Tag Aliases and Body Tracking (Issue #958)
|
|
793
|
+
// ========================================================================
|
|
794
|
+
|
|
795
|
+
describe("Struct Tag Aliases and Body Tracking", () => {
|
|
796
|
+
it("should register and retrieve struct tag aliases", () => {
|
|
797
|
+
symbolTable.registerStructTagAlias("_widget", "widget_t");
|
|
798
|
+
expect(symbolTable.getStructTagAlias("_widget")).toBe("widget_t");
|
|
799
|
+
expect(symbolTable.getStructTagAlias("_unknown")).toBeUndefined();
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it("should populate forward and reverse alias maps", () => {
|
|
803
|
+
symbolTable.registerStructTagAlias("_foo", "foo_t");
|
|
804
|
+
const aliases = symbolTable.getAllStructTagAliases();
|
|
805
|
+
expect(aliases).toContainEqual(["_foo", "foo_t"]);
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it("should track struct tags with bodies", () => {
|
|
809
|
+
symbolTable.markStructTagHasBody("_widget");
|
|
810
|
+
const bodies = symbolTable.getAllStructTagsWithBodies();
|
|
811
|
+
expect(bodies).toContain("_widget");
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
it("should restore struct tag aliases from cache", () => {
|
|
815
|
+
symbolTable.restoreStructTagAliases([
|
|
816
|
+
["_foo", "foo_t"],
|
|
817
|
+
["_bar", "bar_t"],
|
|
818
|
+
]);
|
|
819
|
+
expect(symbolTable.getStructTagAlias("_foo")).toBe("foo_t");
|
|
820
|
+
expect(symbolTable.getStructTagAlias("_bar")).toBe("bar_t");
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it("should restore reverse map (typedefToTag) so isOpaqueType resolves after cache restore", () => {
|
|
824
|
+
// Simulate cache restore: aliases + opaque types + bodies
|
|
825
|
+
symbolTable.restoreStructTagAliases([["_widget", "widget_t"]]);
|
|
826
|
+
symbolTable.restoreOpaqueTypes(["widget_t"]);
|
|
827
|
+
// widget_t is opaque (no body for _widget)
|
|
828
|
+
expect(symbolTable.isOpaqueType("widget_t")).toBe(true);
|
|
829
|
+
// Now restore body — isOpaqueType should resolve via typedefToTag
|
|
830
|
+
symbolTable.restoreStructTagsWithBodies(["_widget"]);
|
|
831
|
+
expect(symbolTable.isOpaqueType("widget_t")).toBe(false);
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
it("should restore struct tags with bodies from cache", () => {
|
|
835
|
+
symbolTable.restoreStructTagsWithBodies(["_foo", "_bar"]);
|
|
836
|
+
const bodies = symbolTable.getAllStructTagsWithBodies();
|
|
837
|
+
expect(bodies).toContain("_foo");
|
|
838
|
+
expect(bodies).toContain("_bar");
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
it("should clear all struct state on clear()", () => {
|
|
842
|
+
symbolTable.registerStructTagAlias("_foo", "foo_t");
|
|
843
|
+
symbolTable.markStructTagHasBody("_foo");
|
|
844
|
+
symbolTable.clear();
|
|
845
|
+
expect(symbolTable.getStructTagAlias("_foo")).toBeUndefined();
|
|
846
|
+
expect(symbolTable.getAllStructTagsWithBodies()).toHaveLength(0);
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
|
|
707
850
|
// ========================================================================
|
|
708
851
|
// Clear
|
|
709
852
|
// ========================================================================
|
|
@@ -664,7 +664,7 @@ describe("CResolver - Opaque Type Detection (Issue #948)", () => {
|
|
|
664
664
|
expect(symbolTable.isOpaqueType("point_t")).toBe(false);
|
|
665
665
|
});
|
|
666
666
|
|
|
667
|
-
it("
|
|
667
|
+
it("resolves opaque type as non-opaque when full definition is found in separate parse", () => {
|
|
668
668
|
// Forward declaration first
|
|
669
669
|
const tree1 = TestHelpers.parseC(`typedef struct _foo foo_t;`);
|
|
670
670
|
const symbolTable = new SymbolTable();
|
|
@@ -672,14 +672,14 @@ describe("CResolver - Opaque Type Detection (Issue #948)", () => {
|
|
|
672
672
|
|
|
673
673
|
expect(symbolTable.isOpaqueType("foo_t")).toBe(true);
|
|
674
674
|
|
|
675
|
-
// Full definition later
|
|
675
|
+
// Full definition later — query-time resolution sees body
|
|
676
676
|
const tree2 = TestHelpers.parseC(`struct _foo { int value; };`);
|
|
677
677
|
CResolver.resolve(tree2!, "test.h", symbolTable);
|
|
678
678
|
|
|
679
679
|
expect(symbolTable.isOpaqueType("foo_t")).toBe(false);
|
|
680
680
|
});
|
|
681
681
|
|
|
682
|
-
it("
|
|
682
|
+
it("resolves opaque type as non-opaque when full definition follows typedef in same parse", () => {
|
|
683
683
|
const tree = TestHelpers.parseC(`
|
|
684
684
|
typedef struct _point_t point_t;
|
|
685
685
|
struct _point_t { int x; int y; };
|
|
@@ -137,47 +137,17 @@ class StructCollector {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
// Issue #958: Track
|
|
141
|
-
//
|
|
140
|
+
// Issue #958: Track forward-declared typedef struct types (no body).
|
|
141
|
+
// These always need pointer semantics (ADR-006). Inline typedefs with
|
|
142
|
+
// bodies (typedef struct { ... } X;) are concrete value types.
|
|
142
143
|
// Issue #957: Don't track pointer typedefs - they're already pointers.
|
|
143
|
-
if (isTypedef && typedefName && !isPointerTypedef) {
|
|
144
|
+
if (isTypedef && !hasBody && typedefName && !isPointerTypedef) {
|
|
144
145
|
symbolTable.markTypedefStructType(typedefName, sourceFile);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
// Issue #
|
|
148
|
-
if (hasBody) {
|
|
149
|
-
|
|
150
|
-
symbolTable,
|
|
151
|
-
sourceFile,
|
|
152
|
-
structTag,
|
|
153
|
-
typedefName,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Unmark opaque and typedef struct types when a full struct definition is encountered.
|
|
160
|
-
* Handles: typedef struct _foo foo; struct _foo { ... };
|
|
161
|
-
* Issue #958: Only unmarks typedefStructTypes when definition is in SAME file.
|
|
162
|
-
*/
|
|
163
|
-
private static unmarkOpaqueTypesOnDefinition(
|
|
164
|
-
symbolTable: SymbolTable,
|
|
165
|
-
sourceFile: string,
|
|
166
|
-
structTag?: string,
|
|
167
|
-
typedefName?: string,
|
|
168
|
-
): void {
|
|
169
|
-
if (structTag) {
|
|
170
|
-
const typedefAlias = symbolTable.getStructTagAlias(structTag);
|
|
171
|
-
symbolTable.unmarkOpaqueType(structTag);
|
|
172
|
-
symbolTable.unmarkTypedefStructType(structTag, sourceFile);
|
|
173
|
-
if (typedefAlias) {
|
|
174
|
-
symbolTable.unmarkOpaqueType(typedefAlias);
|
|
175
|
-
symbolTable.unmarkTypedefStructType(typedefAlias, sourceFile);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (typedefName) {
|
|
179
|
-
symbolTable.unmarkOpaqueType(typedefName);
|
|
180
|
-
symbolTable.unmarkTypedefStructType(typedefName, sourceFile);
|
|
148
|
+
// Issue #958: Record struct tag body for query-time opaque resolution
|
|
149
|
+
if (hasBody && structTag) {
|
|
150
|
+
symbolTable.markStructTagHasBody(structTag);
|
|
181
151
|
}
|
|
182
152
|
}
|
|
183
153
|
|