summaryrefslogtreecommitdiffstats
path: root/misc/cgo/errors
diff options
context:
space:
mode:
Diffstat (limited to 'misc/cgo/errors')
-rw-r--r--misc/cgo/errors/argposition_test.go133
-rw-r--r--misc/cgo/errors/badsym_test.go219
-rw-r--r--misc/cgo/errors/errors_test.go166
-rw-r--r--misc/cgo/errors/ptr_test.go632
-rw-r--r--misc/cgo/errors/testdata/err1.go22
-rw-r--r--misc/cgo/errors/testdata/err2.go110
-rw-r--r--misc/cgo/errors/testdata/err4.go15
-rw-r--r--misc/cgo/errors/testdata/err5.go11
-rw-r--r--misc/cgo/errors/testdata/issue11097a.go15
-rw-r--r--misc/cgo/errors/testdata/issue11097b.go15
-rw-r--r--misc/cgo/errors/testdata/issue14669.go23
-rw-r--r--misc/cgo/errors/testdata/issue18452.go18
-rw-r--r--misc/cgo/errors/testdata/issue18889.go7
-rw-r--r--misc/cgo/errors/testdata/issue28069.go26
-rw-r--r--misc/cgo/errors/testdata/issue28721.go29
-rw-r--r--misc/cgo/errors/testdata/issue33061.go17
-rw-r--r--misc/cgo/errors/testdata/issue42580.go44
-rw-r--r--misc/cgo/errors/testdata/issue50710.go14
-rw-r--r--misc/cgo/errors/testdata/long_double_size.go16
-rw-r--r--misc/cgo/errors/testdata/malloc.go34
20 files changed, 1566 insertions, 0 deletions
diff --git a/misc/cgo/errors/argposition_test.go b/misc/cgo/errors/argposition_test.go
new file mode 100644
index 0000000..dd26663
--- /dev/null
+++ b/misc/cgo/errors/argposition_test.go
@@ -0,0 +1,133 @@
+// Copyright 2021 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.
+
+// Issue 42580: cmd/cgo: shifting identifier position in ast
+
+package errorstest
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+type ShortPosition struct {
+ Line int
+ Column int
+ Visited bool
+}
+
+type IdentPositionInfo map[string][]ShortPosition
+
+type Visitor struct {
+ identPosInfo IdentPositionInfo
+ fset *token.FileSet
+ t *testing.T
+}
+
+func (v *Visitor) Visit(node ast.Node) ast.Visitor {
+ if ident, ok := node.(*ast.Ident); ok {
+ if expectedPositions, ok := v.identPosInfo[ident.Name]; ok {
+ gotMatch := false
+ var errorMessage strings.Builder
+ for caseIndex, expectedPos := range expectedPositions {
+ actualPosition := v.fset.PositionFor(ident.Pos(), true)
+ errorOccured := false
+ if expectedPos.Line != actualPosition.Line {
+ fmt.Fprintf(&errorMessage, "wrong line number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Line, actualPosition.Line)
+ errorOccured = true
+ }
+ if expectedPos.Column != actualPosition.Column {
+ fmt.Fprintf(&errorMessage, "wrong column number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Column, actualPosition.Column)
+ errorOccured = true
+ }
+ if errorOccured {
+ continue
+ }
+ gotMatch = true
+ expectedPositions[caseIndex].Visited = true
+ }
+
+ if !gotMatch {
+ v.t.Errorf(errorMessage.String())
+ }
+ }
+ }
+ return v
+}
+
+func TestArgumentsPositions(t *testing.T) {
+ testdata, err := filepath.Abs("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tmpPath := t.TempDir()
+
+ dir := filepath.Join(tmpPath, "src", "testpositions")
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command("go", "tool", "cgo",
+ "-srcdir", testdata,
+ "-objdir", dir,
+ "issue42580.go")
+ cmd.Stderr = new(bytes.Buffer)
+
+ err = cmd.Run()
+ if err != nil {
+ t.Fatalf("%s: %v\n%s", cmd, err, cmd.Stderr)
+ }
+ mainProcessed, err := os.ReadFile(filepath.Join(dir, "issue42580.cgo1.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ expectation := IdentPositionInfo{
+ "checkedPointer": []ShortPosition{
+ ShortPosition{
+ Line: 32,
+ Column: 56,
+ },
+ },
+ "singleInnerPointerChecked": []ShortPosition{
+ ShortPosition{
+ Line: 37,
+ Column: 91,
+ },
+ },
+ "doublePointerChecked": []ShortPosition{
+ ShortPosition{
+ Line: 42,
+ Column: 91,
+ },
+ },
+ }
+ for _, decl := range f.Decls {
+ if fdecl, ok := decl.(*ast.FuncDecl); ok {
+ ast.Walk(&Visitor{expectation, fset, t}, fdecl.Body)
+ }
+ }
+ for ident, positions := range expectation {
+ for _, position := range positions {
+ if !position.Visited {
+ t.Errorf("Position %d:%d missed for %s ident", position.Line, position.Column, ident)
+ }
+ }
+ }
+}
diff --git a/misc/cgo/errors/badsym_test.go b/misc/cgo/errors/badsym_test.go
new file mode 100644
index 0000000..bc3ba2b
--- /dev/null
+++ b/misc/cgo/errors/badsym_test.go
@@ -0,0 +1,219 @@
+// Copyright 2020 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 errorstest
+
+import (
+ "bytes"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+ "unicode"
+)
+
+// A manually modified object file could pass unexpected characters
+// into the files generated by cgo.
+
+const magicInput = "abcdefghijklmnopqrstuvwxyz0123"
+const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//"
+
+const cSymbol = "BadSymbol" + magicInput + "Name"
+const cDefSource = "int " + cSymbol + " = 1;"
+const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }"
+
+// goSource is the source code for the trivial Go file we use.
+// We will replace TMPDIR with the temporary directory name.
+const goSource = `
+package main
+
+// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so
+// extern int F();
+import "C"
+
+func main() {
+ println(C.F())
+}
+`
+
+func TestBadSymbol(t *testing.T) {
+ dir := t.TempDir()
+
+ mkdir := func(base string) string {
+ ret := filepath.Join(dir, base)
+ if err := os.Mkdir(ret, 0755); err != nil {
+ t.Fatal(err)
+ }
+ return ret
+ }
+
+ cdir := mkdir("c")
+ godir := mkdir("go")
+
+ makeFile := func(mdir, base, source string) string {
+ ret := filepath.Join(mdir, base)
+ if err := os.WriteFile(ret, []byte(source), 0644); err != nil {
+ t.Fatal(err)
+ }
+ return ret
+ }
+
+ cDefFile := makeFile(cdir, "cdef.c", cDefSource)
+ cRefFile := makeFile(cdir, "cref.c", cRefSource)
+
+ ccCmd := cCompilerCmd(t)
+
+ cCompile := func(arg, base, src string) string {
+ out := filepath.Join(cdir, base)
+ run := append(ccCmd, arg, "-o", out, src)
+ output, err := exec.Command(run[0], run[1:]...).CombinedOutput()
+ if err != nil {
+ t.Log(run)
+ t.Logf("%s", output)
+ t.Fatal(err)
+ }
+ if err := os.Remove(src); err != nil {
+ t.Fatal(err)
+ }
+ return out
+ }
+
+ // Build a shared library that defines a symbol whose name
+ // contains magicInput.
+
+ cShared := cCompile("-shared", "c.so", cDefFile)
+
+ // Build an object file that refers to the symbol whose name
+ // contains magicInput.
+
+ cObj := cCompile("-c", "c.o", cRefFile)
+
+ // Rewrite the shared library and the object file, replacing
+ // magicInput with magicReplace. This will have the effect of
+ // introducing a symbol whose name looks like a cgo command.
+ // The cgo tool will use that name when it generates the
+ // _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag
+ // pragma into a Go file. We used to not check the pragmas in
+ // _cgo_import.go.
+
+ rewrite := func(from, to string) {
+ obj, err := os.ReadFile(from)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if bytes.Count(obj, []byte(magicInput)) == 0 {
+ t.Fatalf("%s: did not find magic string", from)
+ }
+
+ if len(magicInput) != len(magicReplace) {
+ t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace))
+ }
+
+ obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace))
+
+ if err := os.WriteFile(to, obj, 0644); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ cBadShared := filepath.Join(godir, "cbad.so")
+ rewrite(cShared, cBadShared)
+
+ cBadObj := filepath.Join(godir, "cbad.o")
+ rewrite(cObj, cBadObj)
+
+ goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir)
+ makeFile(godir, "go.go", goSourceBadObject)
+
+ makeFile(godir, "go.mod", "module badsym")
+
+ // Try to build our little package.
+ cmd := exec.Command("go", "build", "-ldflags=-v")
+ cmd.Dir = godir
+ output, err := cmd.CombinedOutput()
+
+ // The build should fail, but we want it to fail because we
+ // detected the error, not because we passed a bad flag to the
+ // C linker.
+
+ if err == nil {
+ t.Errorf("go build succeeded unexpectedly")
+ }
+
+ t.Logf("%s", output)
+
+ for _, line := range bytes.Split(output, []byte("\n")) {
+ if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) {
+ // This is the error from cgo.
+ continue
+ }
+
+ // We passed -ldflags=-v to see the external linker invocation,
+ // which should not include -badflag.
+ if bytes.Contains(line, []byte("-badflag")) {
+ t.Error("output should not mention -badflag")
+ }
+
+ // Also check for compiler errors, just in case.
+ // GCC says "unrecognized command line option".
+ // clang says "unknown argument".
+ if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) {
+ t.Error("problem should have been caught before invoking C linker")
+ }
+ }
+}
+
+func cCompilerCmd(t *testing.T) []string {
+ cc := []string{goEnv(t, "CC")}
+
+ out := goEnv(t, "GOGCCFLAGS")
+ quote := '\000'
+ start := 0
+ lastSpace := true
+ backslash := false
+ s := string(out)
+ for i, c := range s {
+ if quote == '\000' && unicode.IsSpace(c) {
+ if !lastSpace {
+ cc = append(cc, s[start:i])
+ lastSpace = true
+ }
+ } else {
+ if lastSpace {
+ start = i
+ lastSpace = false
+ }
+ if quote == '\000' && !backslash && (c == '"' || c == '\'') {
+ quote = c
+ backslash = false
+ } else if !backslash && quote == c {
+ quote = '\000'
+ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
+ backslash = true
+ } else {
+ backslash = false
+ }
+ }
+ }
+ if !lastSpace {
+ cc = append(cc, s[start:])
+ }
+
+ // Force reallocation (and avoid aliasing bugs) for tests that append to cc.
+ cc = cc[:len(cc):len(cc)]
+
+ return cc
+}
+
+func goEnv(t *testing.T, key string) string {
+ out, err := exec.Command("go", "env", key).CombinedOutput()
+ if err != nil {
+ t.Logf("go env %s\n", key)
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ return strings.TrimSpace(string(out))
+}
diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go
new file mode 100644
index 0000000..8168032
--- /dev/null
+++ b/misc/cgo/errors/errors_test.go
@@ -0,0 +1,166 @@
+// Copyright 2017 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 errorstest
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func path(file string) string {
+ return filepath.Join("testdata", file)
+}
+
+func check(t *testing.T, file string) {
+ t.Run(file, func(t *testing.T) {
+ t.Parallel()
+
+ contents, err := os.ReadFile(path(file))
+ if err != nil {
+ t.Fatal(err)
+ }
+ var errors []*regexp.Regexp
+ for i, line := range bytes.Split(contents, []byte("\n")) {
+ if bytes.HasSuffix(line, []byte("ERROR HERE")) {
+ re := regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1)))
+ errors = append(errors, re)
+ continue
+ }
+
+ if _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")); ok {
+ re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag))
+ if err != nil {
+ t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag)
+ continue
+ }
+ errors = append(errors, re)
+ }
+
+ if _, frag, ok := bytes.Cut(line, []byte("ERROR MESSAGE: ")); ok {
+ re, err := regexp.Compile(string(frag))
+ if err != nil {
+ t.Errorf("Invalid regexp after `ERROR MESSAGE: `: %#q", frag)
+ continue
+ }
+ errors = append(errors, re)
+ }
+ }
+ if len(errors) == 0 {
+ t.Fatalf("cannot find ERROR HERE")
+ }
+ expect(t, file, errors)
+ })
+}
+
+func expect(t *testing.T, file string, errors []*regexp.Regexp) {
+ dir, err := os.MkdirTemp("", filepath.Base(t.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ dst := filepath.Join(dir, strings.TrimSuffix(file, ".go"))
+ cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("expected cgo to fail but it succeeded")
+ }
+
+ lines := bytes.Split(out, []byte("\n"))
+ for _, re := range errors {
+ found := false
+ for _, line := range lines {
+ if re.Match(line) {
+ t.Logf("found match for %#q: %q", re, line)
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("expected error output to contain %#q", re)
+ }
+ }
+
+ if t.Failed() {
+ t.Logf("actual output:\n%s", out)
+ }
+}
+
+func sizeofLongDouble(t *testing.T) int {
+ cmd := exec.Command("go", "run", path("long_double_size.go"))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+
+ i, err := strconv.Atoi(strings.TrimSpace(string(out)))
+ if err != nil {
+ t.Fatalf("long_double_size.go printed invalid size: %s", out)
+ }
+ return i
+}
+
+func TestReportsTypeErrors(t *testing.T) {
+ for _, file := range []string{
+ "err1.go",
+ "err2.go",
+ "err5.go",
+ "issue11097a.go",
+ "issue11097b.go",
+ "issue18452.go",
+ "issue18889.go",
+ "issue28721.go",
+ "issue33061.go",
+ "issue50710.go",
+ } {
+ check(t, file)
+ }
+
+ if sizeofLongDouble(t) > 8 {
+ for _, file := range []string{
+ "err4.go",
+ "issue28069.go",
+ } {
+ check(t, file)
+ }
+ }
+}
+
+func TestToleratesOptimizationFlag(t *testing.T) {
+ for _, cflags := range []string{
+ "",
+ "-O",
+ } {
+ cflags := cflags
+ t.Run(cflags, func(t *testing.T) {
+ t.Parallel()
+
+ cmd := exec.Command("go", "build", path("issue14669.go"))
+ cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ })
+ }
+}
+
+func TestMallocCrashesOnNil(t *testing.T) {
+ t.Parallel()
+
+ cmd := exec.Command("go", "run", path("malloc.go"))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Fatalf("succeeded unexpectedly")
+ }
+}
diff --git a/misc/cgo/errors/ptr_test.go b/misc/cgo/errors/ptr_test.go
new file mode 100644
index 0000000..0f39dc8
--- /dev/null
+++ b/misc/cgo/errors/ptr_test.go
@@ -0,0 +1,632 @@
+// Copyright 2015 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.
+
+// Tests that cgo detects invalid pointer passing at runtime.
+
+package errorstest
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync/atomic"
+ "testing"
+)
+
+var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
+
+// ptrTest is the tests without the boilerplate.
+type ptrTest struct {
+ name string // for reporting
+ c string // the cgo comment
+ c1 string // cgo comment forced into non-export cgo file
+ imports []string // a list of imports
+ support string // supporting functions
+ body string // the body of the main function
+ extra []extra // extra files
+ fail bool // whether the test should fail
+ expensive bool // whether the test requires the expensive check
+}
+
+type extra struct {
+ name string
+ contents string
+}
+
+var ptrTests = []ptrTest{
+ {
+ // Passing a pointer to a struct that contains a Go pointer.
+ name: "ptr1",
+ c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
+ body: `C.f1(&C.s1{new(C.int)})`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to a struct that contains a Go pointer.
+ name: "ptr2",
+ c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
+ body: `p := &C.s2{new(C.int)}; C.f2(p)`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to an int field of a Go struct
+ // that (irrelevantly) contains a Go pointer.
+ name: "ok1",
+ c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
+ body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
+ fail: false,
+ },
+ {
+ // Passing a pointer to a pointer field of a Go struct.
+ name: "ptrfield",
+ c: `struct s4 { int i; int *p; }; void f4(int **p) {}`,
+ body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to a pointer field of a Go
+ // struct, where the field does not contain a Go
+ // pointer, but another field (irrelevantly) does.
+ name: "ptrfieldok",
+ c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
+ body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice with no Go pointers.
+ name: "sliceok1",
+ c: `void f6(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice with a Go pointer.
+ name: "sliceptr1",
+ c: `void f7(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
+ fail: true,
+ },
+ {
+ // Passing the address of a slice with a Go pointer,
+ // where we are passing the address of an element that
+ // is not a Go pointer.
+ name: "sliceptr2",
+ c: `void f8(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
+ fail: true,
+ },
+ {
+ // Passing the address of a slice that is an element
+ // in a struct only looks at the slice.
+ name: "sliceok2",
+ c: `void f9(void **p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S9 struct { p *int; s []unsafe.Pointer }`,
+ body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice of an array that is
+ // an element in a struct, with a type conversion.
+ name: "sliceok3",
+ c: `void f10(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S10 struct { p *int; a [4]byte }`,
+ body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice of an array that is
+ // an element in a struct, with a type conversion.
+ name: "sliceok4",
+ c: `typedef void* PV11; void f11(PV11 p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S11 struct { p *int; a [4]byte }`,
+ body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
+ fail: false,
+ },
+ {
+ // Passing the address of a static variable with no
+ // pointers doesn't matter.
+ name: "varok",
+ c: `void f12(char** parg) {}`,
+ support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
+ body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a static variable with
+ // pointers does matter.
+ name: "var1",
+ c: `void f13(char*** parg) {}`,
+ support: `var hello13 = [...]*C.char{new(C.char)}`,
+ body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
+ fail: true,
+ },
+ {
+ // Storing a Go pointer into C memory should fail.
+ name: "barrier",
+ c: `#include <stdlib.h>
+ char **f14a() { return malloc(sizeof(char*)); }
+ void f14b(char **p) {}`,
+ body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Storing a Go pointer into C memory by assigning a
+ // large value should fail.
+ name: "barrierstruct",
+ c: `#include <stdlib.h>
+ struct s15 { char *a[10]; };
+ struct s15 *f15() { return malloc(sizeof(struct s15)); }
+ void f15b(struct s15 *p) {}`,
+ body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Storing a Go pointer into C memory using a slice
+ // copy should fail.
+ name: "barrierslice",
+ c: `#include <stdlib.h>
+ struct s16 { char *a[10]; };
+ struct s16 *f16() { return malloc(sizeof(struct s16)); }
+ void f16b(struct s16 *p) {}`,
+ body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // A very large value uses a GC program, which is a
+ // different code path.
+ name: "barriergcprogarray",
+ c: `#include <stdlib.h>
+ struct s17 { char *a[32769]; };
+ struct s17 *f17() { return malloc(sizeof(struct s17)); }
+ void f17b(struct s17 *p) {}`,
+ body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Similar case, with a source on the heap.
+ name: "barriergcprogarrayheap",
+ c: `#include <stdlib.h>
+ struct s18 { char *a[32769]; };
+ struct s18 *f18() { return malloc(sizeof(struct s18)); }
+ void f18b(struct s18 *p) {}
+ void f18c(void *p) {}`,
+ imports: []string{"unsafe"},
+ body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // A GC program with a struct.
+ name: "barriergcprogstruct",
+ c: `#include <stdlib.h>
+ struct s19a { char *a[32769]; };
+ struct s19b { struct s19a f; };
+ struct s19b *f19() { return malloc(sizeof(struct s19b)); }
+ void f19b(struct s19b *p) {}`,
+ body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Similar case, with a source on the heap.
+ name: "barriergcprogstructheap",
+ c: `#include <stdlib.h>
+ struct s20a { char *a[32769]; };
+ struct s20b { struct s20a f; };
+ struct s20b *f20() { return malloc(sizeof(struct s20b)); }
+ void f20b(struct s20b *p) {}
+ void f20c(void *p) {}`,
+ imports: []string{"unsafe"},
+ body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Exported functions may not return Go pointers.
+ name: "export1",
+ c: `extern unsigned char *GoFn21();`,
+ support: `//export GoFn21
+ func GoFn21() *byte { return new(byte) }`,
+ body: `C.GoFn21()`,
+ fail: true,
+ },
+ {
+ // Returning a C pointer is fine.
+ name: "exportok",
+ c: `#include <stdlib.h>
+ extern unsigned char *GoFn22();`,
+ support: `//export GoFn22
+ func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
+ body: `C.GoFn22()`,
+ },
+ {
+ // Passing a Go string is fine.
+ name: "passstring",
+ c: `#include <stddef.h>
+ typedef struct { const char *p; ptrdiff_t n; } gostring23;
+ gostring23 f23(gostring23 s) { return s; }`,
+ imports: []string{"unsafe"},
+ body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
+ },
+ {
+ // Passing a slice of Go strings fails.
+ name: "passstringslice",
+ c: `void f24(void *p) {}`,
+ imports: []string{"strings", "unsafe"},
+ support: `type S24 struct { a [1]string }`,
+ body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
+ fail: true,
+ },
+ {
+ // Exported functions may not return strings.
+ name: "retstring",
+ c: `extern void f25();`,
+ imports: []string{"strings"},
+ support: `//export GoStr25
+ func GoStr25() string { return strings.Repeat("a", 2) }`,
+ body: `C.f25()`,
+ c1: `#include <stddef.h>
+ typedef struct { const char *p; ptrdiff_t n; } gostring25;
+ extern gostring25 GoStr25();
+ void f25() { GoStr25(); }`,
+ fail: true,
+ },
+ {
+ // Don't check non-pointer data.
+ // Uses unsafe code to get a pointer we shouldn't check.
+ // Although we use unsafe, the uintptr represents an integer
+ // that happens to have the same representation as a pointer;
+ // that is, we are testing something that is not unsafe.
+ name: "ptrdata1",
+ c: `#include <stdlib.h>
+ void f26(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
+ body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`,
+ fail: false,
+ },
+ {
+ // Like ptrdata1, but with a type that uses a GC program.
+ name: "ptrdata2",
+ c: `#include <stdlib.h>
+ void f27(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
+ body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`,
+ fail: false,
+ },
+ {
+ // Check deferred pointers when they are used, not
+ // when the defer statement is run.
+ name: "defer1",
+ c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
+ body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
+ fail: true,
+ },
+ {
+ // Check a pointer to a union if the union has any
+ // pointer fields.
+ name: "union1",
+ c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
+ imports: []string{"unsafe"},
+ body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
+ fail: true,
+ },
+ {
+ // Don't check a pointer to a union if the union does
+ // not have any pointer fields.
+ // Like ptrdata1 above, the uintptr represents an
+ // integer that happens to have the same
+ // representation as a pointer.
+ name: "union2",
+ c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
+ imports: []string{"unsafe"},
+ body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
+ fail: false,
+ },
+ {
+ // Test preemption while entering a cgo call. Issue #21306.
+ name: "preemptduringcall",
+ c: `void f30() {}`,
+ imports: []string{"runtime", "sync"},
+ body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
+ fail: false,
+ },
+ {
+ // Test poller deadline with cgocheck=2. Issue #23435.
+ name: "deadline",
+ c: `#define US31 10`,
+ imports: []string{"os", "time"},
+ body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
+ fail: false,
+ },
+ {
+ // Test for double evaluation of channel receive.
+ name: "chanrecv",
+ c: `void f32(char** p) {}`,
+ imports: []string{"time"},
+ body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`,
+ fail: false,
+ },
+ {
+ // Test that converting the address of a struct field
+ // to unsafe.Pointer still just checks that field.
+ // Issue #25941.
+ name: "structfield",
+ c: `void f33(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
+ body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
+ fail: false,
+ },
+ {
+ // Test that converting multiple struct field
+ // addresses to unsafe.Pointer still just checks those
+ // fields. Issue #25941.
+ name: "structfield2",
+ c: `void f34(void* p, int r, void* s) {}`,
+ imports: []string{"unsafe"},
+ support: `type S34 struct { a [8]byte; p *int; b int64; }`,
+ body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
+ fail: false,
+ },
+ {
+ // Test that second argument to cgoCheckPointer is
+ // evaluated when a deferred function is deferred, not
+ // when it is run.
+ name: "defer2",
+ c: `void f35(char **pc) {}`,
+ support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
+ body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
+ fail: false,
+ },
+ {
+ // Test that indexing into a function call still
+ // examines only the slice being indexed.
+ name: "buffer",
+ c: `void f36(void *p) {}`,
+ imports: []string{"bytes", "unsafe"},
+ body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
+ fail: false,
+ },
+ {
+ // Test that bgsweep releasing a finalizer is OK.
+ name: "finalizer",
+ c: `// Nothing to declare.`,
+ imports: []string{"os"},
+ support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
+ body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
+ fail: false,
+ },
+ {
+ // Test that converting generated struct to interface is OK.
+ name: "structof",
+ c: `// Nothing to declare.`,
+ imports: []string{"reflect"},
+ support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
+ body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
+ fail: false,
+ },
+ {
+ // Test that a converted address of a struct field results
+ // in a check for just that field and not the whole struct.
+ name: "structfieldcast",
+ c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`,
+ support: `type S40 struct { p *int; a C.struct_S40i }`,
+ body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`,
+ fail: false,
+ },
+}
+
+func TestPointerChecks(t *testing.T) {
+ dir, exe := buildPtrTests(t)
+
+ // We (TestPointerChecks) return before the parallel subtest functions do,
+ // so we can't just defer os.RemoveAll(dir). Instead we have to wait for
+ // the parallel subtests to finish. This code looks racy but is not:
+ // the add +1 run in serial before testOne blocks. The -1 run in parallel
+ // after testOne finishes.
+ var pending int32
+ for _, pt := range ptrTests {
+ pt := pt
+ t.Run(pt.name, func(t *testing.T) {
+ atomic.AddInt32(&pending, +1)
+ defer func() {
+ if atomic.AddInt32(&pending, -1) == 0 {
+ os.RemoveAll(dir)
+ }
+ }()
+ testOne(t, pt, exe)
+ })
+ }
+}
+
+func buildPtrTests(t *testing.T) (dir, exe string) {
+ var gopath string
+ if *tmp != "" {
+ gopath = *tmp
+ dir = ""
+ } else {
+ d, err := os.MkdirTemp("", filepath.Base(t.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ dir = d
+ gopath = d
+ }
+
+ src := filepath.Join(gopath, "src", "ptrtest")
+ if err := os.MkdirAll(src, 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ // Prepare two cgo inputs: one for standard cgo and one for //export cgo.
+ // (The latter cannot have C definitions, only declarations.)
+ var cgo1, cgo2 bytes.Buffer
+ fmt.Fprintf(&cgo1, "package main\n\n/*\n")
+ fmt.Fprintf(&cgo2, "package main\n\n/*\n")
+
+ // C code
+ for _, pt := range ptrTests {
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ cgo = &cgo2
+ }
+ fmt.Fprintf(cgo, "%s\n", pt.c)
+ fmt.Fprintf(&cgo1, "%s\n", pt.c1)
+ }
+ fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
+ fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
+
+ // Imports
+ did1 := make(map[string]bool)
+ did2 := make(map[string]bool)
+ did1["os"] = true // for ptrTestMain
+ fmt.Fprintf(&cgo1, "import \"os\"\n")
+
+ for _, pt := range ptrTests {
+ did := did1
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ did = did2
+ cgo = &cgo2
+ }
+ for _, imp := range pt.imports {
+ if !did[imp] {
+ did[imp] = true
+ fmt.Fprintf(cgo, "import %q\n", imp)
+ }
+ }
+ }
+
+ // Func support and bodies.
+ for _, pt := range ptrTests {
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ cgo = &cgo2
+ }
+ fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
+ }
+
+ // Func list and main dispatch.
+ fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
+ for _, pt := range ptrTests {
+ fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
+ }
+ fmt.Fprintf(&cgo1, "}\n\n")
+ fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
+
+ if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command("go", "build", "-o", "ptrtest.exe")
+ cmd.Dir = src
+ cmd.Env = append(os.Environ(), "GOPATH="+gopath)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go build: %v\n%s", err, out)
+ }
+
+ return dir, filepath.Join(src, "ptrtest.exe")
+}
+
+const ptrTestMain = `
+func main() {
+ for _, arg := range os.Args[1:] {
+ f := funcs[arg]
+ if f == nil {
+ panic("missing func "+arg)
+ }
+ f()
+ }
+}
+`
+
+var csem = make(chan bool, 16)
+
+func testOne(t *testing.T, pt ptrTest, exe string) {
+ t.Parallel()
+
+ // Run the tests in parallel, but don't run too many
+ // executions in parallel, to avoid overloading the system.
+ runcmd := func(cgocheck string) ([]byte, error) {
+ csem <- true
+ defer func() { <-csem }()
+ cmd := exec.Command(exe, pt.name)
+ cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
+ return cmd.CombinedOutput()
+ }
+
+ if pt.expensive {
+ buf, err := runcmd("1")
+ if err != nil {
+ t.Logf("%s", buf)
+ if pt.fail {
+ t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
+ } else {
+ t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
+ }
+ }
+
+ }
+
+ cgocheck := ""
+ if pt.expensive {
+ cgocheck = "2"
+ }
+
+ buf, err := runcmd(cgocheck)
+ if pt.fail {
+ if err == nil {
+ t.Logf("%s", buf)
+ t.Fatalf("did not fail as expected")
+ } else if !bytes.Contains(buf, []byte("Go pointer")) {
+ t.Logf("%s", buf)
+ t.Fatalf("did not print expected error (failed with %v)", err)
+ }
+ } else {
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly: %v", err)
+ }
+
+ if !pt.expensive {
+ // Make sure it passes with the expensive checks.
+ buf, err := runcmd("2")
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly with expensive checks: %v", err)
+ }
+ }
+ }
+
+ if pt.fail {
+ buf, err := runcmd("0")
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
+ }
+ }
+}
diff --git a/misc/cgo/errors/testdata/err1.go b/misc/cgo/errors/testdata/err1.go
new file mode 100644
index 0000000..ced7443
--- /dev/null
+++ b/misc/cgo/errors/testdata/err1.go
@@ -0,0 +1,22 @@
+// Copyright 2013 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
+
+/*
+#cgo LDFLAGS: -L/nonexist
+
+void test() {
+ xxx; // ERROR HERE
+}
+
+// Issue 8442. Cgo output unhelpful error messages for
+// invalid C preambles.
+void issue8442foo(UNDEF*); // ERROR HERE
+*/
+import "C"
+
+func main() {
+ C.test()
+}
diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go
new file mode 100644
index 0000000..aa94158
--- /dev/null
+++ b/misc/cgo/errors/testdata/err2.go
@@ -0,0 +1,110 @@
+// Copyright 2013 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
+
+/*
+#include <stdio.h>
+
+typedef struct foo foo_t;
+typedef struct bar bar_t;
+
+foo_t *foop;
+
+long double x = 0;
+
+static int transform(int x) { return x; }
+
+typedef void v;
+void F(v** p) {}
+
+void fvi(void *p, int x) {}
+
+void fppi(int** p) {}
+
+int i;
+void fi(int i) {}
+*/
+import "C"
+import (
+ "unsafe"
+)
+
+func main() {
+ s := ""
+ _ = s
+ C.malloc(s) // ERROR HERE
+
+ x := (*C.bar_t)(nil)
+ C.foop = x // ERROR HERE
+
+ // issue 13129: used to output error about C.unsignedshort with CC=clang
+ var x1 C.ushort
+ x1 = int(0) // ERROR HERE: C\.ushort
+
+ // issue 13423
+ _ = C.fopen() // ERROR HERE
+
+ // issue 13467
+ var x2 rune = '✈'
+ var _ rune = C.transform(x2) // ERROR HERE: C\.int
+
+ // issue 13635: used to output error about C.unsignedchar.
+ // This test tests all such types.
+ var (
+ _ C.uchar = "uc" // ERROR HERE: C\.uchar
+ _ C.schar = "sc" // ERROR HERE: C\.schar
+ _ C.ushort = "us" // ERROR HERE: C\.ushort
+ _ C.uint = "ui" // ERROR HERE: C\.uint
+ _ C.ulong = "ul" // ERROR HERE: C\.ulong
+ _ C.longlong = "ll" // ERROR HERE: C\.longlong
+ _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong
+ _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat
+ _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble
+ )
+
+ // issue 13830
+ // cgo converts C void* to Go unsafe.Pointer, so despite appearances C
+ // void** is Go *unsafe.Pointer. This test verifies that we detect the
+ // problem at build time.
+ {
+ type v [0]byte
+
+ f := func(p **v) {
+ C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE
+ }
+ var p *v
+ f(&p)
+ }
+
+ // issue 16116
+ _ = C.fvi(1) // ERROR HERE
+
+ // Issue 16591: Test that we detect an invalid call that was being
+ // hidden by a type conversion inserted by cgo checking.
+ {
+ type x *C.int
+ var p *x
+ C.fppi(p) // ERROR HERE
+ }
+
+ // issue 26745
+ _ = func(i int) int {
+ // typecheck reports at column 14 ('+'), but types2 reports at
+ // column 10 ('C').
+ // TODO(mdempsky): Investigate why, and see if types2 can be
+ // updated to match typecheck behavior.
+ return C.i + 1 // ERROR HERE: \b(10|14)\b
+ }
+ _ = func(i int) {
+ // typecheck reports at column 7 ('('), but types2 reports at
+ // column 8 ('i'). The types2 position is more correct, but
+ // updating typecheck here is fundamentally challenging because of
+ // IR limitations.
+ C.fi(i) // ERROR HERE: \b(7|8)\b
+ }
+
+ C.fi = C.fi // ERROR HERE
+
+}
diff --git a/misc/cgo/errors/testdata/err4.go b/misc/cgo/errors/testdata/err4.go
new file mode 100644
index 0000000..8e5f78e
--- /dev/null
+++ b/misc/cgo/errors/testdata/err4.go
@@ -0,0 +1,15 @@
+// Copyright 2017 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
+
+/*
+long double x = 0;
+*/
+import "C"
+
+func main() {
+ _ = C.x // ERROR HERE
+ _ = C.x
+}
diff --git a/misc/cgo/errors/testdata/err5.go b/misc/cgo/errors/testdata/err5.go
new file mode 100644
index 0000000..c12a290
--- /dev/null
+++ b/misc/cgo/errors/testdata/err5.go
@@ -0,0 +1,11 @@
+// Copyright 2023 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
+
+//line /tmp/_cgo_.go:1
+//go:cgo_dynamic_linker "/elf/interp"
+// ERROR MESSAGE: only allowed in cgo-generated code
+
+func main() {}
diff --git a/misc/cgo/errors/testdata/issue11097a.go b/misc/cgo/errors/testdata/issue11097a.go
new file mode 100644
index 0000000..028d10c
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue11097a.go
@@ -0,0 +1,15 @@
+// Copyright 2015 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
+
+/*
+//enum test { foo, bar };
+*/
+import "C"
+
+func main() {
+ var a = C.enum_test(1) // ERROR HERE
+ _ = a
+}
diff --git a/misc/cgo/errors/testdata/issue11097b.go b/misc/cgo/errors/testdata/issue11097b.go
new file mode 100644
index 0000000..b00f24f
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue11097b.go
@@ -0,0 +1,15 @@
+// Copyright 2015 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
+
+/*
+//enum test { foo, bar };
+*/
+import "C"
+
+func main() {
+ p := new(C.enum_test) // ERROR HERE
+ _ = p
+}
diff --git a/misc/cgo/errors/testdata/issue14669.go b/misc/cgo/errors/testdata/issue14669.go
new file mode 100644
index 0000000..04d2bcb
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue14669.go
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+// Issue 14669: test that fails when build with CGO_CFLAGS selecting
+// optimization.
+
+package p
+
+/*
+const int E = 1;
+
+typedef struct s {
+ int c;
+} s;
+*/
+import "C"
+
+func F() {
+ _ = C.s{
+ c: C.E,
+ }
+}
diff --git a/misc/cgo/errors/testdata/issue18452.go b/misc/cgo/errors/testdata/issue18452.go
new file mode 100644
index 0000000..0386d76
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue18452.go
@@ -0,0 +1,18 @@
+// Copyright 2017 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.
+
+// Issue 18452: show pos info in undefined name errors
+
+package p
+
+import (
+ "C"
+ "fmt"
+)
+
+func a() {
+ fmt.Println("Hello, world!")
+ C.function_that_does_not_exist() // ERROR HERE
+ C.pi // ERROR HERE
+}
diff --git a/misc/cgo/errors/testdata/issue18889.go b/misc/cgo/errors/testdata/issue18889.go
new file mode 100644
index 0000000..bba6b8f
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue18889.go
@@ -0,0 +1,7 @@
+package main
+
+import "C"
+
+func main() {
+ _ = C.malloc // ERROR HERE
+}
diff --git a/misc/cgo/errors/testdata/issue28069.go b/misc/cgo/errors/testdata/issue28069.go
new file mode 100644
index 0000000..e19a3b4
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue28069.go
@@ -0,0 +1,26 @@
+// Copyright 2018 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.
+
+// Test that the error message for an unrepresentable typedef in a
+// union appears on the right line. This test is only run if the size
+// of long double is larger than 64.
+
+package main
+
+/*
+typedef long double Float128;
+
+typedef struct SV {
+ union {
+ Float128 float128;
+ } value;
+} SV;
+*/
+import "C"
+
+type ts struct {
+ tv *C.SV // ERROR HERE
+}
+
+func main() {}
diff --git a/misc/cgo/errors/testdata/issue28721.go b/misc/cgo/errors/testdata/issue28721.go
new file mode 100644
index 0000000..0eb2a92
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue28721.go
@@ -0,0 +1,29 @@
+// Copyright 2018 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.
+
+// cgo should reject the use of mangled C names.
+
+package main
+
+/*
+typedef struct a {
+ int i;
+} a;
+void fn(void) {}
+*/
+import "C"
+
+type B _Ctype_struct_a // ERROR HERE
+
+var a _Ctype_struct_a // ERROR HERE
+
+type A struct {
+ a *_Ctype_struct_a // ERROR HERE
+}
+
+var notExist _Ctype_NotExist // ERROR HERE
+
+func main() {
+ _Cfunc_fn() // ERROR HERE
+}
diff --git a/misc/cgo/errors/testdata/issue33061.go b/misc/cgo/errors/testdata/issue33061.go
new file mode 100644
index 0000000..77d5f7a
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue33061.go
@@ -0,0 +1,17 @@
+// Copyright 2019 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.
+
+// cgo shouldn't crash if there is an extra argument with a C reference.
+
+package main
+
+// void F(void* p) {};
+import "C"
+
+import "unsafe"
+
+func F() {
+ var i int
+ C.F(unsafe.Pointer(&i), C.int(0)) // ERROR HERE
+}
diff --git a/misc/cgo/errors/testdata/issue42580.go b/misc/cgo/errors/testdata/issue42580.go
new file mode 100644
index 0000000..aba80df
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue42580.go
@@ -0,0 +1,44 @@
+// Copyright 2021 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.
+
+// Issue 42580: cmd/cgo: shifting identifier position in ast
+
+package cgotest
+
+// typedef int (*intFunc) ();
+//
+// char* strarg = "";
+//
+// int func_with_char(char* arg, void* dummy)
+// {return 5;}
+//
+// int* get_arr(char* arg, void* dummy)
+// {return NULL;}
+import "C"
+import "unsafe"
+
+// Test variables
+var (
+ checkedPointer = []byte{1}
+ doublePointerChecked = []byte{1}
+ singleInnerPointerChecked = []byte{1}
+)
+
+// This test checks the positions of variable identifiers.
+// Changing the positions of the test variables idents after this point will break the test.
+
+func TestSingleArgumentCast() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(&checkedPointer[0])), unsafe.Pointer(C.strarg))
+ return retcode
+}
+
+func TestSingleArgumentCastRecFuncAsSimpleArg() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&singleInnerPointerChecked[0])), unsafe.Pointer(C.strarg)))), nil)
+ return retcode
+}
+
+func TestSingleArgumentCastRecFunc() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&doublePointerChecked[0])), unsafe.Pointer(C.strarg)))), unsafe.Pointer(C.strarg))
+ return retcode
+}
diff --git a/misc/cgo/errors/testdata/issue50710.go b/misc/cgo/errors/testdata/issue50710.go
new file mode 100644
index 0000000..dffea22
--- /dev/null
+++ b/misc/cgo/errors/testdata/issue50710.go
@@ -0,0 +1,14 @@
+// Copyright 2022 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
+
+// size_t StrLen(_GoString_ s) {
+// return _GoStringLen(s);
+// }
+import "C"
+
+func main() {
+ C.StrLen1() // ERROR HERE
+}
diff --git a/misc/cgo/errors/testdata/long_double_size.go b/misc/cgo/errors/testdata/long_double_size.go
new file mode 100644
index 0000000..8b797f8
--- /dev/null
+++ b/misc/cgo/errors/testdata/long_double_size.go
@@ -0,0 +1,16 @@
+// Copyright 2017 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
+
+/*
+const int sizeofLongDouble = sizeof(long double);
+*/
+import "C"
+
+import "fmt"
+
+func main() {
+ fmt.Println(C.sizeofLongDouble)
+}
diff --git a/misc/cgo/errors/testdata/malloc.go b/misc/cgo/errors/testdata/malloc.go
new file mode 100644
index 0000000..65da020
--- /dev/null
+++ b/misc/cgo/errors/testdata/malloc.go
@@ -0,0 +1,34 @@
+// Copyright 2016 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.
+
+// Test that C.malloc does not return nil.
+
+package main
+
+// #include <stdlib.h>
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func main() {
+ var size C.size_t
+ size--
+
+ // The Dragonfly libc succeeds when asked to allocate
+ // 0xffffffffffffffff bytes, so pass a different value that
+ // causes it to fail.
+ if runtime.GOOS == "dragonfly" {
+ size = C.size_t(0x7fffffff << (32 * (^uintptr(0) >> 63)))
+ }
+
+ p := C.malloc(size)
+ if p == nil {
+ fmt.Println("malloc: C.malloc returned nil")
+ // Just exit normally--the test script expects this
+ // program to crash, so exiting normally indicates failure.
+ }
+}