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>
diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go
index d2340ca..6518f38 100644
--- a/go/analysis/analysistest/analysistest.go
+++ b/go/analysis/analysistest/analysistest.go
@@ -135,9 +135,11 @@
 	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/copylock/copylock.go b/go/analysis/passes/copylock/copylock.go
new file mode 100644
index 0000000..e684cd3
--- /dev/null
+++ b/go/analysis/passes/copylock/copylock.go
@@ -0,0 +1,283 @@
+// 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.
+
+package copylock
+
+import (
+	"bytes"
+	"fmt"
+	"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"
+)
+
+var Analyzer = &analysis.Analyzer{
+	Name:             "copylocks",
+	Doc:              "check for locks erroneously passed by value",
+	Requires:         []*analysis.Analyzer{inspect.Analyzer},
+	RunDespiteErrors: true,
+	Run:              run,
+}
+
+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(pass *analysis.Pass, as *ast.AssignStmt) {
+	for i, x := range as.Rhs {
+		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(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(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(pass *analysis.Pass, cl *ast.CompositeLit) {
+	for _, x := range cl.Elts {
+		if node, ok := x.(*ast.KeyValueExpr); ok {
+			x = node.Value
+		}
+		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(pass *analysis.Pass, rs *ast.ReturnStmt) {
+	for _, x := range rs.Results {
+		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(pass *analysis.Pass, ce *ast.CallExpr) {
+	var id *ast.Ident
+	switch fun := ce.Fun.(type) {
+	case *ast.Ident:
+		id = fun
+	case *ast.SelectorExpr:
+		id = fun.Sel
+	}
+	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(pass, x); path != nil {
+			pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path)
+		}
+	}
+}
+
+// checkCopyLocksFunc checks whether a function might
+// inadvertently copy a lock, by checking whether
+// its receiver, parameters, or return values
+// are locks.
+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(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(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
+				pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
+			}
+		}
+	}
+
+	// Don't check typ.Results. If T has a Lock field it's OK to write
+	//     return T{}
+	// because that is returning the zero value. Leave result checking
+	// to the return statement.
+}
+
+// checkCopyLocksRange checks whether a range statement
+// might inadvertently copy a lock by checking whether
+// any of the range variables are locks.
+func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) {
+	checkCopyLocksRangeVar(pass, r.Tok, r.Key)
+	checkCopyLocksRangeVar(pass, r.Tok, r.Value)
+}
+
+func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
+	if e == nil {
+		return
+	}
+	id, isId := e.(*ast.Ident)
+	if isId && id.Name == "_" {
+		return
+	}
+
+	var typ types.Type
+	if rtok == token.DEFINE {
+		if !isId {
+			return
+		}
+		obj := pass.TypesInfo.Defs[id]
+		if obj == nil {
+			return
+		}
+		typ = obj.Type()
+	} else {
+		typ = pass.TypesInfo.Types[e].Type
+	}
+
+	if typ == nil {
+		return
+	}
+	if path := lockPath(pass.Pkg, typ); path != nil {
+		pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
+	}
+}
+
+type typePath []types.Type
+
+// String pretty-prints a typePath.
+func (path typePath) String() string {
+	n := len(path)
+	var buf bytes.Buffer
+	for i := range path {
+		if i > 0 {
+			fmt.Fprint(&buf, " contains ")
+		}
+		// The human-readable path is in reverse order, outermost to innermost.
+		fmt.Fprint(&buf, path[n-i-1].String())
+	}
+	return buf.String()
+}
+
+func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
+	if _, ok := x.(*ast.CompositeLit); ok {
+		return nil
+	}
+	if _, ok := x.(*ast.CallExpr); ok {
+		// A call may return a zero value.
+		return nil
+	}
+	if star, ok := x.(*ast.StarExpr); ok {
+		if _, ok := star.X.(*ast.CallExpr); ok {
+			// A call may return a pointer to a zero value.
+			return nil
+		}
+	}
+	return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
+}
+
+// lockPath returns a typePath describing the location of a lock value
+// contained in typ. If there is no contained lock, it returns nil.
+func lockPath(tpkg *types.Package, typ types.Type) typePath {
+	if typ == nil {
+		return nil
+	}
+
+	for {
+		atyp, ok := typ.Underlying().(*types.Array)
+		if !ok {
+			break
+		}
+		typ = atyp.Elem()
+	}
+
+	// We're only interested in the case in which the underlying
+	// type is a struct. (Interfaces and pointers are safe to copy.)
+	styp, ok := typ.Underlying().(*types.Struct)
+	if !ok {
+		return nil
+	}
+
+	// We're looking for cases in which a pointer to this type
+	// is a sync.Locker, but a value is not. This differentiates
+	// embedded interfaces from embedded values.
+	if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
+		return []types.Type{typ}
+	}
+
+	nfields := styp.NumFields()
+	for i := 0; i < nfields; i++ {
+		ftyp := styp.Field(i).Type()
+		subpath := lockPath(tpkg, ftyp)
+		if subpath != nil {
+			return append(subpath, typ)
+		}
+	}
+
+	return nil
+}
+
+var lockerType *types.Interface
+
+// Construct a sync.Locker interface type.
+func init() {
+	nullary := types.NewSignature(nil, nil, nil, false) // func()
+	methods := []*types.Func{
+		types.NewFunc(token.NoPos, nil, "Lock", nullary),
+		types.NewFunc(token.NoPos, nil, "Unlock", nullary),
+	}
+	lockerType = types.NewInterface(methods, nil).Complete()
+}
diff --git a/go/analysis/passes/copylock/copylock_test.go b/go/analysis/passes/copylock/copylock_test.go
new file mode 100644
index 0000000..3e4ca12
--- /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 0000000..57d4076
--- /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 280747a..801bc6f 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 @@
 }
 
 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 @@
 }
 
 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 @@
 }
 
 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) 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 @@
 	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 @@
 	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 @@
 // 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 0000000..cd95a52
