const debug … const Doc … var Analyzer … func run(pass *analysis.Pass) (interface{ … } func checkCgo(fset *token.FileSet, f *ast.File, info *types.Info, reportf func(token.Pos, string, ...interface{ … } // typeCheckCgoSourceFiles returns type-checked syntax trees for the raw // cgo files of a package (those that import "C"). Such files are not // Go, so there may be gaps in type information around C.f references. // // This checker was initially written in vet to inspect raw cgo source // files using partial type information. However, Analyzers in the new // analysis API are presented with the type-checked, "cooked" Go ASTs // resulting from cgo-processing files, so we must choose between // working with the cooked file generated by cgo (which was tried but // proved fragile) or locating the raw cgo file (e.g. from //line // directives) and working with that, as we now do. // // Specifically, we must type-check the raw cgo source files (or at // least the subtrees needed for this analyzer) in an environment that // simulates the rest of the already type-checked package. // // For example, for each raw cgo source file in the original package, // such as this one: // // package p // import "C" // import "fmt" // type T int // const k = 3 // var x, y = fmt.Println() // func f() { ... } // func g() { ... C.malloc(k) ... } // func (T) f(int) string { ... } // // we synthesize a new ast.File, shown below, that dot-imports the // original "cooked" package using a special name ("·this·"), so that all // references to package members resolve correctly. (References to // unexported names cause an "unexported" error, which we ignore.) // // To avoid shadowing names imported from the cooked package, // package-level declarations in the new source file are modified so // that they do not declare any names. // (The cgocall analysis is concerned with uses, not declarations.) // Specifically, type declarations are discarded; // all names in each var and const declaration are blanked out; // each method is turned into a regular function by turning // the receiver into the first parameter; // and all functions are renamed to "_". // // package p // import . "·this·" // declares T, k, x, y, f, g, T.f // import "C" // import "fmt" // const _ = 3 // var _, _ = fmt.Println() // func _() { ... } // func _() { ... C.malloc(k) ... } // func _(T, int) string { ... } // // In this way, the raw function bodies and const/var initializer // expressions are preserved but refer to the "cooked" objects imported // from "·this·", and none of the transformed package-level declarations // actually declares anything. In the example above, the reference to k // in the argument of the call to C.malloc resolves to "·this·".k, which // has an accurate type. // // This approach could in principle be generalized to more complex // analyses on raw cgo files. One could synthesize a "C" package so that // C.f would resolve to "·this·"._C_func_f, for example. But we have // limited ourselves here to preserving function bodies and initializer // expressions since that is all that the cgocall analyzer needs. func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info, sizes types.Sizes) ([]*ast.File, *types.Info, error) { … } // cgoBaseType tries to look through type conversions involving // unsafe.Pointer to find the real type. It converts: // // unsafe.Pointer(x) => x // *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x func cgoBaseType(info *types.Info, arg ast.Expr) types.Type { … } // typeOKForCgoCall reports whether the type of arg is OK to pass to a // C function using cgo. This is not true for Go types with embedded // pointers. m is used to avoid infinite recursion on recursive types. func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool { … } func isUnsafePointer(info *types.Info, e ast.Expr) bool { … } type importerFunc … func (f importerFunc) Import(path string) (*types.Package, error) { … } // TODO(adonovan): make this a library function or method of Info. func imported(info *types.Info, spec *ast.ImportSpec) *types.Package { … }