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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if len(pkgs) != 1 {
|
||||||
return nil, fmt.Errorf("pattern %q expanded to %d packages, want 1",
|
return nil, fmt.Errorf("pattern %q expanded to %d packages, want 1",
|
||||||
pkgpath, len(pkgs))
|
pkgpath, len(pkgs))
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file contains the code to check that locks are not passed by value.
|
package copylock
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -14,87 +10,106 @@ import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"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() {
|
var Analyzer = &analysis.Analyzer{
|
||||||
register("copylocks",
|
Name: "copylocks",
|
||||||
"check that locks are not passed by value",
|
Doc: "check for locks erroneously passed by value",
|
||||||
checkCopyLocks,
|
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||||
funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt)
|
RunDespiteErrors: true,
|
||||||
|
Run: run,
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkCopyLocks checks whether node might
|
func run(pass *analysis.Pass) (interface{}, error) {
|
||||||
// inadvertently copy a lock.
|
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||||
func checkCopyLocks(f *File, node ast.Node) {
|
|
||||||
switch node := node.(type) {
|
nodeFilter := []ast.Node{
|
||||||
case *ast.RangeStmt:
|
(*ast.AssignStmt)(nil),
|
||||||
checkCopyLocksRange(f, node)
|
(*ast.CallExpr)(nil),
|
||||||
case *ast.FuncDecl:
|
(*ast.CompositeLit)(nil),
|
||||||
checkCopyLocksFunc(f, node.Name.Name, node.Recv, node.Type)
|
(*ast.FuncDecl)(nil),
|
||||||
case *ast.FuncLit:
|
(*ast.FuncLit)(nil),
|
||||||
checkCopyLocksFunc(f, "func", nil, node.Type)
|
(*ast.GenDecl)(nil),
|
||||||
case *ast.CallExpr:
|
(*ast.RangeStmt)(nil),
|
||||||
checkCopyLocksCallExpr(f, node)
|
(*ast.ReturnStmt)(nil),
|
||||||
case *ast.AssignStmt:
|
|
||||||
checkCopyLocksAssign(f, node)
|
|
||||||
case *ast.GenDecl:
|
|
||||||
checkCopyLocksGenDecl(f, node)
|
|
||||||
case *ast.CompositeLit:
|
|
||||||
checkCopyLocksCompositeLit(f, node)
|
|
||||||
case *ast.ReturnStmt:
|
|
||||||
checkCopyLocksReturnStmt(f, node)
|
|
||||||
}
|
}
|
||||||
|
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
|
// checkCopyLocksAssign checks whether an assignment
|
||||||
// copies a lock.
|
// copies a lock.
|
||||||
func checkCopyLocksAssign(f *File, as *ast.AssignStmt) {
|
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
|
||||||
for i, x := range as.Rhs {
|
for i, x := range as.Rhs {
|
||||||
if path := lockPathRhs(f, x); path != nil {
|
if path := lockPathRhs(pass, x); path != nil {
|
||||||
f.Badf(x.Pos(), "assignment copies lock value to %v: %v", f.gofmt(as.Lhs[i]), path)
|
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
|
// checkCopyLocksGenDecl checks whether lock is copied
|
||||||
// in variable declaration.
|
// in variable declaration.
|
||||||
func checkCopyLocksGenDecl(f *File, gd *ast.GenDecl) {
|
func checkCopyLocksGenDecl(pass *analysis.Pass, gd *ast.GenDecl) {
|
||||||
if gd.Tok != token.VAR {
|
if gd.Tok != token.VAR {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, spec := range gd.Specs {
|
for _, spec := range gd.Specs {
|
||||||
valueSpec := spec.(*ast.ValueSpec)
|
valueSpec := spec.(*ast.ValueSpec)
|
||||||
for i, x := range valueSpec.Values {
|
for i, x := range valueSpec.Values {
|
||||||
if path := lockPathRhs(f, x); path != nil {
|
if path := lockPathRhs(pass, x); path != nil {
|
||||||
f.Badf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
|
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
|
// 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 {
|
for _, x := range cl.Elts {
|
||||||
if node, ok := x.(*ast.KeyValueExpr); ok {
|
if node, ok := x.(*ast.KeyValueExpr); ok {
|
||||||
x = node.Value
|
x = node.Value
|
||||||
}
|
}
|
||||||
if path := lockPathRhs(f, x); path != nil {
|
if path := lockPathRhs(pass, x); path != nil {
|
||||||
f.Badf(x.Pos(), "literal copies lock value from %v: %v", f.gofmt(x), path)
|
pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkCopyLocksReturnStmt detects lock copy in return statement
|
// 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 {
|
for _, x := range rs.Results {
|
||||||
if path := lockPathRhs(f, x); path != nil {
|
if path := lockPathRhs(pass, x); path != nil {
|
||||||
f.Badf(x.Pos(), "return copies lock value: %v", path)
|
pass.Reportf(x.Pos(), "return copies lock value: %v", path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkCopyLocksCallExpr detects lock copy in the arguments to a function call
|
// 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
|
var id *ast.Ident
|
||||||
switch fun := ce.Fun.(type) {
|
switch fun := ce.Fun.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
|
@ -102,15 +117,15 @@ func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) {
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
id = fun.Sel
|
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() {
|
switch fun.Name() {
|
||||||
case "new", "len", "cap", "Sizeof":
|
case "new", "len", "cap", "Sizeof":
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, x := range ce.Args {
|
for _, x := range ce.Args {
|
||||||
if path := lockPathRhs(f, x); path != nil {
|
if path := lockPathRhs(pass, x); path != nil {
|
||||||
f.Badf(x.Pos(), "call of %s copies lock value: %v", f.gofmt(ce.Fun), path)
|
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
|
// inadvertently copy a lock, by checking whether
|
||||||
// its receiver, parameters, or return values
|
// its receiver, parameters, or return values
|
||||||
// are locks.
|
// 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 {
|
if recv != nil && len(recv.List) > 0 {
|
||||||
expr := recv.List[0].Type
|
expr := recv.List[0].Type
|
||||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||||
f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
|
pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ.Params != nil {
|
if typ.Params != nil {
|
||||||
for _, field := range typ.Params.List {
|
for _, field := range typ.Params.List {
|
||||||
expr := field.Type
|
expr := field.Type
|
||||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||||
f.Badf(expr.Pos(), "%s passes lock by value: %v", name, path)
|
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
|
// checkCopyLocksRange checks whether a range statement
|
||||||
// might inadvertently copy a lock by checking whether
|
// might inadvertently copy a lock by checking whether
|
||||||
// any of the range variables are locks.
|
// any of the range variables are locks.
|
||||||
func checkCopyLocksRange(f *File, r *ast.RangeStmt) {
|
func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) {
|
||||||
checkCopyLocksRangeVar(f, r.Tok, r.Key)
|
checkCopyLocksRangeVar(pass, r.Tok, r.Key)
|
||||||
checkCopyLocksRangeVar(f, r.Tok, r.Value)
|
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 {
|
if e == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -164,20 +179,20 @@ func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) {
|
||||||
if !isId {
|
if !isId {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
obj := f.pkg.defs[id]
|
obj := pass.TypesInfo.Defs[id]
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
typ = obj.Type()
|
typ = obj.Type()
|
||||||
} else {
|
} else {
|
||||||
typ = f.pkg.types[e].Type
|
typ = pass.TypesInfo.Types[e].Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if path := lockPath(f.pkg.typesPkg, typ); path != nil {
|
if path := lockPath(pass.Pkg, typ); path != nil {
|
||||||
f.Badf(e.Pos(), "range var %s copies lock: %v", f.gofmt(e), path)
|
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()
|
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 {
|
if _, ok := x.(*ast.CompositeLit); ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -211,7 +226,7 @@ func lockPathRhs(f *File, x ast.Expr) typePath {
|
||||||
return nil
|
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
|
// lockPath returns a typePath describing the location of a lock value
|
|
@ -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")
|
||||||
|
}
|
|
@ -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
|
// This file contains tests for the copylock checker's
|
||||||
// function declaration analysis.
|
// function declaration analysis.
|
||||||
|
|
||||||
package testdata
|
package a
|
||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
func OkFunc(*sync.Mutex) {}
|
func OkFunc(*sync.Mutex) {}
|
||||||
func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes lock by value: sync.Mutex"
|
func BadFunc(sync.Mutex) {} // want "BadFunc passes lock by value: sync.Mutex"
|
||||||
func BadFunc2(sync.Map) {} // ERROR "BadFunc2 passes lock by value: sync.Map contains sync.Mutex"
|
func BadFunc2(sync.Map) {} // want "BadFunc2 passes lock by value: sync.Map contains sync.Mutex"
|
||||||
func OkRet() *sync.Mutex {}
|
func OkRet() *sync.Mutex {}
|
||||||
func BadRet() sync.Mutex {} // Don't warn about results
|
func BadRet() sync.Mutex {} // Don't warn about results
|
||||||
|
|
||||||
var (
|
var (
|
||||||
OkClosure = func(*sync.Mutex) {}
|
OkClosure = func(*sync.Mutex) {}
|
||||||
BadClosure = func(sync.Mutex) {} // ERROR "func passes lock by value: sync.Mutex"
|
BadClosure = func(sync.Mutex) {} // want "func passes lock by value: sync.Mutex"
|
||||||
BadClosure2 = func(sync.Map) {} // ERROR "func passes lock by value: sync.Map contains sync.Mutex"
|
BadClosure2 = func(sync.Map) {} // want "func passes lock by value: sync.Map contains sync.Mutex"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmbeddedRWMutex struct {
|
type EmbeddedRWMutex struct {
|
||||||
|
@ -26,9 +26,9 @@ type EmbeddedRWMutex struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*EmbeddedRWMutex) OkMeth() {}
|
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 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 OkRet() *EmbeddedRWMutex {}
|
||||||
func BadRet() EmbeddedRWMutex {} // Don't warn about results
|
func BadRet() EmbeddedRWMutex {} // Don't warn about results
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ type FieldMutex struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*FieldMutex) OkMeth() {}
|
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 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 {
|
type L0 struct {
|
||||||
L1
|
L1
|
||||||
|
@ -54,7 +54,7 @@ type L2 struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*L0) Ok() {}
|
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 {
|
type EmbeddedMutexPointer struct {
|
||||||
s *sync.Mutex // safe to copy this pointer
|
s *sync.Mutex // safe to copy this pointer
|
||||||
|
@ -78,7 +78,7 @@ func (*CustomLock) Lock() {}
|
||||||
func (*CustomLock) Unlock() {}
|
func (*CustomLock) Unlock() {}
|
||||||
|
|
||||||
func Ok(*CustomLock) {}
|
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
|
// Passing lock values into interface function arguments
|
||||||
func FuncCallInterfaceArg(f func(a int, b interface{})) {
|
func FuncCallInterfaceArg(f func(a int, b interface{})) {
|
||||||
|
@ -88,10 +88,10 @@ func FuncCallInterfaceArg(f func(a int, b interface{})) {
|
||||||
f(1, "foo")
|
f(1, "foo")
|
||||||
f(2, &t)
|
f(2, &t)
|
||||||
f(3, &sync.Mutex{})
|
f(3, &sync.Mutex{})
|
||||||
f(4, m) // ERROR "call of f copies lock value: sync.Mutex"
|
f(4, m) // want "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(5, t) // want "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
|
||||||
var fntab []func(t)
|
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
|
// Returning lock via interface value
|
||||||
|
@ -105,9 +105,9 @@ func ReturnViaInterface(x int) (int, interface{}) {
|
||||||
case 1:
|
case 1:
|
||||||
return 1, &sync.Mutex{}
|
return 1, &sync.Mutex{}
|
||||||
case 2:
|
case 2:
|
||||||
return 2, m // ERROR "return copies lock value: sync.Mutex"
|
return 2, m // want "return copies lock value: sync.Mutex"
|
||||||
default:
|
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.
|
// sync.Mutex gets called out, but without any reference to the sync.Once.
|
||||||
type LocalOnce 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:
|
// False negative:
|
||||||
// LocalMutex doesn't have a Lock method.
|
// LocalMutex doesn't have a Lock method.
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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