diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go index 7e9a274d05628e..81ae4b3f7a3ef6 100644 --- a/src/cmd/cgo/internal/testcshared/cshared_test.go +++ b/src/cmd/cgo/internal/testcshared/cshared_test.go @@ -880,3 +880,52 @@ func TestIssue36233(t *testing.T) { t.Error("missing functions") } } + +func TestIssue68411(t *testing.T) { + globalSkip(t) + testenv.MustHaveCGO(t) + + t.Parallel() + + // Test that the export header uses a void function parameter for + // exported Go functions with no parameters. + + tmpdir, err := os.MkdirTemp("", "cshared-TestIssue68411") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + const exportHeader = "issue68411.h" + + run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue68411/issue68411.go") + data, err := os.ReadFile(exportHeader) + if err != nil { + t.Fatal(err) + } + + funcs := []struct{ name, signature string }{ + {"exportFuncWithNoParams", "void exportFuncWithNoParams(void)"}, + {"exportFuncWithParams", "exportFuncWithParams(GoInt a, GoInt b)"}, + } + + scanner := bufio.NewScanner(bytes.NewReader(data)) + var found int + for scanner.Scan() { + b := scanner.Bytes() + for _, fn := range funcs { + if bytes.Contains(b, []byte(fn.name)) { + found++ + if !bytes.Contains(b, []byte(fn.signature)) { + t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature) + } + } + } + } + if err = scanner.Err(); err != nil { + t.Errorf("scanner encountered error: %v", err) + } + if found != len(funcs) { + t.Error("missing functions") + } +} diff --git a/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go new file mode 100644 index 00000000000000..a761cc38985bbf --- /dev/null +++ b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +//export exportFuncWithNoParams +func exportFuncWithNoParams() {} + +//export exportFuncWithParams +func exportFuncWithParams(a, b int) {} + +func main() {} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 954c4b70c954eb..d53bbe9cdfde4e 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1012,13 +1012,18 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { s += p.cgoType(fn.Recv.List[0].Type).C.String() s += " recv" } - forFieldList(fntype.Params, - func(i int, aname string, atype ast.Expr) { - if i > 0 || fn.Recv != nil { - s += ", " - } - s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i)) - }) + + if len(fntype.Params.List) > 0 { + forFieldList(fntype.Params, + func(i int, aname string, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + s += ", " + } + s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i)) + }) + } else { + s += "void" + } s += ")" if len(exp.Doc) > 0 {