c-next 0.2.5 → 0.2.6

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 (27) hide show
  1. package/dist/index.js +231 -42
  2. package/dist/index.js.map +2 -2
  3. package/package.json +1 -1
  4. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +82 -7
  5. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +92 -0
  6. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +23 -0
  7. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
  8. package/src/transpiler/output/codegen/CodeGenerator.ts +157 -7
  9. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
  10. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
  11. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
  12. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
  13. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
  14. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
  15. package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
  16. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
  17. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
  18. package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
  19. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
  20. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
  21. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
  22. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
  23. package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
  24. package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
  25. package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
  26. package/src/utils/BitUtils.ts +17 -13
  27. package/src/utils/__tests__/BitUtils.test.ts +56 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "packageManager": "npm@11.9.0",
6
6
  "type": "module",
@@ -572,6 +572,9 @@ class FunctionCallAnalyzer {
572
572
  );
573
573
  }
574
574
 
575
+ /** Current scope name during callback assignment scanning */
576
+ private scanCurrentScope: string | null = null;
577
+
575
578
  /**
576
579
  * Detect functions assigned to C function pointer typedefs.
577
580
  * When `PointCallback cb <- my_handler;` is found and PointCallback
@@ -582,13 +585,61 @@ class FunctionCallAnalyzer {
582
585
  ): void {
583
586
  for (const decl of tree.declaration()) {
584
587
  const funcDecl = decl.functionDeclaration();
585
- if (!funcDecl) continue;
588
+ if (funcDecl) {
589
+ this.scanStandaloneFunctionForCallbacks(funcDecl);
590
+ continue;
591
+ }
592
+
593
+ const scopeDecl = decl.scopeDeclaration();
594
+ if (scopeDecl) {
595
+ this.scanScopeForCallbacks(scopeDecl);
596
+ }
597
+ }
598
+ }
599
+
600
+ /**
601
+ * Scan a standalone function declaration for callback assignments.
602
+ */
603
+ private scanStandaloneFunctionForCallbacks(
604
+ funcDecl: Parser.FunctionDeclarationContext,
605
+ ): void {
606
+ const block = funcDecl.block();
607
+ if (!block) return;
608
+
609
+ this.scanCurrentScope = null;
610
+ this.scanBlockForCallbackAssignments(block);
611
+ }
586
612
 
587
- const block = funcDecl.block();
588
- if (!block) continue;
613
+ /**
614
+ * Scan all member functions in a scope for callback assignments (Issue #895).
615
+ */
616
+ private scanScopeForCallbacks(
617
+ scopeDecl: Parser.ScopeDeclarationContext,
618
+ ): void {
619
+ const scopeName = scopeDecl.IDENTIFIER().getText();
589
620
 
590
- this.scanBlockForCallbackAssignments(block);
621
+ for (const member of scopeDecl.scopeMember()) {
622
+ this.scanScopeMemberForCallbacks(member, scopeName);
591
623
  }
624
+
625
+ this.scanCurrentScope = null;
626
+ }
627
+
628
+ /**
629
+ * Scan a single scope member for callback assignments.
630
+ */
631
+ private scanScopeMemberForCallbacks(
632
+ member: Parser.ScopeMemberContext,
633
+ scopeName: string,
634
+ ): void {
635
+ const memberFunc = member.functionDeclaration();
636
+ if (!memberFunc) return;
637
+
638
+ const block = memberFunc.block();
639
+ if (!block) return;
640
+
641
+ this.scanCurrentScope = scopeName;
642
+ this.scanBlockForCallbackAssignments(block);
592
643
  }
593
644
 