--- /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/copylock.go b/go/analysis/passes/vet/copylock.go
deleted file mode 100644
index f449045..0000000
--- a/go/analysis/passes/vet/copylock.go
+++ /dev/null
@@ -1,268 +0,0 @@
-// +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
-
-import (
-	"bytes"
-	"fmt"
-	"go/ast"
-	"go/token"
-	"go/types"
-)
-
-func init() {
-	register("copylocks",
-		"check that locks are not passed by value",
-		checkCopyLocks,
-		funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt)
-}
-
-// 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)
-	}
-}
-
-// checkCopyLocksAssign checks whether an assignment
-// copies a lock.
-func checkCopyLocksAssign(f *File, 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)
-		}
-	}
-}
-
-// checkCopyLocksGenDecl checks whether lock is copied
-// in variable declaration.
-func checkCopyLocksGenDecl(f *File, 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)
-			}
-		}
-	}
-}
-
-// checkCopyLocksCompositeLit detects lock copy inside a composite literal
-func checkCopyLocksCompositeLit(f *File, 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)
-		}
-	}
-}
-
-// checkCopyLocksReturnStmt detects lock copy in return statement
-func checkCopyLocksReturnStmt(f *File, 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)
-		}
-	}
-}
-
-// checkCopyLocksCallExpr detects lock copy in the arguments to a function call
-func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) {
-	var id *ast.Ident
-	switch fun := ce.Fun.(type) {
-	case *ast.Ident:
-		id = fun
-	case *ast.SelectorExpr:
-		id = fun.Sel
-	}
-	if fun, ok := f.pkg.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)
-		}
-	}
-}
-
-// checkCopyLocksFunc checks whether a function might
-// 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) {
-	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 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)
-			}
-		}
-	}
-
-	// Don't check typ.Results. If T has a Lock field it's OK to write
-	//     return T{}
-	// because that is returning the zero value. Leave result checking
-	// to the return statement.
-}
-
-// 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 checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) {
-	if e == nil {
-		return
-	}
-	id, isId := e.(*ast.Ident)
-	if isId && id.Name == "_" {
-		return
-	}
-
-	var typ types.Type
-	if rtok == token.DEFINE {
-		if !isId {
-			return
-		}
-		obj := f.pkg.defs[id]
-		if obj == nil {
-			return
-		}
-		typ = obj.Type()
-	} else {
-		typ = f.pkg.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)
-	}
-}
-
-type typePath []types.Type
-
-// String pretty-prints a typePath.
-func (path typePath) String() string {
-	n := len(path)
-	var buf bytes.Buffer
-	for i := range path {
-		if i > 0 {
-			fmt.Fprint(&buf, " contains ")
-		}
-		// The human-readable path is in reverse order, outermost to innermost.
-		fmt.Fprint(&buf, path[n-i-1].String())
-	}
-	return buf.String()
-}
-
-func lockPathRhs(f *File, x ast.Expr) typePath {
-	if _, ok := x.(*ast.CompositeLit); ok {
-		return nil
-	}
-	if _, ok := x.(*ast.CallExpr); ok {
-		// A call may return a zero value.
-		return nil
-	}
-	if star, ok := x.(*ast.StarExpr); ok {
-		if _, ok := star.X.(*ast.CallExpr); ok {
-			// A call may return a pointer to a zero value.
-			return nil
-		}
-	}
-	return lockPath(f.pkg.typesPkg, f.pkg.types[x].Type)
-}
-
-// lockPath returns a typePath describing the location of a lock value
-// contained in typ. If there is no contained lock, it returns nil.
-func lockPath(tpkg *types.Package, typ types.Type) typePath {
-	if typ == nil {
-		return nil
-	}
-
-	for {
-		atyp, ok := typ.Underlying().(*types.Array)
-		if !ok {
-			break
-		}
-		typ = atyp.Elem()
-	}
-
-	// We're only interested in the case in which the underlying
-	// type is a struct. (Interfaces and pointers are safe to copy.)
-	styp, ok := typ.Underlying().(*types.Struct)
-	if !ok {
-		return nil
-	}
-
-	// We're looking for cases in which a pointer to this type
-	// is a sync.Locker, but a value is not. This differentiates
-	// embedded interfaces from embedded values.
-	if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
-		return []types.Type{typ}
-	}
-
-	nfields := styp.NumFields()
-	for i := 0; i < nfields; i++ {
-		ftyp := styp.Field(i).Type()
-		subpath := lockPath(tpkg, ftyp)
-		if subpath != nil {
-			return append(subpath, typ)
-		}
-	}
-
-	return nil
-}
-
-var lockerType *types.Interface
-
-// Construct a sync.Locker interface type.
-func init() {
-	nullary := types.NewSignature(nil, nil, nil, false) // func()
-	methods := []*types.Func{
-		types.NewFunc(token.NoPos, nil, "Lock", nullary),
-		types.NewFunc(token.NoPos, nil, "Unlock", nullary),
-	}
-	lockerType = types.NewInterface(methods, nil).Complete()
-}
diff --git a/go/analysis/passes/vet/testdata/copylock.go b/go/analysis/passes/vet/testdata/copylock.go
deleted file mode 100644
index e9902a2..0000000
--- 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 f127381..0000000
--- 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"
-	}
-}