mirror of
https://github.com/golang/go
synced 2024-11-19 05:04:43 -07:00
f895b43688
This removes much of the AST logic out of main.go, and makes it easier to build custom vet binaries The trade-off in this change is for flexibility. There's very little change in the per-check files, a lot less code in main.go (specifically the AST walking logic has shrunk), and it makes it much easier to build custom vet binaries simply by dropping new source files in the directory. LGTM=josharian, r R=r, josharian, kamil.kisiel CC=golang-codereviews https://golang.org/cl/83400043
107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
// 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"
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
)
|
|
|
|
func init() {
|
|
register("copylocks",
|
|
"check that locks are not passed by value",
|
|
checkCopyLocks,
|
|
funcDecl)
|
|
}
|
|
|
|
// checkCopyLocks checks whether a function might
|
|
// inadvertently copy a lock, by checking whether
|
|
// its receiver, parameters, or return values
|
|
// are locks.
|
|
func checkCopyLocks(f *File, node ast.Node) {
|
|
d := node.(*ast.FuncDecl)
|
|
|
|
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
expr := d.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", d.Name.Name, path)
|
|
}
|
|
}
|
|
|
|
if d.Type.Params != nil {
|
|
for _, field := range d.Type.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", d.Name.Name, path)
|
|
}
|
|
}
|
|
}
|
|
|
|
if d.Type.Results != nil {
|
|
for _, field := range d.Type.Results.List {
|
|
expr := field.Type
|
|
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
|
f.Badf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type typePath []types.Type
|
|
|
|
// pathString 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()
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 reference to this type
|
|
// can be locked, but a value cannot. This differentiates
|
|
// embedded interfaces from embedded values.
|
|
if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil {
|
|
if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil {
|
|
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
|
|
}
|