//go:build !llgo // +build !llgo package header import ( "bytes" "go/token" "go/types" "os" "strings" "testing" "github.com/goplus/gogen/packages" "github.com/goplus/llgo/ssa" "github.com/goplus/llvm" ) func init() { llvm.InitializeAllTargets() llvm.InitializeAllTargetMCs() llvm.InitializeAllTargetInfos() llvm.InitializeAllAsmParsers() llvm.InitializeAllAsmPrinters() } func TestGenCHeaderExport(t *testing.T) { prog := ssa.NewProgram(nil) prog.SetRuntime(func() *types.Package { fset := token.NewFileSet() imp := packages.NewImporter(fset) pkg, _ := imp.Import(ssa.PkgRuntime) return pkg }) // Define main package and the 'Foo' type within it mainPkgPath := "github.com/goplus/llgo/test_buildmode/main" mainTypesPkg := types.NewPackage(mainPkgPath, "main") fooFields := []*types.Var{ types.NewField(token.NoPos, mainTypesPkg, "a", types.Typ[types.Int], false), types.NewField(token.NoPos, mainTypesPkg, "b", types.Typ[types.Float64], false), } fooStruct := types.NewStruct(fooFields, nil) fooTypeName := types.NewTypeName(token.NoPos, mainTypesPkg, "Foo", nil) fooNamed := types.NewNamed(fooTypeName, fooStruct, nil) mainTypesPkg.Scope().Insert(fooTypeName) // Create SSA package for main mainPkg := prog.NewPackage("main", mainPkgPath) // Define exported functions in mainPkg mainPkg.NewFunc("HelloWorld", types.NewSignatureType(nil, nil, nil, nil, nil, false), ssa.InGo) useFooPtrParams := types.NewTuple(types.NewVar(token.NoPos, nil, "f", types.NewPointer(fooNamed))) useFooPtrResults := types.NewTuple(types.NewVar(token.NoPos, nil, "", fooNamed)) useFooPtrSig := types.NewSignatureType(nil, nil, nil, useFooPtrParams, useFooPtrResults, false) mainPkg.NewFunc("UseFooPtr", useFooPtrSig, ssa.InGo) useFooParams := types.NewTuple(types.NewVar(token.NoPos, nil, "f", fooNamed)) useFooSig := types.NewSignatureType(nil, nil, nil, useFooParams, useFooPtrResults, false) mainPkg.NewFunc("UseFoo", useFooSig, ssa.InGo) // Set exports for main mainPkg.SetExport("HelloWorld", "HelloWorld") mainPkg.SetExport("UseFooPtr", "UseFooPtr") mainPkg.SetExport("UseFoo", "UseFoo") // Create package C cPkgPath := "github.com/goplus/llgo/test_buildmode/bar" cPkg := prog.NewPackage("C", cPkgPath) addParams := types.NewTuple( types.NewVar(token.NoPos, nil, "a", types.Typ[types.Int]), types.NewVar(token.NoPos, nil, "b", types.Typ[types.Int])) addResults := types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.Int])) addSig := types.NewSignatureType(nil, nil, nil, addParams, addResults, false) cPkg.NewFunc("Add", addSig, ssa.InGo) cPkg.NewFunc("Sub", addSig, ssa.InGo) cPkg.SetExport("XAdd", "Add") cPkg.SetExport("XSub", "Sub") // Generate header libname := "testbuild" headerPath := os.TempDir() + "/testbuild.h" err := GenHeaderFile(prog, []ssa.Package{mainPkg, cPkg}, libname, headerPath, true) if err != nil { t.Fatal(err) } data, err := os.ReadFile(headerPath) if err != nil { t.Fatal(err) } required := []string{ "/* Code generated by llgo; DO NOT EDIT. */", "#ifndef __TESTBUILD_H_", "#include ", "typedef struct { const char *p; intptr_t n; } GoString;", "typedef struct {\n intptr_t a;\n double b;\n} main_Foo;", "void\nHelloWorld(void);", "main_Foo\nUseFooPtr(main_Foo* f);", "main_Foo\nUseFoo(main_Foo f);", "intptr_t\nAdd(intptr_t a, intptr_t b);", "intptr_t\nSub(intptr_t a, intptr_t b);", "#endif /* __TESTBUILD_H_ */", } got := string(data) for _, sub := range required { if !strings.Contains(got, sub) { t.Fatalf("Generated content: %s\n", got) t.Fatalf("Generated header is missing expected content:\n%s", sub) } } } func TestCheaderWriterTypes(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Test complex integration scenarios only - basic types are covered by TestGoCTypeName tests := []struct { name string goType types.Type expected string }{ { name: "named struct", goType: func() types.Type { pkg := types.NewPackage("main", "main") s := types.NewStruct([]*types.Var{types.NewField(0, nil, "f1", types.Typ[types.Int], false)}, nil) return types.NewNamed(types.NewTypeName(0, pkg, "MyStruct", nil), s, nil) }(), expected: "typedef struct {\n intptr_t f1;\n} main_MyStruct;\n\n", }, { name: "struct with array field", goType: func() types.Type { arrayType := types.NewArray(types.Typ[types.Float64], 10) return types.NewStruct([]*types.Var{ types.NewField(0, nil, "Values", arrayType, false), }, nil) }(), expected: "typedef struct {\n double Values[10];\n} struct_double_Values;\n\n", }, { name: "struct with multidimensional array", goType: func() types.Type { // Create a 2D array: [4][3]int32 innerArrayType := types.NewArray(types.Typ[types.Int32], 3) outerArrayType := types.NewArray(innerArrayType, 4) return types.NewStruct([]*types.Var{ types.NewField(0, nil, "Matrix", outerArrayType, false), }, nil) }(), expected: "typedef struct {\n int32_t Matrix[4][3];\n} struct_int32_t_Matrix;\n\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hw.typeBuf.Reset() hw.declaredTypes = make(map[string]bool) // Reset declared types for each run // Mark predefined Go types as declared (same as in genHeader) hw.declaredTypes["GoString"] = true hw.declaredTypes["GoSlice"] = true hw.declaredTypes["GoMap"] = true hw.declaredTypes["GoChan"] = true hw.declaredTypes["GoInterface"] = true hw.declaredTypes["GoComplex64"] = true hw.declaredTypes["GoComplex128"] = true if err := hw.writeTypedef(tt.goType); err != nil { t.Fatalf("writeTypedef() error = %v", err) } got := hw.typeBuf.String() if got != tt.expected { t.Errorf("writeTypedef() got = %q, want %q", got, tt.expected) } }) } } // Test for goCTypeName function to cover all basic types func TestGoCTypeName(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) tests := []struct { name string goType types.Type expected string }{ {name: "bool", goType: types.Typ[types.Bool], expected: "_Bool"}, {name: "int8", goType: types.Typ[types.Int8], expected: "int8_t"}, {name: "uint8", goType: types.Typ[types.Uint8], expected: "uint8_t"}, {name: "int16", goType: types.Typ[types.Int16], expected: "int16_t"}, {name: "uint16", goType: types.Typ[types.Uint16], expected: "uint16_t"}, {name: "int32", goType: types.Typ[types.Int32], expected: "int32_t"}, {name: "uint32", goType: types.Typ[types.Uint32], expected: "uint32_t"}, {name: "int64", goType: types.Typ[types.Int64], expected: "int64_t"}, {name: "uint64", goType: types.Typ[types.Uint64], expected: "uint64_t"}, {name: "int", goType: types.Typ[types.Int], expected: "intptr_t"}, {name: "uint", goType: types.Typ[types.Uint], expected: "uintptr_t"}, {name: "uintptr", goType: types.Typ[types.Uintptr], expected: "uintptr_t"}, {name: "float32", goType: types.Typ[types.Float32], expected: "float"}, {name: "float64", goType: types.Typ[types.Float64], expected: "double"}, {name: "complex64", goType: types.Typ[types.Complex64], expected: "GoComplex64"}, {name: "complex128", goType: types.Typ[types.Complex128], expected: "GoComplex128"}, {name: "string", goType: types.Typ[types.String], expected: "GoString"}, {name: "unsafe pointer", goType: types.Typ[types.UnsafePointer], expected: "void*"}, {name: "slice", goType: types.NewSlice(types.Typ[types.Int]), expected: "GoSlice"}, {name: "map", goType: types.NewMap(types.Typ[types.String], types.Typ[types.Int]), expected: "GoMap"}, {name: "chan", goType: types.NewChan(types.SendRecv, types.Typ[types.Int]), expected: "GoChan"}, {name: "interface", goType: types.NewInterfaceType(nil, nil), expected: "GoInterface"}, { name: "array", goType: types.NewArray(types.Typ[types.Int], 5), expected: "intptr_t", }, { name: "pointer to int", goType: types.NewPointer(types.Typ[types.Int]), expected: "intptr_t*", }, { name: "pointer to unknown type", goType: types.NewPointer(types.Typ[types.Invalid]), expected: "void*", }, { name: "array of unknown type", goType: types.NewArray(types.Typ[types.Invalid], 3), expected: "", }, { name: "signature type", goType: types.NewSignature(nil, nil, nil, false), expected: "void (*)(void)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := hw.goCTypeName(tt.goType) if got != tt.expected { t.Errorf("goCTypeName() = %q, want %q", got, tt.expected) } }) } } // Test typeReferencesSelf function func TestTypeReferencesSelf(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) pkg := types.NewPackage("test", "test") // Create a named type for testing nodeTypeName := types.NewTypeName(0, pkg, "Node", nil) nodeStruct := types.NewStruct([]*types.Var{ types.NewField(0, nil, "data", types.Typ[types.Int], false), }, nil) namedNode := types.NewNamed(nodeTypeName, nodeStruct, nil) tests := []struct { name string typ types.Type selfTypeName string expected bool }{ { name: "pointer to self", typ: types.NewPointer(namedNode), selfTypeName: "test_Node", expected: true, }, { name: "slice of self", typ: types.NewSlice(namedNode), selfTypeName: "test_Node", expected: true, }, { name: "array of self", typ: types.NewArray(namedNode, 5), selfTypeName: "test_Node", expected: true, }, { name: "named type self", typ: namedNode, selfTypeName: "test_Node", expected: true, }, { name: "basic type not self", typ: types.Typ[types.Int], selfTypeName: "test_Node", expected: false, }, { name: "different named type", typ: namedNode, selfTypeName: "other_Type", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := hw.typeReferencesSelf(tt.typ, tt.selfTypeName) if got != tt.expected { t.Errorf("typeReferencesSelf() = %v, want %v", got, tt.expected) } }) } } // Test array struct generation functions func TestArrayStructGeneration(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Test ensureArrayStruct arrayType := types.NewArray(types.Typ[types.Int32], 5) name := hw.ensureArrayStruct(arrayType) expectedName := "Array_int32_t_5" if name != expectedName { t.Errorf("ensureArrayStruct() = %q, want %q", name, expectedName) } // Test that typedef was generated output := hw.typeBuf.String() if !strings.Contains(output, "typedef struct") { t.Error("ensureArrayStruct should generate typedef") } if !strings.Contains(output, "int32_t data[5]") { t.Error("ensureArrayStruct should generate correct array field") } // Test duplicate prevention hw.typeBuf.Reset() name2 := hw.ensureArrayStruct(arrayType) // Call again if name2 != name { t.Errorf("ensureArrayStruct should return same name for same type") } duplicateOutput := hw.typeBuf.String() if duplicateOutput != "" { t.Error("ensureArrayStruct should not generate duplicate typedef") } } // Test generateReturnType function func TestGenerateReturnType(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Test basic type basicRet := hw.generateReturnType(types.Typ[types.Int32]) if basicRet != "int32_t" { t.Errorf("generateReturnType(int32) = %q, want %q", basicRet, "int32_t") } // Test array type (should generate struct wrapper) arrayType := types.NewArray(types.Typ[types.Float64], 3) arrayRet := hw.generateReturnType(arrayType) expectedArrayRet := "Array_double_3" if arrayRet != expectedArrayRet { t.Errorf("generateReturnType(array) = %q, want %q", arrayRet, expectedArrayRet) } } // Test generateTypedef function func TestGenerateTypedef(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) pkg := types.NewPackage("test", "test") // Test named struct structType := types.NewStruct([]*types.Var{ types.NewField(0, nil, "value", types.Typ[types.Int], false), }, nil) namedType := types.NewNamed(types.NewTypeName(0, pkg, "TestStruct", nil), structType, nil) typedef := hw.generateTypedef(namedType) if !strings.Contains(typedef, "typedef struct") { t.Error("generateTypedef should generate typedef for named struct") } // Test named basic type namedInt := types.NewNamed(types.NewTypeName(0, pkg, "MyInt", nil), types.Typ[types.Int], nil) typedef2 := hw.generateTypedef(namedInt) if !strings.Contains(typedef2, "typedef intptr_t test_MyInt") { t.Error("generateTypedef should generate typedef for named basic type") } } // Test complex nested structures and dependencies func TestComplexNestedStructures(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Create a complex nested structure pkg := types.NewPackage("test", "test") // Inner struct innerStruct := types.NewStruct([]*types.Var{ types.NewField(0, nil, "value", types.Typ[types.Int], false), }, nil) // Named inner struct innerTypeName := types.NewTypeName(0, pkg, "InnerStruct", nil) namedInner := types.NewNamed(innerTypeName, innerStruct, nil) // Outer struct with inner struct field outerStruct := types.NewStruct([]*types.Var{ types.NewField(0, nil, "inner", namedInner, false), types.NewField(0, nil, "ptr", types.NewPointer(namedInner), false), types.NewField(0, nil, "slice", types.NewSlice(namedInner), false), }, nil) outerTypeName := types.NewTypeName(0, pkg, "OuterStruct", nil) namedOuter := types.NewNamed(outerTypeName, outerStruct, nil) // Test writeTypedef for complex structure err := hw.writeTypedef(namedOuter) if err != nil { t.Fatalf("writeTypedef() error = %v", err) } output := hw.typeBuf.String() // Should contain both inner and outer struct definitions if !strings.Contains(output, "test_InnerStruct") { t.Error("Expected inner struct typedef") } if !strings.Contains(output, "test_OuterStruct") { t.Error("Expected outer struct typedef") } } // Test goCTypeName with more type cases // Test processDependentTypes for error paths and edge cases func TestProcessDependentTypesEdgeCases(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Test signature type dependency (function parameters and results) params := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int])) results := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.String])) sigType := types.NewSignatureType(nil, nil, nil, params, results, false) err := hw.processDependentTypes(sigType, make(map[string]bool)) if err != nil { t.Errorf("processDependentTypes(signature) error = %v", err) } // Test processSignatureTypes directly err = hw.processSignatureTypes(sigType, make(map[string]bool)) if err != nil { t.Errorf("processSignatureTypes error = %v", err) } // Test Map type - this should trigger the Map case in writeTypedefRecursive mapType := types.NewMap(types.Typ[types.String], types.Typ[types.Int]) err = hw.writeTypedefRecursive(mapType, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(map) error = %v", err) } // Test Chan type - this should trigger the Chan case in writeTypedefRecursive chanType := types.NewChan(types.SendRecv, types.Typ[types.Bool]) err = hw.writeTypedefRecursive(chanType, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(chan) error = %v", err) } // Test Map with complex types to trigger both key and value processing struct1 := types.NewStruct([]*types.Var{ types.NewField(0, nil, "key", types.Typ[types.String], false), }, nil) struct2 := types.NewStruct([]*types.Var{ types.NewField(0, nil, "value", types.Typ[types.Int], false), }, nil) complexMapType := types.NewMap(struct1, struct2) err = hw.writeTypedefRecursive(complexMapType, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(complex map) error = %v", err) } // Test function signature with no parameters (edge case) noParamsSig := types.NewSignatureType(nil, nil, nil, nil, results, false) err = hw.processSignatureTypes(noParamsSig, make(map[string]bool)) if err != nil { t.Errorf("processSignatureTypes(no params) error = %v", err) } // Test function signature with no results (edge case) noResultsSig := types.NewSignatureType(nil, nil, nil, params, nil, false) err = hw.processSignatureTypes(noResultsSig, make(map[string]bool)) if err != nil { t.Errorf("processSignatureTypes(no results) error = %v", err) } // Test function type (callback) parameters - IntCallback intCallbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int])) intCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) intCallbackSig := types.NewSignatureType(nil, nil, nil, intCallbackParams, intCallbackResults, false) err = hw.writeTypedefRecursive(intCallbackSig, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(IntCallback) error = %v", err) } // Test function type (callback) parameters - StringCallback stringCallbackParams := types.NewTuple(types.NewVar(0, nil, "s", types.Typ[types.String])) stringCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.String])) stringCallbackSig := types.NewSignatureType(nil, nil, nil, stringCallbackParams, stringCallbackResults, false) err = hw.writeTypedefRecursive(stringCallbackSig, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(StringCallback) error = %v", err) } // Test function type (callback) parameters - VoidCallback voidCallbackSig := types.NewSignatureType(nil, nil, nil, nil, nil, false) err = hw.writeTypedefRecursive(voidCallbackSig, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(VoidCallback) error = %v", err) } // Test Named function type - this should trigger the function typedef generation pkg := types.NewPackage("test", "test") callbackParams := types.NewTuple(types.NewVar(0, nil, "x", types.Typ[types.Int])) callbackSig := types.NewSignatureType(nil, nil, nil, callbackParams, nil, false) callbackTypeName := types.NewTypeName(0, pkg, "Callback", nil) namedCallback := types.NewNamed(callbackTypeName, callbackSig, nil) // Test Named function type with no parameters - NoParamCallback func() int noParamCallbackResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) noParamCallbackSig := types.NewSignatureType(nil, nil, nil, nil, noParamCallbackResults, false) noParamCallbackTypeName := types.NewTypeName(0, pkg, "NoParamCallback", nil) namedNoParamCallback := types.NewNamed(noParamCallbackTypeName, noParamCallbackSig, nil) err = hw.writeTypedef(namedCallback) if err != nil { t.Errorf("writeTypedef(named function) error = %v", err) } err = hw.writeTypedef(namedNoParamCallback) if err != nil { t.Errorf("writeTypedef(no param callback) error = %v", err) } // Verify the generated typedef contains function pointer syntax output := hw.typeBuf.String() if !strings.Contains(output, "test_Callback") { t.Errorf("Expected named function typedef in output") } if !strings.Contains(output, "(*test_Callback)") { t.Errorf("Expected function pointer syntax in typedef: %s", output) } if !strings.Contains(output, "test_NoParamCallback") { t.Errorf("Expected no-param callback typedef in output") } if !strings.Contains(output, "(*test_NoParamCallback)(void)") { t.Errorf("Expected no-param function pointer syntax in typedef: %s", output) } // Test function signature with unnamed parameters (like //export ProcessThreeUnnamedParams) unnamedParams := types.NewTuple( types.NewVar(0, nil, "", types.Typ[types.Int]), types.NewVar(0, nil, "", types.Typ[types.String]), types.NewVar(0, nil, "", types.Typ[types.Bool]), ) unnamedResults := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Float64])) unnamedSig := types.NewSignatureType(nil, nil, nil, unnamedParams, unnamedResults, false) err = hw.writeTypedefRecursive(unnamedSig, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(unnamed params) error = %v", err) } } // Test generateParameterDeclaration function func TestGenerateParameterDeclaration(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) tests := []struct { name string paramType types.Type paramName string expected string }{ { name: "basic type with name", paramType: types.Typ[types.Int], paramName: "x", expected: "intptr_t x", }, { name: "basic type without name", paramType: types.Typ[types.Int], paramName: "", expected: "intptr_t", }, { name: "array type with name", paramType: types.NewArray(types.Typ[types.Int], 5), paramName: "arr", expected: "intptr_t* arr", }, { name: "array type without name", paramType: types.NewArray(types.Typ[types.Int], 5), paramName: "", expected: "intptr_t*", }, { name: "multidimensional array with name", paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3), paramName: "matrix", expected: "intptr_t matrix[3][4]", }, { name: "multidimensional array without name", paramType: types.NewArray(types.NewArray(types.Typ[types.Int], 4), 3), paramName: "", expected: "intptr_t[3][4]", }, { name: "pointer type with name", paramType: types.NewPointer(types.Typ[types.Int]), paramName: "ptr", expected: "intptr_t* ptr", }, { name: "pointer type without name", paramType: types.NewPointer(types.Typ[types.Int]), paramName: "", expected: "intptr_t*", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := hw.generateParameterDeclaration(tt.paramType, tt.paramName) if got != tt.expected { t.Errorf("generateParameterDeclaration() = %q, want %q", got, tt.expected) } }) } } // Test generateNamedStructTypedef with forward declaration func TestGenerateNamedStructTypedefWithForwardDecl(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) pkg := types.NewPackage("test", "test") // Create a self-referential struct that needs forward declaration nodeName := types.NewTypeName(0, pkg, "Node", nil) nodeNamed := types.NewNamed(nodeName, nil, nil) // Create fields including a pointer to itself fields := []*types.Var{ types.NewField(0, nil, "value", types.Typ[types.Int], false), types.NewField(0, nil, "next", types.NewPointer(nodeNamed), false), } nodeStruct := types.NewStruct(fields, nil) nodeNamed.SetUnderlying(nodeStruct) // Test generateNamedStructTypedef result := hw.generateNamedStructTypedef(nodeNamed, nodeStruct) // Should contain forward declaration (with package prefix) if !strings.Contains(result, "typedef struct test_Node test_Node;") { t.Errorf("Expected forward declaration in result: %s", result) } // Should contain the actual struct definition if !strings.Contains(result, "struct test_Node {") { t.Errorf("Expected struct definition in result: %s", result) } } // Test self-referential structures to ensure no infinite recursion func TestSelfReferentialStructure(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) pkg := types.NewPackage("test", "test") // Create a self-referential struct: Node with a pointer to itself nodeTypeName := types.NewTypeName(0, pkg, "Node", nil) nodeStruct := types.NewStruct([]*types.Var{ types.NewField(0, nil, "data", types.Typ[types.Int], false), }, nil) namedNode := types.NewNamed(nodeTypeName, nodeStruct, nil) // Add a self-referential field after creating the named type nodeStructWithPtr := types.NewStruct([]*types.Var{ types.NewField(0, nil, "data", types.Typ[types.Int], false), types.NewField(0, nil, "next", types.NewPointer(namedNode), false), }, nil) // Create a new named type with the updated struct nodeTypeNameFinal := types.NewTypeName(0, pkg, "SelfRefNode", nil) namedNodeFinal := types.NewNamed(nodeTypeNameFinal, nodeStructWithPtr, nil) // This should not cause infinite recursion err := hw.writeTypedef(namedNodeFinal) if err != nil { t.Fatalf("writeTypedef() error = %v", err) } output := hw.typeBuf.String() if !strings.Contains(output, "test_SelfRefNode") { t.Error("Expected self-referential struct typedef") } } // Test function signature dependencies func TestFunctionSignatureDependencies(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) pkg := types.NewPackage("test", "test") // Create struct type for function parameters paramStruct := types.NewStruct([]*types.Var{ types.NewField(0, nil, "id", types.Typ[types.Int], false), }, nil) paramTypeName := types.NewTypeName(0, pkg, "ParamStruct", nil) namedParam := types.NewNamed(paramTypeName, paramStruct, nil) // Create function signature with struct parameters and return values params := types.NewTuple( types.NewVar(0, nil, "input", namedParam), types.NewVar(0, nil, "count", types.Typ[types.Int]), ) results := types.NewTuple( types.NewVar(0, nil, "output", namedParam), ) funcSig := types.NewSignatureType(nil, nil, nil, params, results, false) // Test that function signature dependencies are processed err := hw.processDependentTypes(funcSig, make(map[string]bool)) if err != nil { t.Fatalf("processDependentTypes() error = %v", err) } // Test named basic type alias (should trigger the "else" branch in processDependentTypes) namedInt := types.NewNamed(types.NewTypeName(0, pkg, "MyInt", nil), types.Typ[types.Int], nil) err = hw.processDependentTypes(namedInt, make(map[string]bool)) if err != nil { t.Errorf("processDependentTypes(named int) error = %v", err) } // Test duplicate type prevention - mark type as already declared hw2 := newCHeaderWriter(prog) hw2.declaredTypes["test_DuplicateType"] = true duplicateType := types.NewNamed( types.NewTypeName(0, pkg, "DuplicateType", nil), types.Typ[types.Int], nil, ) err = hw2.writeTypedefRecursive(duplicateType, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(duplicate) error = %v", err) } if strings.Contains(hw2.typeBuf.String(), "DuplicateType") { t.Error("Should not generate typedef for already declared type") } // Test visiting map to prevent infinite recursion visiting := make(map[string]bool) visiting["test_MyInt"] = true // Mark as visiting err = hw.writeTypedefRecursive(namedInt, visiting) if err != nil { t.Errorf("writeTypedefRecursive(already visiting) error = %v", err) } // Test invalid type (should trigger cType == "" path) invalidType := types.Typ[types.Invalid] err = hw.writeTypedefRecursive(invalidType, make(map[string]bool)) if err != nil { t.Errorf("writeTypedefRecursive(invalid) error = %v", err) } } // Test error conditions and edge cases func TestEdgeCasesAndErrorConditions(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Test with properly initialized but empty function - skip nil test since it causes panic // This would require creating a complete Function object which is complex // Test writeCommonIncludes hw.typeBuf.Reset() err := hw.writeCommonIncludes() if err != nil { t.Fatalf("writeCommonIncludes() error = %v", err) } output := hw.typeBuf.String() expectedIncludes := []string{ "GoString", "GoSlice", "GoMap", "GoChan", "GoInterface", } for _, expected := range expectedIncludes { if !strings.Contains(output, expected) { t.Errorf("Expected %s in common includes", expected) } } } // Test writeTo function func TestWriteTo(t *testing.T) { prog := ssa.NewProgram(nil) hw := newCHeaderWriter(prog) // Add some content to both buffers hw.typeBuf.WriteString("typedef struct { int x; } TestStruct;\n") hw.funcBuf.WriteString("void TestFunction(void);\n") var output bytes.Buffer err := hw.writeTo(&output) if err != nil { t.Fatalf("writeTo() error = %v", err) } got := output.String() if !strings.Contains(got, "TestStruct") { t.Error("writeTo() should write type definitions") } if !strings.Contains(got, "TestFunction") { t.Error("writeTo() should write function declarations") } } // Test genHeader function func TestGenHeader(t *testing.T) { prog := ssa.NewProgram(nil) // Create a mock package pkg := prog.NewPackage("", "testpkg") var output bytes.Buffer err := genHeader(prog, []ssa.Package{pkg}, &output) if err != nil { t.Fatalf("genHeader() error = %v", err) } got := output.String() if !strings.Contains(got, "GoString") { t.Error("genHeader() should include Go runtime types") } } // Test GenCHeader function func TestGenCHeader(t *testing.T) { prog := ssa.NewProgram(nil) // Create a mock package pkg := prog.NewPackage("", "testpkg") // Create a temp file for output tmpfile, err := os.CreateTemp("", "test_header_*.h") if err != nil { t.Fatalf("Failed to create temp file: %v", err) } defer os.Remove(tmpfile.Name()) defer tmpfile.Close() err = GenHeaderFile(prog, []ssa.Package{pkg}, "testlib", tmpfile.Name(), false) if err != nil { t.Fatalf("GenCHeader() error = %v", err) } // Read the file and verify content content, err := os.ReadFile(tmpfile.Name()) if err != nil { t.Fatalf("Failed to read generated file: %v", err) } got := string(content) if !strings.Contains(got, "#ifndef") { t.Error("GenCHeader() should generate header guards") } if !strings.Contains(got, "GoString") { t.Error("GenCHeader() should include Go runtime types") } } // Test genHeader with init function coverage func TestGenHeaderWithInitFunction(t *testing.T) { prog := ssa.NewProgram(nil) // Create a package pkgPath := "github.com/test/mypackage" pkg := prog.NewPackage("", pkgPath) // Create an init function signature: func() initSig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false) // Create the init function with the expected name format initFnName := pkgPath + ".init" _ = pkg.NewFunc(initFnName, initSig, ssa.InGo) // Test genHeader which should now detect the init function var output bytes.Buffer err := genHeader(prog, []ssa.Package{pkg}, &output) if err != nil { t.Fatalf("genHeader() error = %v", err) } got := output.String() // Should contain Go runtime types if !strings.Contains(got, "GoString") { t.Error("genHeader() should include Go runtime types") } // Should contain the init function declaration with C-compatible name expectedInitName := "github_com_test_mypackage_init" if !strings.Contains(got, expectedInitName) { t.Errorf("genHeader() should include init function declaration with name %s, got: %s", expectedInitName, got) } }