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.
- package/dist/index.js +231 -42
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +82 -7
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +92 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +23 -0
- package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
- package/src/transpiler/output/codegen/CodeGenerator.ts +157 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
- package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
- package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
- package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
- package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
- package/src/utils/BitUtils.ts +17 -13
- package/src/utils/__tests__/BitUtils.test.ts +56 -56
package/package.json
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
588
|
-
|
|
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
|
-
|
|
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
|
|
706
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
3855
|
+
const type = this.generateType(ctx.type());
|
|
3854
3856
|
|
|
3855
|
-
|
|
3856
|
-
if (!name.startsWith("c_") || !ctx.expression()) {
|
|
3857
|
+
if (!ctx.expression()) {
|
|
3857
3858
|
return type;
|
|
3858
3859
|
}
|
|
3859
3860
|
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
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
|
});
|