PR tree-optimization/86274 - SEGFAULT when logging std::to_string(NAN)
[official-gcc.git] / libgo / go / cmd / vet / copylock.go
blobce14e1af3439b0a0107371ba0792c5ce40d42287
1 // Copyright 2013 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.
5 // This file contains the code to check that locks are not passed by value.
7 package main
9 import (
10 "bytes"
11 "fmt"
12 "go/ast"
13 "go/token"
14 "go/types"
17 func init() {
18 register("copylocks",
19 "check that locks are not passed by value",
20 checkCopyLocks,
21 funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt)
24 // checkCopyLocks checks whether node might
25 // inadvertently copy a lock.
26 func checkCopyLocks(f *File, node ast.Node) {
27 switch node := node.(type) {
28 case *ast.RangeStmt:
29 checkCopyLocksRange(f, node)
30 case *ast.FuncDecl:
31 checkCopyLocksFunc(f, node.Name.Name, node.Recv, node.Type)
32 case *ast.FuncLit:
33 checkCopyLocksFunc(f, "func", nil, node.Type)
34 case *ast.CallExpr:
35 checkCopyLocksCallExpr(f, node)
36 case *ast.AssignStmt:
37 checkCopyLocksAssign(f, node)
38 case *ast.GenDecl:
39 checkCopyLocksGenDecl(f, node)
40 case *ast.CompositeLit:
41 checkCopyLocksCompositeLit(f, node)
42 case *ast.ReturnStmt:
43 checkCopyLocksReturnStmt(f, node)
47 // checkCopyLocksAssign checks whether an assignment
48 // copies a lock.
49 func checkCopyLocksAssign(f *File, as *ast.AssignStmt) {
50 for i, x := range as.Rhs {
51 if path := lockPathRhs(f, x); path != nil {
52 f.Badf(x.Pos(), "assignment copies lock value to %v: %v", f.gofmt(as.Lhs[i]), path)
57 // checkCopyLocksGenDecl checks whether lock is copied
58 // in variable declaration.
59 func checkCopyLocksGenDecl(f *File, gd *ast.GenDecl) {
60 if gd.Tok != token.VAR {
61 return
63 for _, spec := range gd.Specs {
64 valueSpec := spec.(*ast.ValueSpec)
65 for i, x := range valueSpec.Values {
66 if path := lockPathRhs(f, x); path != nil {
67 f.Badf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
73 // checkCopyLocksCompositeLit detects lock copy inside a composite literal
74 func checkCopyLocksCompositeLit(f *File, cl *ast.CompositeLit) {
75 for _, x := range cl.Elts {
76 if node, ok := x.(*ast.KeyValueExpr); ok {
77 x = node.Value
79 if path := lockPathRhs(f, x); path != nil {
80 f.Badf(x.Pos(), "literal copies lock value from %v: %v", f.gofmt(x), path)
85 // checkCopyLocksReturnStmt detects lock copy in return statement
86 func checkCopyLocksReturnStmt(f *File, rs *ast.ReturnStmt) {
87 for _, x := range rs.Results {
88 if path := lockPathRhs(f, x); path != nil {
89 f.Badf(x.Pos(), "return copies lock value: %v", path)
94 // checkCopyLocksCallExpr detects lock copy in the arguments to a function call
95 func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) {
96 var id *ast.Ident
97 switch fun := ce.Fun.(type) {
98 case *ast.Ident:
99 id = fun
100 case *ast.SelectorExpr:
101 id = fun.Sel
103 if fun, ok := f.pkg.uses[id].(*types.Builtin); ok {
104 switch fun.Name() {
105 case "new", "len", "cap", "Sizeof":
106 return
109 for _, x := range ce.Args {
110 if path := lockPathRhs(f, x); path != nil {
111 f.Badf(x.Pos(), "call of %s copies lock value: %v", f.gofmt(ce.Fun), path)
116 // checkCopyLocksFunc checks whether a function might
117 // inadvertently copy a lock, by checking whether
118 // its receiver, parameters, or return values
119 // are locks.
120 func checkCopyLocksFunc(f *File, name string, recv *ast.FieldList, typ *ast.FuncType) {
121 if recv != nil && len(recv.List) > 0 {
122 expr := recv.List[0].Type
123 if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
124 f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
128 if typ.Params != nil {
129 for _, field := range typ.Params.List {
130 expr := field.Type
131 if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
132 f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
137 // Don't check typ.Results. If T has a Lock field it's OK to write
138 // return T{}
139 // because that is returning the zero value. Leave result checking
140 // to the return statement.
143 // checkCopyLocksRange checks whether a range statement
144 // might inadvertently copy a lock by checking whether
145 // any of the range variables are locks.
146 func checkCopyLocksRange(f *File, r *ast.RangeStmt) {
147 checkCopyLocksRangeVar(f, r.Tok, r.Key)
148 checkCopyLocksRangeVar(f, r.Tok, r.Value)
151 func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) {
152 if e == nil {
153 return
155 id, isId := e.(*ast.Ident)
156 if isId && id.Name == "_" {
157 return
160 var typ types.Type
161 if rtok == token.DEFINE {
162 if !isId {
163 return
165 obj := f.pkg.defs[id]
166 if obj == nil {
167 return
169 typ = obj.Type()
170 } else {
171 typ = f.pkg.types[e].Type
174 if typ == nil {
175 return
177 if path := lockPath(f.pkg.typesPkg, typ); path != nil {
178 f.Badf(e.Pos(), "range var %s copies lock: %v", f.gofmt(e), path)
182 type typePath []types.Type
184 // String pretty-prints a typePath.
185 func (path typePath) String() string {
186 n := len(path)
187 var buf bytes.Buffer
188 for i := range path {
189 if i > 0 {
190 fmt.Fprint(&buf, " contains ")
192 // The human-readable path is in reverse order, outermost to innermost.
193 fmt.Fprint(&buf, path[n-i-1].String())
195 return buf.String()
198 func lockPathRhs(f *File, x ast.Expr) typePath {
199 if _, ok := x.(*ast.CompositeLit); ok {
200 return nil
202 if _, ok := x.(*ast.CallExpr); ok {
203 // A call may return a zero value.
204 return nil
206 if star, ok := x.(*ast.StarExpr); ok {
207 if _, ok := star.X.(*ast.CallExpr); ok {
208 // A call may return a pointer to a zero value.
209 return nil
212 return lockPath(f.pkg.typesPkg, f.pkg.types[x].Type)
215 // lockPath returns a typePath describing the location of a lock value
216 // contained in typ. If there is no contained lock, it returns nil.
217 func lockPath(tpkg *types.Package, typ types.Type) typePath {
218 if typ == nil {
219 return nil
222 for {
223 atyp, ok := typ.Underlying().(*types.Array)
224 if !ok {
225 break
227 typ = atyp.Elem()
230 // We're only interested in the case in which the underlying
231 // type is a struct. (Interfaces and pointers are safe to copy.)
232 styp, ok := typ.Underlying().(*types.Struct)
233 if !ok {
234 return nil
237 // We're looking for cases in which a reference to this type
238 // can be locked, but a value cannot. This differentiates
239 // embedded interfaces from embedded values.
240 if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil {
241 if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil {
242 return []types.Type{typ}
246 nfields := styp.NumFields()
247 for i := 0; i < nfields; i++ {
248 ftyp := styp.Field(i).Type()
249 subpath := lockPath(tpkg, ftyp)
250 if subpath != nil {
251 return append(subpath, typ)
255 return nil