Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / cgocall / cgocall.go
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package cgocall defines an Analyzer that detects some violations of
6 // the cgo pointer passing rules.
7 package cgocall
8
9 import (
10         "fmt"
11         "go/ast"
12         "go/format"
13         "go/parser"
14         "go/token"
15         "go/types"
16         "log"
17         "os"
18         "strconv"
19
20         "golang.org/x/tools/go/analysis"
21         "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
22 )
23
24 const debug = false
25
26 const Doc = `detect some violations of the cgo pointer passing rules
27
28 Check for invalid cgo pointer passing.
29 This looks for code that uses cgo to call C code passing values
30 whose types are almost always invalid according to the cgo pointer
31 sharing rules.
32 Specifically, it warns about attempts to pass a Go chan, map, func,
33 or slice to C, either directly, or via a pointer, array, or struct.`
34
35 var Analyzer = &analysis.Analyzer{
36         Name:             "cgocall",
37         Doc:              Doc,
38         RunDespiteErrors: true,
39         Run:              run,
40 }
41
42 func run(pass *analysis.Pass) (interface{}, error) {
43         if !analysisutil.Imports(pass.Pkg, "runtime/cgo") {
44                 return nil, nil // doesn't use cgo
45         }
46
47         cgofiles, info, err := typeCheckCgoSourceFiles(pass.Fset, pass.Pkg, pass.Files, pass.TypesInfo, pass.TypesSizes)
48         if err != nil {
49                 return nil, err
50         }
51         for _, f := range cgofiles {
52                 checkCgo(pass.Fset, f, info, pass.Reportf)
53         }
54         return nil, nil
55 }
56
57 func checkCgo(fset *token.FileSet, f *ast.File, info *types.Info, reportf func(token.Pos, string, ...interface{})) {
58         ast.Inspect(f, func(n ast.Node) bool {
59                 call, ok := n.(*ast.CallExpr)
60                 if !ok {
61                         return true
62                 }
63
64                 // Is this a C.f() call?
65                 var name string
66                 if sel, ok := analysisutil.Unparen(call.Fun).(*ast.SelectorExpr); ok {
67                         if id, ok := sel.X.(*ast.Ident); ok && id.Name == "C" {
68                                 name = sel.Sel.Name
69                         }
70                 }
71                 if name == "" {
72                         return true // not a call we need to check
73                 }
74
75                 // A call to C.CBytes passes a pointer but is always safe.
76                 if name == "CBytes" {
77                         return true
78                 }
79
80                 if debug {
81                         log.Printf("%s: call to C.%s", fset.Position(call.Lparen), name)
82                 }
83
84                 for _, arg := range call.Args {
85                         if !typeOKForCgoCall(cgoBaseType(info, arg), make(map[types.Type]bool)) {
86                                 reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
87                                 break
88                         }
89
90                         // Check for passing the address of a bad type.
91                         if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 &&
92                                 isUnsafePointer(info, conv.Fun) {
93                                 arg = conv.Args[0]
94                         }
95                         if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
96                                 if !typeOKForCgoCall(cgoBaseType(info, u.X), make(map[types.Type]bool)) {
97                                         reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
98                                         break
99                                 }
100                         }
101                 }
102                 return true
103         })
104 }
105
106 // typeCheckCgoSourceFiles returns type-checked syntax trees for the raw
107 // cgo files of a package (those that import "C"). Such files are not
108 // Go, so there may be gaps in type information around C.f references.
109 //
110 // This checker was initially written in vet to inspect raw cgo source
111 // files using partial type information. However, Analyzers in the new
112 // analysis API are presented with the type-checked, "cooked" Go ASTs
113 // resulting from cgo-processing files, so we must choose between
114 // working with the cooked file generated by cgo (which was tried but
115 // proved fragile) or locating the raw cgo file (e.g. from //line
116 // directives) and working with that, as we now do.
117 //
118 // Specifically, we must type-check the raw cgo source files (or at
119 // least the subtrees needed for this analyzer) in an environment that
120 // simulates the rest of the already type-checked package.
121 //
122 // For example, for each raw cgo source file in the original package,
123 // such as this one:
124 //
125 //      package p
126 //      import "C"
127 //      import "fmt"
128 //      type T int
129 //      const k = 3
130 //      var x, y = fmt.Println()
131 //      func f() { ... }
132 //      func g() { ... C.malloc(k) ... }
133 //      func (T) f(int) string { ... }
134 //
135 // we synthesize a new ast.File, shown below, that dot-imports the
136 // original "cooked" package using a special name ("·this·"), so that all
137 // references to package members resolve correctly. (References to
138 // unexported names cause an "unexported" error, which we ignore.)
139 //
140 // To avoid shadowing names imported from the cooked package,
141 // package-level declarations in the new source file are modified so
142 // that they do not declare any names.
143 // (The cgocall analysis is concerned with uses, not declarations.)
144 // Specifically, type declarations are discarded;
145 // all names in each var and const declaration are blanked out;
146 // each method is turned into a regular function by turning
147 // the receiver into the first parameter;
148 // and all functions are renamed to "_".
149 //
150 //      package p
151 //      import . "·this·" // declares T, k, x, y, f, g, T.f
152 //      import "C"
153 //      import "fmt"
154 //      const _ = 3
155 //      var _, _ = fmt.Println()
156 //      func _() { ... }
157 //      func _() { ... C.malloc(k) ... }
158 //      func _(T, int) string { ... }
159 //
160 // In this way, the raw function bodies and const/var initializer
161 // expressions are preserved but refer to the "cooked" objects imported
162 // from "·this·", and none of the transformed package-level declarations
163 // actually declares anything. In the example above, the reference to k
164 // in the argument of the call to C.malloc resolves to "·this·".k, which
165 // has an accurate type.
166 //
167 // This approach could in principle be generalized to more complex
168 // analyses on raw cgo files. One could synthesize a "C" package so that
169 // C.f would resolve to "·this·"._C_func_f, for example. But we have
170 // limited ourselves here to preserving function bodies and initializer
171 // expressions since that is all that the cgocall analyzer needs.
172 //
173 func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info, sizes types.Sizes) ([]*ast.File, *types.Info, error) {
174         const thispkg = "·this·"
175
176         // Which files are cgo files?
177         var cgoFiles []*ast.File
178         importMap := map[string]*types.Package{thispkg: pkg}
179         for _, raw := range files {
180                 // If f is a cgo-generated file, Position reports
181                 // the original file, honoring //line directives.
182                 filename := fset.Position(raw.Pos()).Filename
183                 f, err := parser.ParseFile(fset, filename, nil, parser.Mode(0))
184                 if err != nil {
185                         return nil, nil, fmt.Errorf("can't parse raw cgo file: %v", err)
186                 }
187                 found := false
188                 for _, spec := range f.Imports {
189                         if spec.Path.Value == `"C"` {
190                                 found = true
191                                 break
192                         }
193                 }
194                 if !found {
195                         continue // not a cgo file
196                 }
197
198                 // Record the original import map.
199                 for _, spec := range raw.Imports {
200                         path, _ := strconv.Unquote(spec.Path.Value)
201                         importMap[path] = imported(info, spec)
202                 }
203
204                 // Add special dot-import declaration:
205                 //    import . "·this·"
206                 var decls []ast.Decl
207                 decls = append(decls, &ast.GenDecl{
208                         Tok: token.IMPORT,
209                         Specs: []ast.Spec{
210                                 &ast.ImportSpec{
211                                         Name: &ast.Ident{Name: "."},
212                                         Path: &ast.BasicLit{
213                                                 Kind:  token.STRING,
214                                                 Value: strconv.Quote(thispkg),
215                                         },
216                                 },
217                         },
218                 })
219
220                 // Transform declarations from the raw cgo file.
221                 for _, decl := range f.Decls {
222                         switch decl := decl.(type) {
223                         case *ast.GenDecl:
224                                 switch decl.Tok {
225                                 case token.TYPE:
226                                         // Discard type declarations.
227                                         continue
228                                 case token.IMPORT:
229                                         // Keep imports.
230                                 case token.VAR, token.CONST:
231                                         // Blank the declared var/const names.
232                                         for _, spec := range decl.Specs {
233                                                 spec := spec.(*ast.ValueSpec)
234                                                 for i := range spec.Names {
235                                                         spec.Names[i].Name = "_"
236                                                 }
237                                         }
238                                 }
239                         case *ast.FuncDecl:
240                                 // Blank the declared func name.
241                                 decl.Name.Name = "_"
242
243                                 // Turn a method receiver:  func (T) f(P) R {...}
244                                 // into regular parameter:  func _(T, P) R {...}
245                                 if decl.Recv != nil {
246                                         var params []*ast.Field
247                                         params = append(params, decl.Recv.List...)
248                                         params = append(params, decl.Type.Params.List...)
249                                         decl.Type.Params.List = params
250                                         decl.Recv = nil
251                                 }
252                         }
253                         decls = append(decls, decl)
254                 }
255                 f.Decls = decls
256                 if debug {
257                         format.Node(os.Stderr, fset, f) // debugging
258                 }
259                 cgoFiles = append(cgoFiles, f)
260         }
261         if cgoFiles == nil {
262                 return nil, nil, nil // nothing to do (can't happen?)
263         }
264
265         // Type-check the synthetic files.
266         tc := &types.Config{
267                 FakeImportC: true,
268                 Importer: importerFunc(func(path string) (*types.Package, error) {
269                         return importMap[path], nil
270                 }),
271                 Sizes: sizes,
272                 Error: func(error) {}, // ignore errors (e.g. unused import)
273         }
274
275         // It's tempting to record the new types in the
276         // existing pass.TypesInfo, but we don't own it.
277         altInfo := &types.Info{
278                 Types: make(map[ast.Expr]types.TypeAndValue),
279         }
280         tc.Check(pkg.Path(), fset, cgoFiles, altInfo)
281
282         return cgoFiles, altInfo, nil
283 }
284
285 // cgoBaseType tries to look through type conversions involving
286 // unsafe.Pointer to find the real type. It converts:
287 //   unsafe.Pointer(x) => x
288 //   *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
289 func cgoBaseType(info *types.Info, arg ast.Expr) types.Type {
290         switch arg := arg.(type) {
291         case *ast.CallExpr:
292                 if len(arg.Args) == 1 && isUnsafePointer(info, arg.Fun) {
293                         return cgoBaseType(info, arg.Args[0])
294                 }
295         case *ast.StarExpr:
296                 call, ok := arg.X.(*ast.CallExpr)
297                 if !ok || len(call.Args) != 1 {
298                         break
299                 }
300                 // Here arg is *f(v).
301                 t := info.Types[call.Fun].Type
302                 if t == nil {
303                         break
304                 }
305                 ptr, ok := t.Underlying().(*types.Pointer)
306                 if !ok {
307                         break
308                 }
309                 // Here arg is *(*p)(v)
310                 elem, ok := ptr.Elem().Underlying().(*types.Basic)
311                 if !ok || elem.Kind() != types.UnsafePointer {
312                         break
313                 }
314                 // Here arg is *(*unsafe.Pointer)(v)
315                 call, ok = call.Args[0].(*ast.CallExpr)
316                 if !ok || len(call.Args) != 1 {
317                         break
318                 }
319                 // Here arg is *(*unsafe.Pointer)(f(v))
320                 if !isUnsafePointer(info, call.Fun) {
321                         break
322                 }
323                 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
324                 u, ok := call.Args[0].(*ast.UnaryExpr)
325                 if !ok || u.Op != token.AND {
326                         break
327                 }
328                 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
329                 return cgoBaseType(info, u.X)
330         }
331
332         return info.Types[arg].Type
333 }
334
335 // typeOKForCgoCall reports whether the type of arg is OK to pass to a
336 // C function using cgo. This is not true for Go types with embedded
337 // pointers. m is used to avoid infinite recursion on recursive types.
338 func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool {
339         if t == nil || m[t] {
340                 return true
341         }
342         m[t] = true
343         switch t := t.Underlying().(type) {
344         case *types.Chan, *types.Map, *types.Signature, *types.Slice:
345                 return false
346         case *types.Pointer:
347                 return typeOKForCgoCall(t.Elem(), m)
348         case *types.Array:
349                 return typeOKForCgoCall(t.Elem(), m)
350         case *types.Struct:
351                 for i := 0; i < t.NumFields(); i++ {
352                         if !typeOKForCgoCall(t.Field(i).Type(), m) {
353                                 return false
354                         }
355                 }
356         }
357         return true
358 }
359
360 func isUnsafePointer(info *types.Info, e ast.Expr) bool {
361         t := info.Types[e].Type
362         return t != nil && t.Underlying() == types.Typ[types.UnsafePointer]
363 }
364
365 type importerFunc func(path string) (*types.Package, error)
366
367 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
368
369 // TODO(adonovan): make this a library function or method of Info.
370 func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
371         obj, ok := info.Implicits[spec]
372         if !ok {
373                 obj = info.Defs[spec.Name] // renaming import
374         }
375         return obj.(*types.PkgName).Imported()
376 }