From 6a1bb134f9dfddf915421d15c0bb6f8a5fcbfc25 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 22 Nov 2019 11:15:23 +0100 Subject: [PATCH] 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. --- cgo/cgo_test.go | 40 +++++++++++++++++++++++++++-------- cgo/libclang.go | 1 - cgo/testdata/errors.go | 33 +++++++++++++++++++++++++++++ cgo/testdata/errors.out.go | 43 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 cgo/testdata/errors.go create mode 100644 cgo/testdata/errors.out.go diff --git a/cgo/cgo_test.go b/cgo/cgo_test.go index 410e7108..a2dbbd52 100644 --- a/cgo/cgo_test.go +++ b/cgo/cgo_test.go @@ -11,6 +11,7 @@ import ( "go/types" "io/ioutil" "path/filepath" + "runtime" "strings" "testing" ) @@ -21,7 +22,7 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output. func TestCGo(t *testing.T) { var cflags = []string{"--target=armv6m-none-eabi"} - for _, name := range []string{"basic", "types"} { + for _, name := range []string{"basic", "errors", "types"} { name := name // avoid a race condition t.Run(name, func(t *testing.T) { t.Parallel() @@ -35,23 +36,19 @@ func TestCGo(t *testing.T) { } // Process the AST with CGo. - cgoAST, errs := Process([]*ast.File{f}, "testdata", fset, cflags) - for _, err := range errs { - t.Errorf("error during CGo processing: %v", err) - } + cgoAST, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags) // Check the AST for type errors. - hasTypeError := false + var typecheckErrors []error config := types.Config{ Error: func(err error) { - t.Error("typecheck error:", err) - hasTypeError = true + typecheckErrors = append(typecheckErrors, err) }, Importer: simpleImporter{}, Sizes: types.SizesFor("gccgo", "arm"), } _, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil) - if err != nil && !hasTypeError { + if err != nil && len(typecheckErrors) == 0 { // Only report errors when no type errors are found (an // unexpected condition). t.Error(err) @@ -61,6 +58,20 @@ func TestCGo(t *testing.T) { // becomes easier to read (and will hopefully change less with CGo // changes). buf := &bytes.Buffer{} + if len(cgoErrors) != 0 { + buf.WriteString("// CGo errors:\n") + for _, err := range cgoErrors { + buf.WriteString(formatDiagnostic(err)) + } + buf.WriteString("\n") + } + if len(typecheckErrors) != 0 { + buf.WriteString("// Type checking errors after CGo processing:\n") + for _, err := range typecheckErrors { + buf.WriteString(formatDiagnostic(err)) + } + buf.WriteString("\n") + } err = format.Node(buf, fset, cgoAST) if err != nil { t.Errorf("could not write out CGo AST: %v", err) @@ -107,3 +118,14 @@ func (i simpleImporter) Import(path string) (*types.Package, error) { return nil, fmt.Errorf("importer not implemented for package %s", path) } } + +// formatDiagnostics formats the error message to be an indented comment. It +// also fixes Windows path name issues (backward slashes). +func formatDiagnostic(err error) string { + msg := err.Error() + if runtime.GOOS == "windows" { + // Fix Windows path slashes. + msg = strings.Replace(msg, "testdata\\", "testdata/", -1) + } + return "// " + msg + "\n" +} diff --git a/cgo/libclang.go b/cgo/libclang.go index 336122a7..2e918a49 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -132,7 +132,6 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j))) } } - return } ref := storedRefs.Put(p) diff --git a/cgo/testdata/errors.go b/cgo/testdata/errors.go new file mode 100644 index 00000000..cdb28cf3 --- /dev/null +++ b/cgo/testdata/errors.go @@ -0,0 +1,33 @@ +package main + +/* +#warning some warning + +typedef struct { + int x; + int y; +} point_t; + +typedef someType noType; // undefined type + +#define SOME_CONST_1 5) // invalid const syntax +#define SOME_CONST_2 6) // const not used (so no error) +#define SOME_CONST_3 1234 // const too large for byte +*/ +import "C" + +// Make sure that errors for the following lines won't change with future +// additions to the CGo preamble. +//line errors.go:100 +var ( + // constant too large + _ C.uint8_t = 2 << 10 + + // z member does not exist + _ C.point_t = C.point_t{z: 3} + + // constant has syntax error + _ = C.SOME_CONST_1 + + _ byte = C.SOME_CONST_3 +) diff --git a/cgo/testdata/errors.out.go b/cgo/testdata/errors.out.go new file mode 100644 index 00000000..ace2390d --- /dev/null +++ b/cgo/testdata/errors.out.go @@ -0,0 +1,43 @@ +// CGo errors: +// testdata/errors.go:4:2: warning: some warning +// testdata/errors.go:11:9: error: unknown type name 'someType' +// testdata/errors.go:13:23: unexpected token ) + +// Type checking errors after CGo processing: +// testdata/errors.go:102: 2 << 10 (untyped int constant 2048) overflows uint8 +// testdata/errors.go:105: unknown field z in struct literal +// testdata/errors.go:108: undeclared name: C.SOME_CONST_1 +// testdata/errors.go:110: C.SOME_CONST_3 (untyped int constant 1234) overflows byte + +package main + +import "unsafe" + +var _ unsafe.Pointer + +const C.SOME_CONST_3 = 1234 + +type C.int16_t = int16 +type C.int32_t = int32 +type C.int64_t = int64 +type C.int8_t = int8 +type C.uint16_t = uint16 +type C.uint32_t = uint32 +type C.uint64_t = uint64 +type C.uint8_t = uint8 +type C.uintptr_t = uintptr +type C.char uint8 +type C.int int32 +type C.long int32 +type C.longlong int64 +type C.schar int8 +type C.short int16 +type C.uchar uint8 +type C.uint uint32 +type C.ulong uint32 +type C.ulonglong uint64 +type C.ushort uint16 +type C.point_t = struct { + x C.int + y C.int +}