aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2018-01-12 10:11:01 -0800
committerKeith Randall <khr@golang.org>2018-01-17 06:44:25 +0000
commit2dc025e4e171ee99d31d11efe5ccf53794e89020 (patch)
tree61e38e15ffad4dda98aba09c7034077246beb655
parentd162a297ed216fb02ebe409ace2387c3a656de66 (diff)
downloadgo-2dc025e4e171ee99d31d11efe5ccf53794e89020.tar.gz
go-2dc025e4e171ee99d31d11efe5ccf53794e89020.zip
cmd/fix: extend typechecker to use cgo types
If a file uses cgo, incorporate the types generated by running cgo. Update #23091 Change-Id: I10958fa7fd6027c2c96a9fd8a9658de35439719f Reviewed-on: https://go-review.googlesource.com/87616 Reviewed-by: Robert Griesemer <gri@golang.org>
-rw-r--r--src/cmd/fix/cftype.go10
-rw-r--r--src/cmd/fix/jnitype.go3
-rw-r--r--src/cmd/fix/typecheck.go73
3 files changed, 81 insertions, 5 deletions
diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go
index 841bd4dccb..5e742a4fdf 100644
--- a/src/cmd/fix/cftype.go
+++ b/src/cmd/fix/cftype.go
@@ -30,17 +30,19 @@ var cftypeFix = fix{
// and similar for other *Ref types.
// This fix finds nils initializing these types and replaces the nils with 0s.
func cftypefix(f *ast.File) bool {
- return typefix(f, func(s string) bool {
- return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref")
+ var tc TypeConfig
+ return typefix(f, &tc, func(s string) bool {
+ return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref") &&
+ (s == "C.CFTypeRef" || tc.External[s[:len(s)-3]+"GetTypeID"] == "func() C.CFTypeID")
})
}
// typefix replaces nil with 0 for all nils whose type, when passed to badType, returns true.
-func typefix(f *ast.File, badType func(string) bool) bool {
+func typefix(f *ast.File, tc *TypeConfig, badType func(string) bool) bool {
if !imports(f, "C") {
return false
}
- typeof, _ := typecheck(&TypeConfig{}, f)
+ typeof, _ := typecheck(tc, f)
// step 1: Find all the nils with the offending types.
// Compute their replacement.
diff --git a/src/cmd/fix/jnitype.go b/src/cmd/fix/jnitype.go
index 29abe0f007..75ae570c4d 100644
--- a/src/cmd/fix/jnitype.go
+++ b/src/cmd/fix/jnitype.go
@@ -27,7 +27,8 @@ var jniFix = fix{
// and similar for subtypes of jobject.
// This fix finds nils initializing these types and replaces the nils with 0s.
func jnifix(f *ast.File) bool {
- return typefix(f, func(s string) bool {
+ var tc TypeConfig
+ return typefix(f, &tc, func(s string) bool {
switch s {
case "C.jobject":
return true
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
index 58d915869d..a52a54202d 100644
--- a/src/cmd/fix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -7,9 +7,14 @@ package main
import (
"fmt"
"go/ast"
+ "go/parser"
"go/token"
+ "io/ioutil"
"os"
+ "os/exec"
+ "path/filepath"
"reflect"
+ "runtime"
"strings"
)
@@ -74,6 +79,11 @@ type TypeConfig struct {
Type map[string]*Type
Var map[string]string
Func map[string]string
+
+ // External maps from a name to its type.
+ // It provides additional typings not present in the Go source itself.
+ // For now, the only additional typings are those generated by cgo.
+ External map[string]string
}
// typeof returns the type of the given name, which may be of
@@ -140,6 +150,66 @@ func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, ass
*cfg1 = *cfg // make copy so we can add locally
copied := false
+ // If we import "C", add types of cgo objects.
+ cfg.External = map[string]string{}
+ if imports(f, "C") {
+ // Run cgo on gofmtFile(f)
+ // Parse, extract decls from _cgo_gotypes.go
+ // Map _Ctype_* types to C.* types.
+ err := func() error {
+ txt, err := gofmtFile(f)
+ if err != nil {
+ return err
+ }
+ dir, err := ioutil.TempDir(os.TempDir(), "fix_cgo_typecheck")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(dir)
+ err = ioutil.WriteFile(filepath.Join(dir, "in.go"), txt, 0600)
+ if err != nil {
+ return err
+ }
+ cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go")
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ out, err := ioutil.ReadFile(filepath.Join(dir, "_cgo_gotypes.go"))
+ if err != nil {
+ return err
+ }
+ cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0)
+ if err != nil {
+ return err
+ }
+ for _, decl := range cgo.Decls {
+ fn, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ if strings.HasPrefix(fn.Name.Name, "_Cfunc_") {
+ var params, results []string
+ for _, p := range fn.Type.Params.List {
+ t := gofmt(p.Type)
+ t = strings.Replace(t, "_Ctype_", "C.", -1)
+ params = append(params, t)
+ }
+ for _, r := range fn.Type.Results.List {
+ t := gofmt(r.Type)
+ t = strings.Replace(t, "_Ctype_", "C.", -1)
+ results = append(results, t)
+ }
+ cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results)
+ }
+ }
+ return nil
+ }()
+ if err != nil {
+ fmt.Printf("warning: no cgo types: %s\n", err)
+ }
+ }
+
// gather function declarations
for _, decl := range f.Decls {
fn, ok := decl.(*ast.FuncDecl)
@@ -434,6 +504,9 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
}
// Otherwise, use type of function to determine arguments.
t := typeof[n.Fun]
+ if t == "" {
+ t = cfg.External[gofmt(n.Fun)]
+ }
in, out := splitFunc(t)
if in == nil && out == nil {
return