594
645
  /**
@@ -702,17 +753,41 @@ class FunctionCallAnalyzer {
702
753
 
703
754
  /**
704
755
  * Extract a function reference from an expression context.
705
- * Matches bare identifiers (e.g., "my_handler") and qualified scope
706
- * names (e.g., "MyScope.handler").
756
+ * Matches:
757
+ * - Bare identifiers: "my_handler"
758
+ * - Qualified scope names: "MyScope.handler"
759
+ * - Self-scope reference: "this.handler" (resolved using scanCurrentScope)
760
+ * - Global scope reference: "global.ScopeName.handler"
707
761
  * Returns null if the expression is not a function reference.
708
762
  */
709
763
  private extractFunctionReference(
710
764
  expr: Parser.ExpressionContext,
711
765
  ): string | null {
712
766
  const text = expr.getText();
713
- if (/^\w+(\.\w+)?$/.test(text)) {
767
+
768
+ // Pattern 1: this.member -> CurrentScope.member (Issue #895)
769
+ const thisPattern = /^this\.(\w+)$/;
770
+ const thisMatch = thisPattern.exec(text);
771
+ if (thisMatch) {
772
+ if (!this.scanCurrentScope) {
773
+ return null; // this.member outside scope context
774
+ }
775
+ return this.scanCurrentScope + "." + thisMatch[1];
776
+ }
777
+
778
+ // Pattern 2: global.Scope.member -> Scope.member (Issue #895)
779
+ const globalPattern = /^global\.(\w+)\.(\w+)$/;
780
+ const globalMatch = globalPattern.exec(text);
781
+ if (globalMatch) {
782
+ return globalMatch[1] + "." + globalMatch[2];
783
+ }
784
+
785
+ // Pattern 3: Bare identifier or simple Scope.member
786
+ const simplePattern = /^\w+(\.\w+)?$/;
787
+ if (simplePattern.test(text)) {
714
788
  return text;
715
789
  }
790
+
716
791
  return null;
717
792
  }
718
793
 
@@ -25,6 +25,38 @@ function parse(source: string) {
25
25
  return parser.program();
26
26
  }
27
27
 
28
+ /**
29
+ * Helper to register widget callback symbols for Issue #895 tests.
30
+ * Adds widget_set_flush_cb function and flush_cb_t typedef to symbol table.
31
+ */
32
+ function addWidgetCallbackSymbols(symbolTable: SymbolTable): void {
33
+ // Register the C function that takes a callback parameter
34
+ symbolTable.addCSymbol({
35
+ name: "widget_set_flush_cb",
36
+ kind: "function",
37
+ sourceLanguage: ESourceLanguage.C,
38
+ sourceFile: "widget.h",
39
+ sourceLine: 1,
40
+ isExported: true,
41
+ type: "void",
42
+ parameters: [
43
+ { name: "w", type: "widget_t*", isConst: false, isArray: false },
44
+ { name: "cb", type: "flush_cb_t", isConst: false, isArray: false },
45
+ ],
46
+ });
47
+
48
+ // Register the callback typedef
49
+ symbolTable.addCSymbol({
50
+ name: "flush_cb_t",
51
+ kind: "type",
52
+ sourceLanguage: ESourceLanguage.C,
53
+ sourceFile: "widget.h",
54
+ sourceLine: 2,
55
+ isExported: true,
56
+ type: "void (*)(widget_t*, const rect_t*, uint8_t*)",
57
+ });
58
+ }
59
+
28
60
  describe("FunctionCallAnalyzer", () => {
29
61
  // ========================================================================
30
62
  // Define Before Use
@@ -1024,5 +1056,65 @@ describe("FunctionCallAnalyzer", () => {
1024
1056
  true,
1025
1057
  );
1026
1058
  });
1059
+
1060
+ it("should detect scope callback with this.member pattern (Issue #895 Bug A)", () => {
1061
+ // Bug A: PR #897 only fixed global functions. Scope callbacks using
1062
+ // this.cb pattern are not detected because extractFunctionReference
1063
+ // does not handle the this keyword.
1064
+ const code = `
1065
+ scope ScopeAP {
1066
+ public void cb(u32 w) {
1067
+ u32 x <- w;
1068
+ }
1069
+
1070
+ public void register_it() {
1071
+ u32 w <- 0;
1072
+ global.widget_set_flush_cb(w, this.cb);
1073
+ }
1074
+ }
1075
+ `;
1076
+ const tree = parse(code);
1077
+ const symbolTable = new SymbolTable();
1078
+ addWidgetCallbackSymbols(symbolTable);
1079
+
1080
+ CodeGenState.callbackCompatibleFunctions.clear();
1081
+ const analyzer = new FunctionCallAnalyzer();
1082
+ analyzer.analyze(tree, symbolTable);
1083
+
1084
+ // Should detect ScopeAP_cb as callback-compatible
1085
+ expect(CodeGenState.callbackCompatibleFunctions.has("ScopeAP_cb")).toBe(
1086
+ true,
1087
+ );
1088
+ });
1089
+
1090
+ it("should detect scope callback with global.Scope.member pattern (Issue #895 Bug A)", () => {
1091
+ // Bug A: PR #897 only fixed global functions. Scope callbacks using
1092
+ // global.ScopeName.member pattern are not detected because
1093
+ // extractFunctionReference does not handle two-level dotted paths.
1094
+ const code = `
1095
+ scope ScopeAP {
1096
+ public void cb(u32 w) {
1097
+ u32 x <- w;
1098
+ }
1099
+ }
1100
+
1101
+ void register_from_global() {
1102
+ u32 w <- 0;
1103
+ global.widget_set_flush_cb(w, global.ScopeAP.cb);
1104
+ }
1105
+ `;
1106
+ const tree = parse(code);
1107
+ const symbolTable = new SymbolTable();
1108
+ addWidgetCallbackSymbols(symbolTable);
1109
+
1110
+ CodeGenState.callbackCompatibleFunctions.clear();
1111
+ const analyzer = new FunctionCallAnalyzer();
1112
+ analyzer.analyze(tree, symbolTable);
1113
+
1114
+ // Should detect ScopeAP_cb as callback-compatible
1115
+ expect(CodeGenState.callbackCompatibleFunctions.has("ScopeAP_cb")).toBe(
1116
+ true,
1117
+ );
1118
+ });
1027
1119
  });
1028
1120
  });
@@ -572,6 +572,29 @@ describe("CResolver - Additional Edge Cases", () => {
572
572
  expect(result.symbols[0].name).toBe("getPtr");
573
573
  expect(result.symbols[0].kind).toBe("function");
574
574
  });
575
+
576
+ it("collects pointer return type from function prototype (Issue #895 Bug B)", () => {
577
+ const tree = TestHelpers.parseC(`widget_t *widget_create(int w, int h);`);
578
+ const result = CResolver.resolve(tree!, "test.h");
579
+
580
+ expect(result.symbols).toHaveLength(1);
581
+ const symbol = result.symbols[0];
582
+ expect(symbol.name).toBe("widget_create");
583
+ expect(symbol.kind).toBe("function");
584
+ if (symbol.kind === "function") {
585
+ expect(symbol.type).toBe("widget_t*");
586
+ expect(symbol.parameters).toHaveLength(2);
587
+ }
588
+ });
589
+
590
+ it("collects non-pointer return type correctly (Issue #895 Bug B)", () => {
591
+ const tree = TestHelpers.parseC(`int getValue();`);
592
+ const result = CResolver.resolve(tree!, "test.h");
593
+
594
+ if (result.symbols[0].kind === "function") {
595
+ expect(result.symbols[0].type).toBe("int");
596
+ }
597
+ });
575
598
  });
576
599
 
