diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go index d2340cad..6518f388 100644 --- a/go/analysis/analysistest/analysistest.go +++ b/go/analysis/analysistest/analysistest.go @@ -135,9 +135,11 @@ func loadPackage(dir, pkgpath string) (*packages.Package, error) { if err != nil { return nil, err } - if packages.PrintErrors(pkgs) > 0 { - return nil, fmt.Errorf("loading %s failed", pkgpath) - } + + // Print errors but do not stop: + // some Analyzers may be disposed to RunDespiteErrors + packages.PrintErrors(pkgs) + if len(pkgs) != 1 { return nil, fmt.Errorf("pattern %q expanded to %d packages, want 1", pkgpath, len(pkgs)) diff --git a/go/analysis/passes/vet/copylock.go b/go/analysis/passes/copylock/copylock.go similarity index 54% rename from go/analysis/passes/vet/copylock.go rename to go/analysis/passes/copylock/copylock.go index f4490457..e684cd37 100644 --- a/go/analysis/passes/vet/copylock.go +++ b/go/analysis/passes/copylock/copylock.go @@ -1,12 +1,8 @@ -// +build ignore - // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file contains the code to check that locks are not passed by value. - -package main +package copylock import ( "bytes" @@ -14,87 +10,106 @@ import ( "go/ast" "go/token" "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/analysis/passes/internal/analysisutil" + "golang.org/x/tools/go/ast/inspector" ) -func init() { - register("copylocks", - "check that locks are not passed by value", - checkCopyLocks, - funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt) +var Analyzer = &analysis.Analyzer{ + Name: "copylocks", + Doc: "check for locks erroneously passed by value", + Requires: []*analysis.Analyzer{inspect.Analyzer}, + RunDespiteErrors: true, + Run: run, } -// checkCopyLocks checks whether node might -// inadvertently copy a lock. -func checkCopyLocks(f *File, node ast.Node) { - switch node := node.(type) { - case *ast.RangeStmt: - checkCopyLocksRange(f, node) - case *ast.FuncDecl: - checkCopyLocksFunc(f, node.Name.Name, node.Recv, node.Type) - case *ast.FuncLit: - checkCopyLocksFunc(f, "func", nil, node.Type) - case *ast.CallExpr: - checkCopyLocksCallExpr(f, node) - case *ast.AssignStmt: - checkCopyLocksAssign(f, node) - case *ast.GenDecl: - checkCopyLocksGenDecl(f, node) - case *ast.CompositeLit: - checkCopyLocksCompositeLit(f, node) - case *ast.ReturnStmt: - checkCopyLocksReturnStmt(f, node) +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.AssignStmt)(nil), + (*ast.CallExpr)(nil), + (*ast.CompositeLit)(nil), + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + (*ast.GenDecl)(nil), + (*ast.RangeStmt)(nil), + (*ast.ReturnStmt)(nil), } + inspect.Preorder(nodeFilter, func(node ast.Node) { + switch node := node.(type) { + case *ast.RangeStmt: + checkCopyLocksRange(pass, node) + case *ast.FuncDecl: + checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type) + case *ast.FuncLit: + checkCopyLocksFunc(pass, "func", nil, node.Type) + case *ast.CallExpr: + checkCopyLocksCallExpr(pass, node) + case *ast.AssignStmt: + checkCopyLocksAssign(pass, node) + case *ast.GenDecl: + checkCopyLocksGenDecl(pass, node) + case *ast.CompositeLit: + checkCopyLocksCompositeLit(pass, node) + case *ast.ReturnStmt: + checkCopyLocksReturnStmt(pass, node) + } + }) + return nil, nil } // checkCopyLocksAssign checks whether an assignment // copies a lock. -func checkCopyLocksAssign(f *File, as *ast.AssignStmt) { +func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) { for i, x := range as.Rhs { - if path := lockPathRhs(f, x); path != nil { - f.Badf(x.Pos(), "assignment copies lock value to %v: %v", f.gofmt(as.Lhs[i]), path) + if path := lockPathRhs(pass, x); path != nil { + pass.Reportf(x.Pos(), "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path) } } } // checkCopyLocksGenDecl checks whether lock is copied // in variable declaration. -func checkCopyLocksGenDecl(f *File, gd *ast.GenDecl) { +func checkCopyLocksGenDecl(pass *analysis.Pass, gd *ast.GenDecl) { if gd.Tok != token.VAR { return } for _, spec := range gd.Specs { valueSpec := spec.(*ast.ValueSpec) for i, x := range valueSpec.Values { - if path := lockPathRhs(f, x); path != nil { - f.Badf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path) + if path := lockPathRhs(pass, x); path != nil { + pass.Reportf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path) } } } } // checkCopyLocksCompositeLit detects lock copy inside a composite literal -func checkCopyLocksCompositeLit(f *File, cl *ast.CompositeLit) { +func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) { for _, x := range cl.Elts { if node, ok := x.(*ast.KeyValueExpr); ok { x = node.Value } - if path := lockPathRhs(f, x); path != nil { - f.Badf(x.Pos(), "literal copies lock value from %v: %v", f.gofmt(x), path) + if path := lockPathRhs(pass, x); path != nil { + pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path) } } } // checkCopyLocksReturnStmt detects lock copy in return statement -func checkCopyLocksReturnStmt(f *File, rs *ast.ReturnStmt) { +func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) { for _, x := range rs.Results { - if path := lockPathRhs(f, x); path != nil { - f.Badf(x.Pos(), "return copies lock value: %v", path) + if path := lockPathRhs(pass, x); path != nil { + pass.Reportf(x.Pos(), "return copies lock value: %v", path) } } } // checkCopyLocksCallExpr detects lock copy in the arguments to a function call -func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) { +func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) { var id *ast.Ident switch fun := ce.Fun.(type) { case *ast.Ident: @@ -102,15 +117,15 @@ func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) { case *ast.SelectorExpr: id = fun.Sel } - if fun, ok := f.pkg.uses[id].(*types.Builtin); ok { + if fun, ok := pass.TypesInfo.Uses[id].(*types.Builtin); ok { switch fun.Name() { case "new", "len", "cap", "Sizeof": return } } for _, x := range ce.Args { - if path := lockPathRhs(f, x); path != nil { - f.Badf(x.Pos(), "call of %s copies lock value: %v", f.gofmt(ce.Fun), path) + if path := lockPathRhs(pass, x); path != nil { + pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path) } } } @@ -119,19 +134,19 @@ func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) { // inadvertently copy a lock, by checking whether // its receiver, parameters, or return values // are locks. -func checkCopyLocksFunc(f *File, name string, recv *ast.FieldList, typ *ast.FuncType) { +func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) { 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) + if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil { + pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path) } } if typ.Params != nil { 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) + if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil { + pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path) } } } @@ -145,12 +160,12 @@ func checkCopyLocksFunc(f *File, name string, recv *ast.FieldList, typ *ast.Func // checkCopyLocksRange checks whether a range statement // might inadvertently copy a lock by checking whether // any of the range variables are locks. -func checkCopyLocksRange(f *File, r *ast.RangeStmt) { - checkCopyLocksRangeVar(f, r.Tok, r.Key) - checkCopyLocksRangeVar(f, r.Tok, r.Value) +func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) { + checkCopyLocksRangeVar(pass, r.Tok, r.Key) + checkCopyLocksRangeVar(pass, r.Tok, r.Value) } -func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) { +func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) { if e == nil { return } @@ -164,20 +179,20 @@ func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) { if !isId { return } - obj := f.pkg.defs[id] + obj := pass.TypesInfo.Defs[id] if obj == nil { return } typ = obj.Type() } else { - typ = f.pkg.types[e].Type + typ = pass.TypesInfo.Types[e].Type } if typ == nil { return } - if path := lockPath(f.pkg.typesPkg, typ); path != nil { - f.Badf(e.Pos(), "range var %s copies lock: %v", f.gofmt(e), path) + if path := lockPath(pass.Pkg, typ); path != nil { + pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path) } } @@ -197,7 +212,7 @@ func (path typePath) String() string { return buf.String() } -func lockPathRhs(f *File, x ast.Expr) typePath { +func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath { if _, ok := x.(*ast.CompositeLit); ok { return nil } @@ -211,7 +226,7 @@ func lockPathRhs(f *File, x ast.Expr) typePath { return nil } } - return lockPath(f.pkg.typesPkg, f.pkg.types[x].Type) + return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type) } // lockPath returns a typePath describing the location of a lock value diff --git a/go/analysis/passes/copylock/copylock_test.go b/go/analysis/passes/copylock/copylock_test.go new file mode 100644 index 00000000..3e4ca122 --- /dev/null +++ b/go/analysis/passes/copylock/copylock_test.go @@ -0,0 +1,13 @@ +package copylock_test + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" + "golang.org/x/tools/go/analysis/passes/copylock" +) + +func TestFromFileSystem(t *testing.T) { + testdata := analysistest.TestData() + analysistest.Run(t, testdata, copylock.Analyzer, "a") +} diff --git a/go/analysis/passes/copylock/testdata/src/a/copylock.go b/go/analysis/passes/copylock/testdata/src/a/copylock.go new file mode 100644 index 00000000..57d40765 --- /dev/null +++ b/go/analysis/passes/copylock/testdata/src/a/copylock.go @@ -0,0 +1,188 @@ +package a + +import ( + "sync" + "sync/atomic" + "unsafe" + . "unsafe" + unsafe1 "unsafe" +) + +func OkFunc() { + var x *sync.Mutex + p := x + var y sync.Mutex + p = &y + + var z = sync.Mutex{} + w := sync.Mutex{} + + w = sync.Mutex{} + q := struct{ L sync.Mutex }{ + L: sync.Mutex{}, + } + + yy := []Tlock{ + Tlock{}, + Tlock{ + once: sync.Once{}, + }, + } + + nl := new(sync.Mutex) + mx := make([]sync.Mutex, 10) + xx := struct{ L *sync.Mutex }{ + L: new(sync.Mutex), + } +} + +type Tlock struct { + once sync.Once +} + +func BadFunc() { + var x *sync.Mutex + p := x + var y sync.Mutex + p = &y + *p = *x // want `assignment copies lock value to \*p: sync.Mutex` + + var t Tlock + var tp *Tlock + tp = &t + *tp = t // want `assignment copies lock value to \*tp: a.Tlock contains sync.Once contains sync.Mutex` + t = *tp // want "assignment copies lock value to t: a.Tlock contains sync.Once contains sync.Mutex" + + y := *x // want "assignment copies lock value to y: sync.Mutex" + var z = t // want "variable declaration copies lock value to z: a.Tlock contains sync.Once contains sync.Mutex" + + w := struct{ L sync.Mutex }{ + L: *x, // want `literal copies lock value from \*x: sync.Mutex` + } + var q = map[int]Tlock{ + 1: t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex" + 2: *tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex` + } + yy := []Tlock{ + t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex" + *tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex` + } + + // override 'new' keyword + new := func(interface{}) {} + new(t) // want "call of new copies lock value: a.Tlock contains sync.Once contains sync.Mutex" + + // copy of array of locks + var muA [5]sync.Mutex + muB := muA // want "assignment copies lock value to muB: sync.Mutex" + muA = muB // want "assignment copies lock value to muA: sync.Mutex" + muSlice := muA[:] // OK + + // multidimensional array + var mmuA [5][5]sync.Mutex + mmuB := mmuA // want "assignment copies lock value to mmuB: sync.Mutex" + mmuA = mmuB // want "assignment copies lock value to mmuA: sync.Mutex" + mmuSlice := mmuA[:] // OK + + // slice copy is ok + var fmuA [5][][5]sync.Mutex + fmuB := fmuA // OK + fmuA = fmuB // OK + fmuSlice := fmuA[:] // OK +} + +func LenAndCapOnLockArrays() { + var a [5]sync.Mutex + aLen := len(a) // OK + aCap := cap(a) // OK + + // override 'len' and 'cap' keywords + + len := func(interface{}) {} + len(a) // want "call of len copies lock value: sync.Mutex" + + cap := func(interface{}) {} + cap(a) // want "call of cap copies lock value: sync.Mutex" +} + +func SizeofMutex() { + var mu sync.Mutex + unsafe.Sizeof(mu) // OK + unsafe1.Sizeof(mu) // OK + Sizeof(mu) // OK + unsafe := struct{ Sizeof func(interface{}) }{} + unsafe.Sizeof(mu) // want "call of unsafe.Sizeof copies lock value: sync.Mutex" + Sizeof := func(interface{}) {} + Sizeof(mu) // want "call of Sizeof copies lock value: sync.Mutex" +} + +// SyncTypesCheck checks copying of sync.* types except sync.Mutex +func SyncTypesCheck() { + // sync.RWMutex copying + var rwmuX sync.RWMutex + var rwmuXX = sync.RWMutex{} + rwmuX1 := new(sync.RWMutex) + rwmuY := rwmuX // want "assignment copies lock value to rwmuY: sync.RWMutex" + rwmuY = rwmuX // want "assignment copies lock value to rwmuY: sync.RWMutex" + var rwmuYY = rwmuX // want "variable declaration copies lock value to rwmuYY: sync.RWMutex" + rwmuP := &rwmuX + rwmuZ := &sync.RWMutex{} + + // sync.Cond copying + var condX sync.Cond + var condXX = sync.Cond{} + condX1 := new(sync.Cond) + condY := condX // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy" + condY = condX // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy" + var condYY = condX // want "variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy" + condP := &condX + condZ := &sync.Cond{ + L: &sync.Mutex{}, + } + condZ = sync.NewCond(&sync.Mutex{}) + + // sync.WaitGroup copying + var wgX sync.WaitGroup + var wgXX = sync.WaitGroup{} + wgX1 := new(sync.WaitGroup) + wgY := wgX // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" + wgY = wgX // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" + var wgYY = wgX // want "variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy" + wgP := &wgX + wgZ := &sync.WaitGroup{} + + // sync.Pool copying + var poolX sync.Pool + var poolXX = sync.Pool{} + poolX1 := new(sync.Pool) + poolY := poolX // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" + poolY = poolX // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" + var poolYY = poolX // want "variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy" + poolP := &poolX + poolZ := &sync.Pool{} + + // sync.Once copying + var onceX sync.Once + var onceXX = sync.Once{} + onceX1 := new(sync.Once) + onceY := onceX // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex" + onceY = onceX // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex" + var onceYY = onceX // want "variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex" + onceP := &onceX + onceZ := &sync.Once{} +} + +// AtomicTypesCheck checks copying of sync/atomic types +func AtomicTypesCheck() { + // atomic.Value copying + var vX atomic.Value + var vXX = atomic.Value{} + vX1 := new(atomic.Value) + // These are OK because the value has not been used yet. + // (And vet can't tell whether it has been used, so they're always OK.) + vY := vX + vY = vX + var vYY = vX + vP := &vX + vZ := &atomic.Value{} +} diff --git a/go/analysis/passes/vet/testdata/copylock_func.go b/go/analysis/passes/copylock/testdata/src/a/copylock_func.go similarity index 62% rename from go/analysis/passes/vet/testdata/copylock_func.go rename to go/analysis/passes/copylock/testdata/src/a/copylock_func.go index 280747a3..801bc6f2 100644 --- a/go/analysis/passes/vet/testdata/copylock_func.go +++ b/go/analysis/passes/copylock/testdata/src/a/copylock_func.go @@ -5,20 +5,20 @@ // This file contains tests for the copylock checker's // function declaration analysis. -package testdata +package a import "sync" func OkFunc(*sync.Mutex) {} -func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes lock by value: sync.Mutex" -func BadFunc2(sync.Map) {} // ERROR "BadFunc2 passes lock by value: sync.Map contains sync.Mutex" +func BadFunc(sync.Mutex) {} // want "BadFunc passes lock by value: sync.Mutex" +func BadFunc2(sync.Map) {} // want "BadFunc2 passes lock by value: sync.Map contains sync.Mutex" func OkRet() *sync.Mutex {} func BadRet() sync.Mutex {} // Don't warn about results var ( OkClosure = func(*sync.Mutex) {} - BadClosure = func(sync.Mutex) {} // ERROR "func passes lock by value: sync.Mutex" - BadClosure2 = func(sync.Map) {} // ERROR "func passes lock by value: sync.Map contains sync.Mutex" + BadClosure = func(sync.Mutex) {} // want "func passes lock by value: sync.Mutex" + BadClosure2 = func(sync.Map) {} // want "func passes lock by value: sync.Map contains sync.Mutex" ) type EmbeddedRWMutex struct { @@ -26,9 +26,9 @@ type EmbeddedRWMutex struct { } func (*EmbeddedRWMutex) OkMeth() {} -func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes lock by value: testdata.EmbeddedRWMutex" +func (EmbeddedRWMutex) BadMeth() {} // want "BadMeth passes lock by value: a.EmbeddedRWMutex" func OkFunc(e *EmbeddedRWMutex) {} -func BadFunc(EmbeddedRWMutex) {} // ERROR "BadFunc passes lock by value: testdata.EmbeddedRWMutex" +func BadFunc(EmbeddedRWMutex) {} // want "BadFunc passes lock by value: a.EmbeddedRWMutex" func OkRet() *EmbeddedRWMutex {} func BadRet() EmbeddedRWMutex {} // Don't warn about results @@ -37,9 +37,9 @@ type FieldMutex struct { } func (*FieldMutex) OkMeth() {} -func (FieldMutex) BadMeth() {} // ERROR "BadMeth passes lock by value: testdata.FieldMutex contains sync.Mutex" +func (FieldMutex) BadMeth() {} // want "BadMeth passes lock by value: a.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) {} // want "BadFunc passes lock by value: a.FieldMutex contains sync.Mutex" type L0 struct { L1 @@ -54,7 +54,7 @@ type L2 struct { } func (*L0) Ok() {} -func (L0) Bad() {} // ERROR "Bad passes lock by value: testdata.L0 contains testdata.L1 contains testdata.L2" +func (L0) Bad() {} // want "Bad passes lock by value: a.L0 contains a.L1 contains a.L2" type EmbeddedMutexPointer struct { s *sync.Mutex // safe to copy this pointer @@ -78,7 +78,7 @@ func (*CustomLock) Lock() {} func (*CustomLock) Unlock() {} func Ok(*CustomLock) {} -func Bad(CustomLock) {} // ERROR "Bad passes lock by value: testdata.CustomLock" +func Bad(CustomLock) {} // want "Bad passes lock by value: a.CustomLock" // Passing lock values into interface function arguments func FuncCallInterfaceArg(f func(a int, b interface{})) { @@ -88,10 +88,10 @@ func FuncCallInterfaceArg(f func(a int, b interface{})) { f(1, "foo") f(2, &t) f(3, &sync.Mutex{}) - f(4, m) // ERROR "call of f copies lock value: sync.Mutex" - f(5, t) // ERROR "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex" + f(4, m) // want "call of f copies lock value: sync.Mutex" + f(5, t) // want "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex" var fntab []func(t) - fntab[0](t) // ERROR "call of fntab.0. copies lock value: struct.lock sync.Mutex. contains sync.Mutex" + fntab[0](t) // want "call of fntab.0. copies lock value: struct.lock sync.Mutex. contains sync.Mutex" } // Returning lock via interface value @@ -105,9 +105,9 @@ func ReturnViaInterface(x int) (int, interface{}) { case 1: return 1, &sync.Mutex{} case 2: - return 2, m // ERROR "return copies lock value: sync.Mutex" + return 2, m // want "return copies lock value: sync.Mutex" default: - return 3, t // ERROR "return copies lock value: struct.lock sync.Mutex. contains sync.Mutex" + return 3, t // want "return copies lock value: struct.lock sync.Mutex. contains sync.Mutex" } } @@ -126,7 +126,7 @@ func AcceptedCases() { // 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() {} // want "Bad passes lock by value: a.LocalOnce contains sync.Mutex" // False negative: // LocalMutex doesn't have a Lock method. diff --git a/go/analysis/passes/copylock/testdata/src/a/copylock_range.go b/go/analysis/passes/copylock/testdata/src/a/copylock_range.go new file mode 100644 index 00000000..cd95a52c --- /dev/null +++ b/go/analysis/passes/copylock/testdata/src/a/copylock_range.go @@ -0,0 +1,67 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the copylock checker's +// range statement analysis. + +package a + +import "sync" + +func rangeMutex() { + var mu sync.Mutex + var i int + + var s []sync.Mutex + for range s { + } + for i = range s { + } + for i := range s { + } + for i, _ = range s { + } + for i, _ := range s { + } + for _, mu = range s { // want "range var mu copies lock: sync.Mutex" + } + for _, m := range s { // want "range var m copies lock: sync.Mutex" + } + for i, mu = range s { // want "range var mu copies lock: sync.Mutex" + } + for i, m := range s { // want "range var m copies lock: sync.Mutex" + } + + var a [3]sync.Mutex + for _, m := range a { // want "range var m copies lock: sync.Mutex" + } + + var m map[sync.Mutex]sync.Mutex + for k := range m { // want "range var k copies lock: sync.Mutex" + } + for mu, _ = range m { // want "range var mu copies lock: sync.Mutex" + } + for k, _ := range m { // want "range var k copies lock: sync.Mutex" + } + for _, mu = range m { // want "range var mu copies lock: sync.Mutex" + } + for _, v := range m { // want "range var v copies lock: sync.Mutex" + } + + var c chan sync.Mutex + for range c { + } + for mu = range c { // want "range var mu copies lock: sync.Mutex" + } + for v := range c { // want "range var v copies lock: sync.Mutex" + } + + // Test non-idents in range variables + var t struct { + i int + mu sync.Mutex + } + for t.i, t.mu = range s { // want "range var t.mu copies lock: sync.Mutex" + } +} diff --git a/go/analysis/passes/vet/testdata/copylock.go b/go/analysis/passes/vet/testdata/copylock.go deleted file mode 100644 index e9902a27..00000000 --- a/go/analysis/passes/vet/testdata/copylock.go +++ /dev/null @@ -1,188 +0,0 @@ -package testdata - -import ( - "sync" - "sync/atomic" - "unsafe" - . "unsafe" - unsafe1 "unsafe" -) - -func OkFunc() { - var x *sync.Mutex - p := x - var y sync.Mutex - p = &y - - var z = sync.Mutex{} - w := sync.Mutex{} - - w = sync.Mutex{} - q := struct{ L sync.Mutex }{ - L: sync.Mutex{}, - } - - yy := []Tlock{ - Tlock{}, - Tlock{ - once: sync.Once{}, - }, - } - - nl := new(sync.Mutex) - mx := make([]sync.Mutex, 10) - xx := struct{ L *sync.Mutex }{ - L: new(sync.Mutex), - } -} - -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" - - y := *x // ERROR "assignment copies lock value to y: sync.Mutex" - var z = t // ERROR "variable declaration copies lock value to z: testdata.Tlock contains sync.Once contains sync.Mutex" - - w := struct{ L sync.Mutex }{ - L: *x, // ERROR "literal copies lock value from \*x: sync.Mutex" - } - var q = map[int]Tlock{ - 1: t, // ERROR "literal copies lock value from t: testdata.Tlock contains sync.Once contains sync.Mutex" - 2: *tp, // ERROR "literal copies lock value from \*tp: testdata.Tlock contains sync.Once contains sync.Mutex" - } - yy := []Tlock{ - t, // ERROR "literal copies lock value from t: testdata.Tlock contains sync.Once contains sync.Mutex" - *tp, // ERROR "literal copies lock value from \*tp: testdata.Tlock contains sync.Once contains sync.Mutex" - } - - // override 'new' keyword - new := func(interface{}) {} - new(t) // ERROR "call of new copies lock value: testdata.Tlock contains sync.Once contains sync.Mutex" - - // copy of array of locks - var muA [5]sync.Mutex - muB := muA // ERROR "assignment copies lock value to muB: sync.Mutex" - muA = muB // ERROR "assignment copies lock value to muA: sync.Mutex" - muSlice := muA[:] // OK - - // multidimensional array - var mmuA [5][5]sync.Mutex - mmuB := mmuA // ERROR "assignment copies lock value to mmuB: sync.Mutex" - mmuA = mmuB // ERROR "assignment copies lock value to mmuA: sync.Mutex" - mmuSlice := mmuA[:] // OK - - // slice copy is ok - var fmuA [5][][5]sync.Mutex - fmuB := fmuA // OK - fmuA = fmuB // OK - fmuSlice := fmuA[:] // OK -} - -func LenAndCapOnLockArrays() { - var a [5]sync.Mutex - aLen := len(a) // OK - aCap := cap(a) // OK - - // override 'len' and 'cap' keywords - - len := func(interface{}) {} - len(a) // ERROR "call of len copies lock value: sync.Mutex" - - cap := func(interface{}) {} - cap(a) // ERROR "call of cap copies lock value: sync.Mutex" -} - -func SizeofMutex() { - var mu sync.Mutex - unsafe.Sizeof(mu) // OK - unsafe1.Sizeof(mu) // OK - Sizeof(mu) // OK - unsafe := struct{ Sizeof func(interface{}) }{} - unsafe.Sizeof(mu) // ERROR "call of unsafe.Sizeof copies lock value: sync.Mutex" - Sizeof := func(interface{}) {} - Sizeof(mu) // ERROR "call of Sizeof copies lock value: sync.Mutex" -} - -// SyncTypesCheck checks copying of sync.* types except sync.Mutex -func SyncTypesCheck() { - // sync.RWMutex copying - var rwmuX sync.RWMutex - var rwmuXX = sync.RWMutex{} - rwmuX1 := new(sync.RWMutex) - rwmuY := rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex" - rwmuY = rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex" - var rwmuYY = rwmuX // ERROR "variable declaration copies lock value to rwmuYY: sync.RWMutex" - rwmuP := &rwmuX - rwmuZ := &sync.RWMutex{} - - // sync.Cond copying - var condX sync.Cond - var condXX = sync.Cond{} - condX1 := new(sync.Cond) - condY := condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy" - condY = condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy" - var condYY = condX // ERROR "variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy" - condP := &condX - condZ := &sync.Cond{ - L: &sync.Mutex{}, - } - condZ = sync.NewCond(&sync.Mutex{}) - - // sync.WaitGroup copying - var wgX sync.WaitGroup - var wgXX = sync.WaitGroup{} - wgX1 := new(sync.WaitGroup) - wgY := wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" - wgY = wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" - var wgYY = wgX // ERROR "variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy" - wgP := &wgX - wgZ := &sync.WaitGroup{} - - // sync.Pool copying - var poolX sync.Pool - var poolXX = sync.Pool{} - poolX1 := new(sync.Pool) - poolY := poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" - poolY = poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" - var poolYY = poolX // ERROR "variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy" - poolP := &poolX - poolZ := &sync.Pool{} - - // sync.Once copying - var onceX sync.Once - var onceXX = sync.Once{} - onceX1 := new(sync.Once) - onceY := onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex" - onceY = onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex" - var onceYY = onceX // ERROR "variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex" - onceP := &onceX - onceZ := &sync.Once{} -} - -// AtomicTypesCheck checks copying of sync/atomic types -func AtomicTypesCheck() { - // atomic.Value copying - var vX atomic.Value - var vXX = atomic.Value{} - vX1 := new(atomic.Value) - // These are OK because the value has not been used yet. - // (And vet can't tell whether it has been used, so they're always OK.) - vY := vX - vY = vX - var vYY = vX - vP := &vX - vZ := &atomic.Value{} -} diff --git a/go/analysis/passes/vet/testdata/copylock_range.go b/go/analysis/passes/vet/testdata/copylock_range.go deleted file mode 100644 index f1273812..00000000 --- a/go/analysis/passes/vet/testdata/copylock_range.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains tests for the copylock checker's -// range statement analysis. - -package testdata - -import "sync" - -func rangeMutex() { - var mu sync.Mutex - var i int - - var s []sync.Mutex - for range s { - } - for i = range s { - } - for i := range s { - } - for i, _ = range s { - } - for i, _ := range s { - } - 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 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" - } - - var a [3]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 mu, _ = range m { // ERROR "range var mu 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 _, 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 v := range c { // ERROR "range var v copies lock: sync.Mutex" - } - - // Test non-idents in range variables - var t struct { - i int - mu sync.Mutex - } - for t.i, t.mu = range s { // ERROR "range var t.mu copies lock: sync.Mutex" - } -}