aboutsummaryrefslogtreecommitdiffhomepage
path: root/loader
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-04-10 15:51:44 +0200
committerRon Evans <[email protected]>2019-04-11 10:11:09 +0200
commit078dd9ff5236e40bfd530f8418d72916ce111e33 (patch)
tree4e4fae7f062de33dbd900865e1a9a909e4a15f39 /loader
parente5029c63d138d11c7e5304c815c4746d72b4ce54 (diff)
downloadtinygo-078dd9ff5236e40bfd530f8418d72916ce111e33.tar.gz
tinygo-078dd9ff5236e40bfd530f8418d72916ce111e33.zip
cgo: improve diagnostics
This makes CGo-emitted diagnostics very similar to regular errors emitted while parsing/typechecking a package. It's not complete, but after introducing some errors in testdata/cgo, this is the resulting output: # ./testdata/cgo/ testdata/cgo/main.h:18:11: error: a parameter list without types is only allowed in a function definition testdata/cgo/main.go:5:10: note: in file included from testdata/cgo/main.go!cgo.c:2: testdata/cgo/main.go:6:19: error: expected identifier or '(' Previously, this was the output: /home/ayke/src/github.com/tinygo-org/tinygo/testdata/cgo/main.h:18:11: error: a parameter list without types is only allowed in a function definition cgo-fake.c:3:19: error: expected identifier or '(' # ./testdata/cgo/ cgo: libclang cannot parse fragment
Diffstat (limited to 'loader')
-rw-r--r--loader/cgo.go11
-rw-r--r--loader/libclang.go66
-rw-r--r--loader/loader.go6
3 files changed, 70 insertions, 13 deletions
diff --git a/loader/cgo.go b/loader/cgo.go
index 626d3dbc4..abb5847c6 100644
--- a/loader/cgo.go
+++ b/loader/cgo.go
@@ -16,6 +16,7 @@ import (
// fileInfo holds all Cgo-related information of a given *ast.File.
type fileInfo struct {
*ast.File
+ *Package
filename string
functions map[string]*functionInfo
globals map[string]*globalInfo
@@ -78,9 +79,10 @@ typedef unsigned long long _Cgo_ulonglong;
// processCgo extracts the `import "C"` statement from the AST, parses the
// comment with libclang, and modifies the AST to use this information.
-func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error {
+func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
info := &fileInfo{
File: f,
+ Package: p,
filename: filename,
functions: map[string]*functionInfo{},
globals: map[string]*globalInfo{},
@@ -114,9 +116,10 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
// source location.
info.importCPos = spec.Path.ValuePos
- err = info.parseFragment(cgoComment+cgoTypes, cflags)
- if err != nil {
- return err
+ pos := info.fset.PositionFor(genDecl.Doc.Pos(), true)
+ errs := info.parseFragment(cgoComment+cgoTypes, cflags, pos.Filename, pos.Line)
+ if errs != nil {
+ return errs
}
// Remove this import declaration.
diff --git a/loader/libclang.go b/loader/libclang.go
index 3b0ce4f2a..22297f4d0 100644
--- a/loader/libclang.go
+++ b/loader/libclang.go
@@ -4,9 +4,10 @@ package loader
// modification. It does not touch the AST itself.
import (
- "errors"
"go/ast"
+ "go/scanner"
"go/token"
+ "path/filepath"
"strconv"
"strings"
"unsafe"
@@ -22,11 +23,19 @@ import "C"
var globalFileInfo *fileInfo
-func (info *fileInfo) parseFragment(fragment string, cflags []string) error {
- index := C.clang_createIndex(0, 1)
+var diagnosticSeverity = [...]string{
+ C.CXDiagnostic_Ignored: "ignored",
+ C.CXDiagnostic_Note: "note",
+ C.CXDiagnostic_Warning: "warning",
+ C.CXDiagnostic_Error: "error",
+ C.CXDiagnostic_Fatal: "fatal",
+}
+
+func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilename string, posLine int) []error {
+ index := C.clang_createIndex(0, 0)
defer C.clang_disposeIndex(index)
- filenameC := C.CString("cgo-fake.c")
+ filenameC := C.CString(posFilename+"!cgo.c")
defer C.free(unsafe.Pointer(filenameC))
fragmentC := C.CString(fragment)
@@ -61,8 +70,53 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string) error {
}
defer C.clang_disposeTranslationUnit(unit)
- if C.clang_getNumDiagnostics(unit) != 0 {
- return errors.New("cgo: libclang cannot parse fragment")
+ if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
+ errs := []error{}
+ addDiagnostic := func(diagnostic C.CXDiagnostic) {
+ spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
+ severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
+ location := C.clang_getDiagnosticLocation(diagnostic)
+ var file C.CXFile
+ var line C.unsigned
+ var column C.unsigned
+ var offset C.unsigned
+ C.clang_getExpansionLocation(location, &file, &line, &column, &offset)
+ filename := getString(C.clang_getFileName(file))
+ if filename == posFilename+"!cgo.c" {
+ // Adjust errors from the `import "C"` snippet.
+ // Note: doesn't adjust filenames inside the error message
+ // itself.
+ filename = posFilename
+ line += C.uint(posLine)
+ offset = 0 // hard to calculate
+ } else if filepath.IsAbs(filename) {
+ // Relative paths for readability, like other Go parser errors.
+ relpath, err := filepath.Rel(info.Program.Dir, filename)
+ if err == nil {
+ filename = relpath
+ }
+ }
+ errs = append(errs, &scanner.Error{
+ Pos: token.Position{
+ Filename: filename,
+ Offset: int(offset),
+ Line: int(line),
+ Column: int(column),
+ },
+ Msg: severity + ": " + spelling,
+ })
+ }
+ for i := 0; i < numDiagnostics; i++ {
+ diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
+ addDiagnostic(diagnostic)
+
+ // Child diagnostics (like notes on redefinitions).
+ diagnostics := C.clang_getChildDiagnostics(diagnostic)
+ for j := 0; j < int(C.clang_getNumDiagnosticsInSet(diagnostics)); j++ {
+ addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
+ }
+ }
+ return errs
}
if globalFileInfo != nil {
diff --git a/loader/loader.go b/loader/loader.go
index c804fd54c..2d15a0600 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -299,9 +299,9 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
fileErrs = append(fileErrs, err)
continue
}
- err = p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir))
- if err != nil {
- fileErrs = append(fileErrs, err)
+ errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir))
+ if errs != nil {
+ fileErrs = append(fileErrs, errs...)
continue
}
files = append(files, f)