register("copylocks",
"check that locks are not passed by value",
checkCopyLocks,
- funcDecl, rangeStmt, funcLit)
+ funcDecl, rangeStmt, funcLit, assignStmt)
}
// checkCopyLocks checks whether node might
checkCopyLocksFunc(f, node.Name.Name, node.Recv, node.Type)
case *ast.FuncLit:
checkCopyLocksFunc(f, "func", nil, node.Type)
+ case *ast.AssignStmt:
+ checkCopyLocksAssign(f, node)
+ }
+}
+
+// checkCopyLocksAssign checks whether an assignment
+// copies a lock.
+func checkCopyLocksAssign(f *File, as *ast.AssignStmt) {
+ for _, x := range as.Lhs {
+ if path := lockPath(f.pkg.typesPkg, f.pkg.types[x].Type); path != nil {
+ f.Badf(x.Pos(), "assignment copies lock value to %v: %v", f.gofmt(x), path)
+ }
}
}
if recv != nil && len(recv.List) > 0 {
expr := recv.List[0].Type
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
- f.Badf(expr.Pos(), "%s passes Lock by value: %v", name, path)
+ f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
}
}
for _, field := range typ.Params.List {
expr := field.Type
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
- f.Badf(expr.Pos(), "%s passes Lock by value: %v", name, path)
+ f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
}
}
}
for _, field := range typ.Results.List {
expr := field.Type
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
- f.Badf(expr.Pos(), "%s returns Lock by value: %v", name, path)
+ f.Badf(expr.Pos(), "%s returns lock by value: %v", name, path)
}
}
}
return
}
if path := lockPath(f.pkg.typesPkg, typ); path != nil {
- f.Badf(e.Pos(), "range var %s copies Lock: %v", f.gofmt(e), path)
+ f.Badf(e.Pos(), "range var %s copies lock: %v", f.gofmt(e), path)
}
}
--- /dev/null
+package testdata
+
+import "sync"
+
+func OkFunc() {
+ var x *sync.Mutex
+ p := x
+ var y sync.Mutex
+ p = &y
+}
+
+type Tlock struct {
+ once sync.Once
+}
+
+func BadFunc() {
+ var x *sync.Mutex
+ p := x
+ var y sync.Mutex
+ p = &y
+ *p = *x // ERROR "assignment copies lock value to \*p: sync.Mutex"
+
+ var t Tlock
+ var tp *Tlock
+ tp = &t
+ *tp = t // ERROR "assignment copies lock value to \*tp: testdata.Tlock contains sync.Once contains sync.Mutex"
+ t = *tp // ERROR "assignment copies lock value to t: testdata.Tlock contains sync.Once contains sync.Mutex"
+}
import "sync"
func OkFunc(*sync.Mutex) {}
-func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes Lock by value: sync.Mutex"
+func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes lock by value: sync.Mutex"
func OkRet() *sync.Mutex {}
-func BadRet() sync.Mutex {} // ERROR "BadRet returns Lock by value: sync.Mutex"
+func BadRet() sync.Mutex {} // ERROR "BadRet returns lock by value: sync.Mutex"
var (
OkClosure = func(*sync.Mutex) {}
- BadClosure = func(sync.Mutex) {} // ERROR "func passes Lock by value: sync.Mutex"
+ BadClosure = func(sync.Mutex) {} // ERROR "func passes lock by value: sync.Mutex"
)
type EmbeddedRWMutex struct {
}
func (*EmbeddedRWMutex) OkMeth() {}
-func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes Lock by value: testdata.EmbeddedRWMutex"
+func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes lock by value: testdata.EmbeddedRWMutex"
func OkFunc(e *EmbeddedRWMutex) {}
-func BadFunc(EmbeddedRWMutex) {} // ERROR "BadFunc passes Lock by value: testdata.EmbeddedRWMutex"
+func BadFunc(EmbeddedRWMutex) {} // ERROR "BadFunc passes lock by value: testdata.EmbeddedRWMutex"
func OkRet() *EmbeddedRWMutex {}
-func BadRet() EmbeddedRWMutex {} // ERROR "BadRet returns Lock by value: testdata.EmbeddedRWMutex"
+func BadRet() EmbeddedRWMutex {} // ERROR "BadRet returns lock by value: testdata.EmbeddedRWMutex"
type FieldMutex struct {
s sync.Mutex
}
func (*FieldMutex) OkMeth() {}
-func (FieldMutex) BadMeth() {} // ERROR "BadMeth passes Lock by value: testdata.FieldMutex contains sync.Mutex"
+func (FieldMutex) BadMeth() {} // ERROR "BadMeth passes lock by value: testdata.FieldMutex contains sync.Mutex"
func OkFunc(*FieldMutex) {}
-func BadFunc(FieldMutex, int) {} // ERROR "BadFunc passes Lock by value: testdata.FieldMutex contains sync.Mutex"
+func BadFunc(FieldMutex, int) {} // ERROR "BadFunc passes lock by value: testdata.FieldMutex contains sync.Mutex"
type L0 struct {
L1
}
func (*L0) Ok() {}
-func (L0) Bad() {} // ERROR "Bad passes Lock by value: testdata.L0 contains testdata.L1 contains testdata.L2"
+func (L0) Bad() {} // ERROR "Bad passes lock by value: testdata.L0 contains testdata.L1 contains testdata.L2"
type EmbeddedMutexPointer struct {
s *sync.Mutex // safe to copy this pointer
func (*CustomLock) Unlock() {}
func Ok(*CustomLock) {}
-func Bad(CustomLock) {} // ERROR "Bad passes Lock by value: testdata.CustomLock"
+func Bad(CustomLock) {} // ERROR "Bad passes lock by value: testdata.CustomLock"
// TODO: Unfortunate cases
// sync.Mutex gets called out, but without any reference to the sync.Once.
type LocalOnce sync.Once
-func (LocalOnce) Bad() {} // ERROR "Bad passes Lock by value: testdata.LocalOnce contains sync.Mutex"
+func (LocalOnce) Bad() {} // ERROR "Bad passes lock by value: testdata.LocalOnce contains sync.Mutex"
// False negative:
// LocalMutex doesn't have a Lock method.
}
for i, _ := range s {
}
- for _, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex"
+ for _, mu = range s { // ERROR "range var mu copies lock: sync.Mutex"
}
- for _, m := range s { // ERROR "range var m copies Lock: sync.Mutex"
+ for _, m := range s { // ERROR "range var m copies lock: sync.Mutex"
}
- for i, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex"
+ for i, mu = range s { // ERROR "range var mu copies lock: sync.Mutex"
}
- for i, m := range s { // ERROR "range var m copies Lock: sync.Mutex"
+ for i, m := range s { // ERROR "range var m copies lock: sync.Mutex"
}
var a [3]sync.Mutex
- for _, m := range a { // ERROR "range var m copies Lock: sync.Mutex"
+ for _, m := range a { // ERROR "range var m copies lock: sync.Mutex"
}
var m map[sync.Mutex]sync.Mutex
- for k := range m { // ERROR "range var k copies Lock: sync.Mutex"
+ for k := range m { // ERROR "range var k copies lock: sync.Mutex"
}
- for mu, _ = range m { // ERROR "range var mu copies Lock: sync.Mutex"
+ for mu, _ = range m { // ERROR "range var mu copies lock: sync.Mutex"
}
- for k, _ := range m { // ERROR "range var k copies Lock: sync.Mutex"
+ for k, _ := range m { // ERROR "range var k copies lock: sync.Mutex"
}
- for _, mu = range m { // ERROR "range var mu copies Lock: sync.Mutex"
+ for _, mu = range m { // ERROR "range var mu copies lock: sync.Mutex"
}
- for _, v := range m { // ERROR "range var v copies Lock: sync.Mutex"
+ for _, v := range m { // ERROR "range var v copies lock: sync.Mutex"
}
var c chan sync.Mutex
for range c {
}
- for mu = range c { // ERROR "range var mu copies Lock: sync.Mutex"
+ for mu = range c { // ERROR "range var mu copies lock: sync.Mutex"
}
- for v := range c { // ERROR "range var v copies Lock: sync.Mutex"
+ for v := range c { // ERROR "range var v copies lock: sync.Mutex"
}
// Test non-idents in range variables
i int
mu sync.Mutex
}
- for t.i, t.mu = range s { // ERROR "range var t.mu copies Lock: sync.Mutex"
+ for t.i, t.mu = range s { // ERROR "range var t.mu copies lock: sync.Mutex"
}
}