577
600
  describe("CResolver - Without SymbolTable", () => {
@@ -2,9 +2,10 @@
2
2
  * FunctionCollector - Collects function symbols from C parse trees.
3
3
  */
4
4
 
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
-
7
- import type { FunctionDefinitionContext } from "../../../parser/c/grammar/CParser";
5
+ import type {
6
+ DeclaratorContext,
7
+ FunctionDefinitionContext,
8
+ } from "../../../parser/c/grammar/CParser";
8
9
  import type ICFunctionSymbol from "../../../../types/symbols/c/ICFunctionSymbol";
9
10
  import type ICParameterInfo from "../../../../types/symbols/c/ICParameterInfo";
10
11
  import ESourceLanguage from "../../../../../utils/types/ESourceLanguage";
@@ -80,7 +81,7 @@ class FunctionCollector {
80
81
  static collectFromDeclaration(
81
82
  name: string,
82
83
  baseType: string,
83
- declarator: any,
84
+ declarator: DeclaratorContext,
84
85
  sourceFile: string,
85
86
  line: number,
86
87
  isExtern: boolean,
@@ -89,6 +90,11 @@ class FunctionCollector {
89
90
  DeclaratorUtils.extractFunctionParameters(declarator),
90
91
  );
91
92
 
93
+ // Issue #895 Bug B: Check if declarator has pointer - means function returns pointer
94
+ // In C grammar: widget_t *func() has declarator with pointer() before directDeclarator
95
+ const hasPointer = declarator.pointer() !== null;
96
+ const returnType = hasPointer ? `${baseType}*` : baseType;
97
+
92
98
  return {
93
99
  kind: "function",
94
100
  name,
@@ -96,7 +102,7 @@ class FunctionCollector {
96
102
  sourceLine: line,
97
103
  sourceLanguage: ESourceLanguage.C,
98
104
  isExported: !isExtern,
99
- type: baseType,
105
+ type: returnType,
100
106
  parameters: parameters.length > 0 ? parameters : undefined,
101
107
  isDeclaration: true,
102
108
  };
@@ -3834,6 +3834,7 @@ export default class CodeGenerator implements IOrchestrator {
3834
3834
  this._inferVariableType(varCtx, name),
3835
3835
  trackLocalVariable: (varCtx, name) =>
3836
3836
  this._trackLocalVariable(varCtx, name),
3837
+ markVariableAsPointer: (name) => this._markVariableAsPointer(name),
3837
3838
  getStringConcatOperands: (concatCtx) =>
3838
3839
  this._getStringConcatOperands(concatCtx),
3839
3840
  getSubstringOperands: (substrCtx) =>
@@ -3845,27 +3846,161 @@ export default class CodeGenerator implements IOrchestrator {
3845
3846
 
3846
3847
  /**
3847
3848
  * Issue #696: Infer variable type, handling nullable C pointer types.
3849
+ * Issue #895 Bug B: Infer pointer type from C function return type.
3848
3850
  */
3849
3851
  private _inferVariableType(
3850
3852
  ctx: Parser.VariableDeclarationContext,
3851
3853
  name: string,
3852
3854
  ): string {
3853
- let type = this.generateType(ctx.type());
3855
+ const type = this.generateType(ctx.type());
3854
3856
 
3855
- // ADR-046: Handle nullable C pointer types (c_ prefix variables)
3856
- if (!name.startsWith("c_") || !ctx.expression()) {
3857
+ if (!ctx.expression()) {
3857
3858
  return type;
3858
3859
  }
3859
3860
 
3860
- const exprText = ctx.expression()!.getText();
3861
- for (const funcName of NullCheckAnalyzer.getStructPointerFunctions()) {
3862
- if (exprText.includes(`${funcName}(`)) {
3863
- return `${type}*`;
3861
+ // Issue #895 Bug B: Check if initializer is a C function call returning pointer
3862
+ const pointerType = this._inferPointerTypeFromFunctionCall(
3863
+ ctx.expression()!,
3864
+ type,
3865
+ );
3866
+ if (pointerType) {
3867
+ return pointerType;
3868
+ }
3869
+
3870
+ // ADR-046: Handle nullable C pointer types (c_ prefix variables)
3871
+ if (name.startsWith("c_")) {
3872
+ const exprText = ctx.expression()!.getText();
3873
+ for (const funcName of NullCheckAnalyzer.getStructPointerFunctions()) {
3874
+ if (exprText.includes(`${funcName}(`)) {
3875
+ return `${type}*`;
3876
+ }
3864
3877
  }
3865
3878
  }
3879
+
3866
3880
  return type;
3867
3881
  }
3868
3882
 
3883
+ /**
3884
+ * Issue #895 Bug B: Infer pointer type from C function return type.
3885
+ * If initializer is a call to a C function that returns T*, and declared
3886
+ * type is T, return T* instead of T.
3887
+ */
3888
+ private _inferPointerTypeFromFunctionCall(
3889
+ expr: Parser.ExpressionContext,
3890
+ declaredType: string,
3891
+ ): string | null {
3892
+ // Extract function name from C function call patterns
3893
+ const funcName = this._extractCFunctionName(expr);
3894
+ if (!funcName) {
3895
+ return null;
3896
+ }
3897
+
3898
+ // Look up C function in symbol table
3899
+ const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
3900
+ if (cFunc?.kind !== "function") {
3901
+ return null;
3902
+ }
3903
+
3904
+ // Check if return type is a pointer to the declared type
3905
+ const returnType = cFunc.type;
3906
+ if (!returnType.endsWith("*")) {
3907
+ return null;
3908
+ }
3909
+
3910
+ // Check if the base return type matches the declared type
3911
+ // e.g., "widget_t *" or "widget_t*" matches declared "widget_t"
3912
+ const returnBaseType = returnType.replace(/\s*\*\s*$/, "").trim();
3913
+ if (returnBaseType === declaredType) {
3914
+ return `${declaredType}*`;
3915
+ }
3916
+
3917
+ return null;
3918
+ }
3919
+
3920
+ /**
3921
+ * Extract C function name from expression patterns.
3922
+ * Handles both:
3923
+ * - global.funcName(...) - explicit global access
3924
+ * - funcName(...) - direct call (if funcName is a known C function)
3925
+ * Returns null if expression doesn't match these patterns.
3926
+ */
3927
+ private _extractCFunctionName(expr: Parser.ExpressionContext): string | null {
3928
+ const postfix = ExpressionUnwrapper.getPostfixExpression(expr);
3929
+ if (!postfix) {
3930
+ return null;
3931
+ }
3932
+
3933
+ const primary = postfix.primaryExpression();
3934
+ const ops = postfix.postfixOp();
3935
+
3936
+ // Pattern 1: global.funcName(...)
3937
+ if (primary.GLOBAL()) {
3938
+ return this._extractGlobalPatternFuncName(ops);
3939
+ }
3940
+
3941
+ // Pattern 2: funcName(...) - direct call
3942
+ const identifier = primary.IDENTIFIER();
3943
+ if (identifier) {
3944
+ return this._extractDirectCallFuncName(identifier.getText(), ops);
3945
+ }
3946
+
3947
+ return null;
3948
+ }
3949
+
3950
+ /**
3951
+ * Extract function name from global.funcName(...) pattern.
3952
+ */
3953
+ private _extractGlobalPatternFuncName(
3954
+ ops: Parser.PostfixOpContext[],
3955
+ ): string | null {
3956
+ if (ops.length < 2) {
3957
+ return null;
3958
+ }
3959
+
3960
+ const memberOp = ops[0];
3961
+ if (!memberOp.IDENTIFIER()) {
3962
+ return null;
3963
+ }
3964
+
3965
+ const callOp = ops[1];
3966
+ if (!this._isCallOp(callOp)) {
3967
+ return null;
3968
+ }
3969
+
3970
+ return memberOp.IDENTIFIER()!.getText();
3971
+ }
3972
+
3973
+ /**
3974
+ * Extract function name from direct funcName(...) call if it's a C function.
3975
+ */
3976
+ private _extractDirectCallFuncName(
3977
+ funcName: string,
3978
+ ops: Parser.PostfixOpContext[],
3979
+ ): string | null {
3980
+ if (ops.length < 1) {
3981
+ return null;
3982
+ }
3983
+
3984
+ if (!this._isCallOp(ops[0])) {
3985
+ return null;
3986
+ }
3987
+
3988
+ // Verify this is actually a C function (not a C-Next scope function)
3989
+ const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
3990
+ if (cFunc?.kind === "function") {
3991
+ return funcName;
3992
+ }
3993
+
3994
+ return null;
3995
+ }
3996
+
3997
+ /**
3998
+ * Check if a postfix op is a function call.
3999
+ */
4000
+ private _isCallOp(op: Parser.PostfixOpContext): boolean {
4001
+ return Boolean(op.argumentList() || op.getText().startsWith("("));
4002
+ }
4003
+
3869
4004
  /**
3870
4005
  * Issue #696: Track local variable for type registry and const values.
3871
4006
  */
@@ -3893,6 +4028,21 @@ export default class CodeGenerator implements IOrchestrator {
3893
4028
  }
3894
4029
  }
3895
4030
 
4031
+ /**
4032
+ * Issue #895 Bug B: Mark variable as a pointer in the type registry.
4033
+ * Called when type inference detects that a variable should be a pointer
4034
+ * (e.g., initialized from a C function returning T*).
4035
+ */
4036
+ private _markVariableAsPointer(name: string): void {
4037
+ const typeInfo = CodeGenState.getVariableTypeInfo(name);
4038
+ if (typeInfo) {
4039
+ CodeGenState.setVariableTypeInfo(name, {
4040
+ ...typeInfo,
4041
+ isPointer: true,
4042
+ });
4043
+ }
4044
+ }
4045
+
3896
4046
  // Issue #792: Methods _handleArrayDeclaration, _getArrayTypeDimension, _parseArrayTypeDimension,
3897
4047
  // _parseFirstArrayDimension, _validateArrayDeclarationSyntax, _extractBaseTypeName,
3898
4048
  // _generateVariableInitializer, _validateIntegerInitializer, _finalizeCppClassAssignments,
@@ -17,6 +17,7 @@ import SymbolTable from "../../../logic/symbols/SymbolTable";
17
17
  import CNextResolver from "../../../logic/symbols/cnext/index";
18
18
  import TSymbolInfoAdapter from "../../../logic/symbols/cnext/adapters/TSymbolInfoAdapter";
19
19
  import CodeGenState from "../../../state/CodeGenState";
20
+ import ESourceLanguage from "../../../../utils/types/ESourceLanguage";
20
21
 
21
22
  /**
22
23
  * Helper to parse C-Next source and return tree + generator ready for testing.
@@ -1209,4 +1210,132 @@ describe("CodeGenerator Coverage Tests", () => {
1209
1210
  expect(code).toContain("cnx_clamp_add_u8");
1210
1211
  });
1211
1212
  });
1213
+
1214
+ // ==========================================================================
1215
+ // Issue #895 Bug B: Pointer type inference from C function calls
1216
+ // ==========================================================================
1217
+ describe("_inferPointerTypeFromFunctionCall / _extractCFunctionName", () => {
1218
+ /**
1219
+ * Helper to setup generator with C symbols added to SymbolTable.
1220
+ */
1221
+ function setupGeneratorWithCSymbols(
1222
+ source: string,
1223
+ cSymbols: Array<{
1224
+ name: string;
1225
+ type: string;
1226
+ sourceFile: string;
1227
+ }>,
1228
+ ): string {
1229
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
1230
+
1231
+ const symbolTable = new SymbolTable();
1232
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
1233
+ symbolTable.addTSymbols(tSymbols);
1234
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
1235
+
1236
+ // Add C function symbols
1237
+ for (const cSym of cSymbols) {
1238
+ symbolTable.addCSymbol({
1239
+ kind: "function",
1240
+ name: cSym.name,
1241
+ type: cSym.type,
1242
+ sourceFile: cSym.sourceFile,
1243
+ sourceLine: 1,
1244
+ sourceLanguage: ESourceLanguage.C,
1245
+ isExported: true,
1246
+ isDeclaration: true,
1247
+ });
1248
+ }
1249
+
1250
+ const generator = new CodeGenerator();
1251
+ CodeGenState.symbolTable = symbolTable;
1252
+
1253
+ return generator.generate(tree, tokenStream, {
1254
+ symbolInfo: symbols,
1255
+ sourcePath: "test.cnx",
1256
+ cppMode: false,
1257
+ });
1258
+ }
1259
+
1260
+ it("should infer pointer type from global.funcName() pattern", () => {
1261
+ const source = `
1262
+ void test() {
1263
+ widget_t w <- global.widget_create();
1264
+ }
1265
+ `;
1266
+
1267
+ const code = setupGeneratorWithCSymbols(source, [
1268
+ { name: "widget_create", type: "widget_t*", sourceFile: "widget.h" },
1269
+ ]);
1270
+
1271
+ // Should generate widget_t* w (pointer), not widget_t w
1272
+ expect(code).toContain("widget_t* w = widget_create()");
1273
+ });
1274
+
1275
+ it("should infer pointer type from direct funcName() call", () => {
1276
+ const source = `
1277
+ void test() {
1278
+ widget_t w <- widget_create();
1279
+ }
1280
+ `;
1281
+
1282
+ const code = setupGeneratorWithCSymbols(source, [
1283
+ { name: "widget_create", type: "widget_t*", sourceFile: "widget.h" },
1284
+ ]);
1285
+
1286
+ // Should generate widget_t* w (pointer), not widget_t w
1287
+ expect(code).toContain("widget_t* w = widget_create()");
1288
+ });
1289
+
1290
+ it("should not infer pointer when C function returns non-pointer", () => {
1291
+ const source = `
1292
+ void test() {
1293
+ i32 val <- global.get_value();
1294
+ }
1295
+ `;
1296
+
1297
+ const code = setupGeneratorWithCSymbols(source, [
1298
+ { name: "get_value", type: "int", sourceFile: "utils.h" },
1299
+ ]);
1300
+
1301
+ // Should generate int32_t val (not a pointer)
1302
+ expect(code).toContain("int32_t val = get_value()");
1303
+ expect(code).not.toContain("int32_t* val");
1304
+ });
1305
+
1306
+ it("should not infer pointer when function is not a C function", () => {
1307
+ // No C symbols registered - create_widget is a C-Next function
1308
+ const source = `
1309
+ struct widget_t { i32 x; }
1310
+ widget_t create_widget() {
1311
+ widget_t dummy <- {x: 0};
1312
+ return dummy;
1313
+ }
1314
+ void test() {
1315
+ widget_t w <- create_widget();
1316
+ }
1317
+ `;
1318
+
1319
+ const { code } = setupGenerator(source);
1320
+
1321
+ // Should NOT generate pointer since create_widget is a C-Next function
1322
+ expect(code).not.toContain("widget_t* w");
1323
+ });
1324
+
1325
+ it("should not infer pointer when return type base doesn't match declared type", () => {
1326
+ const source = `
1327
+ void test() {
1328
+ widget_t w <- global.create_other();
1329
+ }
1330
+ `;
1331
+
1332
+ const code = setupGeneratorWithCSymbols(source, [
1333
+ { name: "create_other", type: "other_t*", sourceFile: "other.h" },
1334
+ ]);
1335
+
1336
+ // Should NOT infer pointer since types don't match
1337
+ // (other_t* doesn't match widget_t)
1338
+ expect(code).not.toContain("widget_t* w");
1339
+ });
1340
+ });
1212
1341
  });