Commit 2dc025e4 authored by Keith Randall's avatar Keith Randall

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/87616Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent d162a297
......@@ -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.
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment