Skip to content

Commit 1374515

Browse files
valyalarobpike
authored andcommitted
cmd/vet: check lock copy in function calls and return statements
Fixes #14529 Change-Id: I6ed059d279ba0fe12d76416859659f28d61781d2 Reviewed-on: https://go-review.googlesource.com/20832 Run-TryBot: Rob Pike <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 49da931 commit 1374515

File tree

3 files changed

+58
-4
lines changed

3 files changed

+58
-4
lines changed

src/cmd/vet/copylock.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func init() {
1818
register("copylocks",
1919
"check that locks are not passed by value",
2020
checkCopyLocks,
21-
funcDecl, rangeStmt, funcLit, assignStmt, genDecl, compositeLit)
21+
funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt)
2222
}
2323

2424
// checkCopyLocks checks whether node might
@@ -31,12 +31,16 @@ func checkCopyLocks(f *File, node ast.Node) {
3131
checkCopyLocksFunc(f, node.Name.Name, node.Recv, node.Type)
3232
case *ast.FuncLit:
3333
checkCopyLocksFunc(f, "func", nil, node.Type)
34+
case *ast.CallExpr:
35+
checkCopyLocksCallExpr(f, node)
3436
case *ast.AssignStmt:
3537
checkCopyLocksAssign(f, node)
3638
case *ast.GenDecl:
3739
checkCopyLocksGenDecl(f, node)
3840
case *ast.CompositeLit:
39-
checkCopyCompositeLit(f, node)
41+
checkCopyLocksCompositeLit(f, node)
42+
case *ast.ReturnStmt:
43+
checkCopyLocksReturnStmt(f, node)
4044
}
4145
}
4246

@@ -66,8 +70,8 @@ func checkCopyLocksGenDecl(f *File, gd *ast.GenDecl) {
6670
}
6771
}
6872

69-
// checkCopyCompositeLit detects lock copy inside a composite literal
70-
func checkCopyCompositeLit(f *File, cl *ast.CompositeLit) {
73+
// checkCopyLocksCompositeLit detects lock copy inside a composite literal
74+
func checkCopyLocksCompositeLit(f *File, cl *ast.CompositeLit) {
7175
for _, x := range cl.Elts {
7276
if node, ok := x.(*ast.KeyValueExpr); ok {
7377
x = node.Value
@@ -78,6 +82,24 @@ func checkCopyCompositeLit(f *File, cl *ast.CompositeLit) {
7882
}
7983
}
8084

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)
90+
}
91+
}
92+
}
93+
94+
// checkCopyLocksCallExpr detects lock copy in function call
95+
func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) {
96+
for _, x := range ce.Args {
97+
if path := lockPathRhs(f, x); path != nil {
98+
f.Badf(x.Pos(), "function call copies lock value: %v", path)
99+
}
100+
}
101+
}
102+
81103
// checkCopyLocksFunc checks whether a function might
82104
// inadvertently copy a lock, by checking whether
83105
// its receiver, parameters, or return values

src/cmd/vet/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ var (
139139
genDecl *ast.GenDecl
140140
interfaceType *ast.InterfaceType
141141
rangeStmt *ast.RangeStmt
142+
returnStmt *ast.ReturnStmt
142143

143144
// checkers is a two-level map.
144145
// The outer level is keyed by a nil pointer, one of the AST vars above.
@@ -476,6 +477,8 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
476477
key = interfaceType
477478
case *ast.RangeStmt:
478479
key = rangeStmt
480+
case *ast.ReturnStmt:
481+
key = returnStmt
479482
}
480483
for _, fn := range f.checkers[key] {
481484
fn(f, node)

src/cmd/vet/testdata/copylock_func.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,35 @@ func (*CustomLock) Unlock() {}
7878
func Ok(*CustomLock) {}
7979
func Bad(CustomLock) {} // ERROR "Bad passes lock by value: testdata.CustomLock"
8080

81+
// Passing lock values into interface function arguments
82+
func FuncCallInterfaceArg(f func(a int, b interface{})) {
83+
var m sync.Mutex
84+
var t struct{ lock sync.Mutex }
85+
86+
f(1, "foo")
87+
f(2, &t)
88+
f(3, &sync.Mutex{})
89+
f(4, m) // ERROR "function call copies lock value: sync.Mutex"
90+
f(5, t) // ERROR "function call copies lock value: struct{lock sync.Mutex} contains sync.Mutex"
91+
}
92+
93+
// Returning lock via interface value
94+
func ReturnViaInterface(x int) (int, interface{}) {
95+
var m sync.Mutex
96+
var t struct{ lock sync.Mutex }
97+
98+
switch x % 4 {
99+
case 0:
100+
return 0, "qwe"
101+
case 1:
102+
return 1, &sync.Mutex{}
103+
case 2:
104+
return 2, m // ERROR "return copies lock value: sync.Mutex"
105+
default:
106+
return 3, t // ERROR "return copies lock value: struct{lock sync.Mutex} contains sync.Mutex"
107+
}
108+
}
109+
81110
// TODO: Unfortunate cases
82111

83112
// Non-ideal error message:

0 commit comments

Comments
 (0)