mirror of
https://github.com/golang/go
synced 2024-11-18 17:54:57 -07:00
go/analysis/passes/copylock: split out of vet
Change-Id: Ib7c735e714d8b5436d055bf99ca90e840c963c7b Reviewed-on: https://go-review.googlesource.com/c/140740 Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
a2b3f7f249
commit
6d96510a3a
@ -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))
|
||||
|
@ -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
|
13
go/analysis/passes/copylock/copylock_test.go
Normal file
13
go/analysis/passes/copylock/copylock_test.go
Normal file
@ -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")
|
||||
}
|
188
go/analysis/passes/copylock/testdata/src/a/copylock.go
vendored
Normal file
188
go/analysis/passes/copylock/testdata/src/a/copylock.go
vendored
Normal file
@ -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{}
|
||||
}
|
@ -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.
|
67
go/analysis/passes/copylock/testdata/src/a/copylock_range.go
vendored
Normal file
67
go/analysis/passes/copylock/testdata/src/a/copylock_range.go
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
188
go/analysis/passes/vet/testdata/copylock.go
vendored
188
go/analysis/passes/vet/testdata/copylock.go
vendored
@ -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{}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user