Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
1
2
3
4
5
6
7 package copylock
8
9 import (
10 "bytes"
11 "fmt"
12 "go/ast"
13 "go/token"
14 "go/types"
15
16 "golang.org/x/tools/go/analysis"
17 "golang.org/x/tools/go/analysis/passes/inspect"
18 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
19 "golang.org/x/tools/go/ast/inspector"
20 )
21
22 const Doc = `check for locks erroneously passed by value
23
24 Inadvertently copying a value containing a lock, such as sync.Mutex or
25 sync.WaitGroup, may cause both copies to malfunction. Generally such
26 values should be referred to through a pointer.`
27
28 var Analyzer = &analysis.Analyzer{
29 Name: "copylocks",
30 Doc: Doc,
31 Requires: []*analysis.Analyzer{inspect.Analyzer},
32 RunDespiteErrors: true,
33 Run: run,
34 }
35
36 func run(pass *analysis.Pass) (interface{}, error) {
37 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
38
39 nodeFilter := []ast.Node{
40 (*ast.AssignStmt)(nil),
41 (*ast.CallExpr)(nil),
42 (*ast.CompositeLit)(nil),
43 (*ast.FuncDecl)(nil),
44 (*ast.FuncLit)(nil),
45 (*ast.GenDecl)(nil),
46 (*ast.RangeStmt)(nil),
47 (*ast.ReturnStmt)(nil),
48 }
49 inspect.Preorder(nodeFilter, func(node ast.Node) {
50 switch node := node.(type) {
51 case *ast.RangeStmt:
52 checkCopyLocksRange(pass, node)
53 case *ast.FuncDecl:
54 checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type)
55 case *ast.FuncLit:
56 checkCopyLocksFunc(pass, "func", nil, node.Type)
57 case *ast.CallExpr:
58 checkCopyLocksCallExpr(pass, node)
59 case *ast.AssignStmt:
60 checkCopyLocksAssign(pass, node)
61 case *ast.GenDecl:
62 checkCopyLocksGenDecl(pass, node)
63 case *ast.CompositeLit:
64 checkCopyLocksCompositeLit(pass, node)
65 case *ast.ReturnStmt:
66 checkCopyLocksReturnStmt(pass, node)
67 }
68 })
69 return nil, nil
70 }
71
72
73
74 func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
75 for i, x := range as.Rhs {
76 if path := lockPathRhs(pass, x); path != nil {
77 pass.Reportf(x.Pos(), "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path)
78 }
79 }
80 }
81
82
83
84 func checkCopyLocksGenDecl(pass *analysis.Pass, gd *ast.GenDecl) {
85 if gd.Tok != token.VAR {
86 return
87 }
88 for _, spec := range gd.Specs {
89 valueSpec := spec.(*ast.ValueSpec)
90 for i, x := range valueSpec.Values {
91 if path := lockPathRhs(pass, x); path != nil {
92 pass.Reportf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
93 }
94 }
95 }
96 }
97
98
99 func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) {
100 for _, x := range cl.Elts {
101 if node, ok := x.(*ast.KeyValueExpr); ok {
102 x = node.Value
103 }
104 if path := lockPathRhs(pass, x); path != nil {
105 pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path)
106 }
107 }
108 }
109
110
111 func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) {
112 for _, x := range rs.Results {
113 if path := lockPathRhs(pass, x); path != nil {
114 pass.Reportf(x.Pos(), "return copies lock value: %v", path)
115 }
116 }
117 }
118
119
120 func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
121 var id *ast.Ident
122 switch fun := ce.Fun.(type) {
123 case *ast.Ident:
124 id = fun
125 case *ast.SelectorExpr:
126 id = fun.Sel
127 }
128 if fun, ok := pass.TypesInfo.Uses[id].(*types.Builtin); ok {
129 switch fun.Name() {
130 case "new", "len", "cap", "Sizeof":
131 return
132 }
133 }
134 for _, x := range ce.Args {
135 if path := lockPathRhs(pass, x); path != nil {
136 pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path)
137 }
138 }
139 }
140
141
142
143
144
145 func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
146 if recv != nil && len(recv.List) > 0 {
147 expr := recv.List[0].Type
148 if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
149 pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
150 }
151 }
152
153 if typ.Params != nil {
154 for _, field := range typ.Params.List {
155 expr := field.Type
156 if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
157 pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
158 }
159 }
160 }
161
162
163
164
165
166 }
167
168
169
170
171 func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) {
172 checkCopyLocksRangeVar(pass, r.Tok, r.Key)
173 checkCopyLocksRangeVar(pass, r.Tok, r.Value)
174 }
175
176 func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
177 if e == nil {
178 return
179 }
180 id, isId := e.(*ast.Ident)
181 if isId && id.Name == "_" {
182 return
183 }
184
185 var typ types.Type
186 if rtok == token.DEFINE {
187 if !isId {
188 return
189 }
190 obj := pass.TypesInfo.Defs[id]
191 if obj == nil {
192 return
193 }
194 typ = obj.Type()
195 } else {
196 typ = pass.TypesInfo.Types[e].Type
197 }
198
199 if typ == nil {
200 return
201 }
202 if path := lockPath(pass.Pkg, typ); path != nil {
203 pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
204 }
205 }
206
207 type typePath []types.Type
208
209
210 func (path typePath) String() string {
211 n := len(path)
212 var buf bytes.Buffer
213 for i := range path {
214 if i > 0 {
215 fmt.Fprint(&buf, " contains ")
216 }
217
218 fmt.Fprint(&buf, path[n-i-1].String())
219 }
220 return buf.String()
221 }
222
223 func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
224 if _, ok := x.(*ast.CompositeLit); ok {
225 return nil
226 }
227 if _, ok := x.(*ast.CallExpr); ok {
228
229 return nil
230 }
231 if star, ok := x.(*ast.StarExpr); ok {
232 if _, ok := star.X.(*ast.CallExpr); ok {
233
234 return nil
235 }
236 }
237 return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
238 }
239
240
241
242 func lockPath(tpkg *types.Package, typ types.Type) typePath {
243 if typ == nil {
244 return nil
245 }
246
247 for {
248 atyp, ok := typ.Underlying().(*types.Array)
249 if !ok {
250 break
251 }
252 typ = atyp.Elem()
253 }
254
255
256
257 styp, ok := typ.Underlying().(*types.Struct)
258 if !ok {
259 return nil
260 }
261
262
263
264
265 if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
266 return []types.Type{typ}
267 }
268
269
270
271
272 if named, ok := typ.(*types.Named); ok &&
273 named.Obj().Name() == "noCopy" &&
274 named.Obj().Pkg().Path() == "sync" {
275 return []types.Type{typ}
276 }
277
278 nfields := styp.NumFields()
279 for i := 0; i < nfields; i++ {
280 ftyp := styp.Field(i).Type()
281 subpath := lockPath(tpkg, ftyp)
282 if subpath != nil {
283 return append(subpath, typ)
284 }
285 }
286
287 return nil
288 }
289
290 var lockerType *types.Interface
291
292
293 func init() {
294 nullary := types.NewSignature(nil, nil, nil, false)
295 methods := []*types.Func{
296 types.NewFunc(token.NoPos, nil, "Lock", nullary),
297 types.NewFunc(token.NoPos, nil, "Unlock", nullary),
298 }
299 lockerType = types.NewInterface(methods, nil).Complete()
300 }
301
View as plain text