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
Этот коммит содержится в:
родитель
e5029c63d1
коммит
078dd9ff52
3 изменённых файлов: 70 добавлений и 13 удалений
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче