Skip to content

Commit 6a1bb13

Browse files
aykevldeadprogram
authored andcommitted
cgo: add tests for errors
This commit adds tests for CGo preprocessing. There are various errors that can be reported while preprocessing, and they should integrate well with the compiler (including accurate source location tracking). Also allow CGo preprocessing to continue after Clang encountered an error, for a better view of what happened.
1 parent f0bb3c0 commit 6a1bb13

File tree

4 files changed

+107
-10
lines changed

4 files changed

+107
-10
lines changed

cgo/cgo_test.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"go/types"
1212
"io/ioutil"
1313
"path/filepath"
14+
"runtime"
1415
"strings"
1516
"testing"
1617
)
@@ -21,7 +22,7 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
2122
func TestCGo(t *testing.T) {
2223
var cflags = []string{"--target=armv6m-none-eabi"}
2324

24-
for _, name := range []string{"basic", "types"} {
25+
for _, name := range []string{"basic", "errors", "types"} {
2526
name := name // avoid a race condition
2627
t.Run(name, func(t *testing.T) {
2728
t.Parallel()
@@ -35,23 +36,19 @@ func TestCGo(t *testing.T) {
3536
}
3637

3738
// Process the AST with CGo.
38-
cgoAST, errs := Process([]*ast.File{f}, "testdata", fset, cflags)
39-
for _, err := range errs {
40-
t.Errorf("error during CGo processing: %v", err)
41-
}
39+
cgoAST, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
4240

4341
// Check the AST for type errors.
44-
hasTypeError := false
42+
var typecheckErrors []error
4543
config := types.Config{
4644
Error: func(err error) {
47-
t.Error("typecheck error:", err)
48-
hasTypeError = true
45+
typecheckErrors = append(typecheckErrors, err)
4946
},
5047
Importer: simpleImporter{},
5148
Sizes: types.SizesFor("gccgo", "arm"),
5249
}
5350
_, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
54-
if err != nil && !hasTypeError {
51+
if err != nil && len(typecheckErrors) == 0 {
5552
// Only report errors when no type errors are found (an
5653
// unexpected condition).
5754
t.Error(err)
@@ -61,6 +58,20 @@ func TestCGo(t *testing.T) {
6158
// becomes easier to read (and will hopefully change less with CGo
6259
// changes).
6360
buf := &bytes.Buffer{}
61+
if len(cgoErrors) != 0 {
62+
buf.WriteString("// CGo errors:\n")
63+
for _, err := range cgoErrors {
64+
buf.WriteString(formatDiagnostic(err))
65+
}
66+
buf.WriteString("\n")
67+
}
68+
if len(typecheckErrors) != 0 {
69+
buf.WriteString("// Type checking errors after CGo processing:\n")
70+
for _, err := range typecheckErrors {
71+
buf.WriteString(formatDiagnostic(err))
72+
}
73+
buf.WriteString("\n")
74+
}
6475
err = format.Node(buf, fset, cgoAST)
6576
if err != nil {
6677
t.Errorf("could not write out CGo AST: %v", err)
@@ -107,3 +118,14 @@ func (i simpleImporter) Import(path string) (*types.Package, error) {
107118
return nil, fmt.Errorf("importer not implemented for package %s", path)
108119
}
109120
}
121+
122+
// formatDiagnostics formats the error message to be an indented comment. It
123+
// also fixes Windows path name issues (backward slashes).
124+
func formatDiagnostic(err error) string {
125+
msg := err.Error()
126+
if runtime.GOOS == "windows" {
127+
// Fix Windows path slashes.
128+
msg = strings.Replace(msg, "testdata\\", "testdata/", -1)
129+
}
130+
return "// " + msg + "\n"
131+
}

cgo/libclang.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
132132
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
133133
}
134134
}
135-
return
136135
}
137136

138137
ref := storedRefs.Put(p)

cgo/testdata/errors.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
/*
4+
#warning some warning
5+
6+
typedef struct {
7+
int x;
8+
int y;
9+
} point_t;
10+
11+
typedef someType noType; // undefined type
12+
13+
#define SOME_CONST_1 5) // invalid const syntax
14+
#define SOME_CONST_2 6) // const not used (so no error)
15+
#define SOME_CONST_3 1234 // const too large for byte
16+
*/
17+
import "C"
18+
19+
// Make sure that errors for the following lines won't change with future
20+
// additions to the CGo preamble.
21+
//line errors.go:100
22+
var (
23+
// constant too large
24+
_ C.uint8_t = 2 << 10
25+
26+
// z member does not exist
27+
_ C.point_t = C.point_t{z: 3}
28+
29+
// constant has syntax error
30+
_ = C.SOME_CONST_1
31+
32+
_ byte = C.SOME_CONST_3
33+
)

cgo/testdata/errors.out.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// CGo errors:
2+
// testdata/errors.go:4:2: warning: some warning
3+
// testdata/errors.go:11:9: error: unknown type name 'someType'
4+
// testdata/errors.go:13:23: unexpected token )
5+
6+
// Type checking errors after CGo processing:
7+
// testdata/errors.go:102: 2 << 10 (untyped int constant 2048) overflows uint8
8+
// testdata/errors.go:105: unknown field z in struct literal
9+
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
10+
// testdata/errors.go:110: C.SOME_CONST_3 (untyped int constant 1234) overflows byte
11+
12+
package main
13+
14+
import "unsafe"
15+
16+
var _ unsafe.Pointer
17+
18+
const C.SOME_CONST_3 = 1234
19+
20+
type C.int16_t = int16
21+
type C.int32_t = int32
22+
type C.int64_t = int64
23+
type C.int8_t = int8
24+
type C.uint16_t = uint16
25+
type C.uint32_t = uint32
26+
type C.uint64_t = uint64
27+
type C.uint8_t = uint8
28+
type C.uintptr_t = uintptr
29+
type C.char uint8
30+
type C.int int32
31+
type C.long int32
32+
type C.longlong int64
33+
type C.schar int8
34+
type C.short int16
35+
type C.uchar uint8
36+
type C.uint uint32
37+
type C.ulong uint32
38+
type C.ulonglong uint64
39+
type C.ushort uint16
40+
type C.point_t = struct {
41+
x C.int
42+
y C.int
43+
}

0 commit comments

Comments
 (0)