@@ -7,9 +7,14 @@ package main
77import (
88 "fmt"
99 "go/ast"
10+ "go/parser"
1011 "go/token"
12+ "io/ioutil"
1113 "os"
14+ "os/exec"
15+ "path/filepath"
1216 "reflect"
17+ "runtime"
1318 "strings"
1419)
1520
@@ -74,6 +79,11 @@ type TypeConfig struct {
7479 Type map [string ]* Type
7580 Var map [string ]string
7681 Func map [string ]string
82+
83+ // External maps from a name to its type.
84+ // It provides additional typings not present in the Go source itself.
85+ // For now, the only additional typings are those generated by cgo.
86+ External map [string ]string
7787}
7888
7989// 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
140150 * cfg1 = * cfg // make copy so we can add locally
141151 copied := false
142152
153+ // If we import "C", add types of cgo objects.
154+ cfg .External = map [string ]string {}
155+ if imports (f , "C" ) {
156+ // Run cgo on gofmtFile(f)
157+ // Parse, extract decls from _cgo_gotypes.go
158+ // Map _Ctype_* types to C.* types.
159+ err := func () error {
160+ txt , err := gofmtFile (f )
161+ if err != nil {
162+ return err
163+ }
164+ dir , err := ioutil .TempDir (os .TempDir (), "fix_cgo_typecheck" )
165+ if err != nil {
166+ return err
167+ }
168+ defer os .Remove (dir )
169+ err = ioutil .WriteFile (filepath .Join (dir , "in.go" ), txt , 0600 )
170+ if err != nil {
171+ return err
172+ }
173+ cmd := exec .Command (filepath .Join (runtime .GOROOT (), "bin" , "go" ), "tool" , "cgo" , "-objdir" , dir , "-srcdir" , dir , "in.go" )
174+ err = cmd .Run ()
175+ if err != nil {
176+ return err
177+ }
178+ out , err := ioutil .ReadFile (filepath .Join (dir , "_cgo_gotypes.go" ))
179+ if err != nil {
180+ return err
181+ }
182+ cgo , err := parser .ParseFile (token .NewFileSet (), "cgo.go" , out , 0 )
183+ if err != nil {
184+ return err
185+ }
186+ for _ , decl := range cgo .Decls {
187+ fn , ok := decl .(* ast.FuncDecl )
188+ if ! ok {
189+ continue
190+ }
191+ if strings .HasPrefix (fn .Name .Name , "_Cfunc_" ) {
192+ var params , results []string
193+ for _ , p := range fn .Type .Params .List {
194+ t := gofmt (p .Type )
195+ t = strings .Replace (t , "_Ctype_" , "C." , - 1 )
196+ params = append (params , t )
197+ }
198+ for _ , r := range fn .Type .Results .List {
199+ t := gofmt (r .Type )
200+ t = strings .Replace (t , "_Ctype_" , "C." , - 1 )
201+ results = append (results , t )
202+ }
203+ cfg .External ["C." + fn .Name .Name [7 :]] = joinFunc (params , results )
204+ }
205+ }
206+ return nil
207+ }()
208+ if err != nil {
209+ fmt .Printf ("warning: no cgo types: %s\n " , err )
210+ }
211+ }
212+
143213 // gather function declarations
144214 for _ , decl := range f .Decls {
145215 fn , ok := decl .(* ast.FuncDecl )
@@ -434,6 +504,9 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
434504 }
435505 // Otherwise, use type of function to determine arguments.
436506 t := typeof [n .Fun ]
507+ if t == "" {
508+ t = cfg .External [gofmt (n .Fun )]
509+ }
437510 in , out := splitFunc (t )
438511 if in == nil && out == nil {
439512 return
0 commit comments