1
0

Update and fix staticcheck

This commit is contained in:
kolaente
2020-05-29 22:15:21 +02:00
parent aae1bc3cab
commit a525787ab7
100 changed files with 12353 additions and 7912 deletions

View File

@ -11,11 +11,11 @@ import (
"sync/atomic"
"golang.org/x/tools/go/analysis"
"honnef.co/go/tools/code"
"honnef.co/go/tools/go/types/typeutil"
"honnef.co/go/tools/internal/passes/buildssa"
"honnef.co/go/tools/internal/passes/buildir"
"honnef.co/go/tools/ir"
"honnef.co/go/tools/lint"
"honnef.co/go/tools/lint/lintdsl"
"honnef.co/go/tools/ssa"
)
// The graph we construct omits nodes along a path that do not
@ -103,7 +103,7 @@ import (
from method to type.
- (8.3) All interface methods are marked as used, even if they never get
called. This is to accomodate sum types (unexported interface
called. This is to accommodate sum types (unexported interface
method that must exist but never gets called.)
- (8.4) All embedded interfaces are marked as used. This is an
@ -419,8 +419,8 @@ type pkg struct {
Pkg *types.Package
TypesInfo *types.Info
TypesSizes types.Sizes
SSA *ssa.Package
SrcFuncs []*ssa.Function
IR *ir.Package
SrcFuncs []*ir.Function
}
type Checker struct {
@ -450,7 +450,7 @@ func (c *Checker) Analyzer() *analysis.Analyzer {
Name: name,
Doc: "Unused code",
Run: c.Run,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Requires: []*analysis.Analyzer{buildir.Analyzer},
}
}
@ -477,15 +477,15 @@ func (c *Checker) Run(pass *analysis.Pass) (interface{}, error) {
c.initialPackages[pass.Pkg] = struct{}{}
c.mu.Unlock()
ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR)
pkg := &pkg{
Fset: pass.Fset,
Files: pass.Files,
Pkg: pass.Pkg,
TypesInfo: pass.TypesInfo,
TypesSizes: pass.TypesSizes,
SSA: ssapkg.Pkg,
SrcFuncs: ssapkg.SrcFuncs,
IR: irpkg.Pkg,
SrcFuncs: irpkg.SrcFuncs,
}
c.processPkg(c.graph, pkg)
@ -638,10 +638,9 @@ func (c *Checker) results() []types.Object {
c.debugf("digraph{\n")
debugNode(c.graph.Root)
c.graph.Nodes.Range(func(k, v interface{}) bool {
debugNode(v.(*Node))
return true
})
for _, v := range c.graph.Nodes {
debugNode(v)
}
c.graph.TypeNodes.Iterate(func(key types.Type, value interface{}) {
debugNode(value.(*Node))
})
@ -655,10 +654,9 @@ func (c *Checker) results() []types.Object {
// don't flag its receiver. if a named type is unused, don't
// flag its methods.
c.graph.Nodes.Range(func(k, v interface{}) bool {
c.graph.quieten(v.(*Node))
return true
})
for _, v := range c.graph.Nodes {
c.graph.quieten(v)
}
c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) {
c.graph.quieten(value.(*Node))
})
@ -688,10 +686,9 @@ func (c *Checker) results() []types.Object {
}
c.debugf("n%d [color=gray];\n", node.id)
}
c.graph.Nodes.Range(func(k, v interface{}) bool {
report(v.(*Node))
return true
})
for _, v := range c.graph.Nodes {
report(v)
}
c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) {
report(value.(*Node))
})
@ -779,8 +776,6 @@ type Graph struct {
fset *token.FileSet
Root *Node
seenTypes typeutil.Map
Nodes sync.Map // map[interface{}]*Node
objNodes sync.Map // map[objNodeKey]*Node
// read-only
wholeProgram bool
@ -788,6 +783,8 @@ type Graph struct {
// need synchronisation
mu sync.Mutex
TypeNodes typeutil.Map
Nodes map[interface{}]*Node
objNodes map[objNodeKey]*Node
}
type context struct {
@ -796,13 +793,13 @@ type context struct {
seenFns map[string]struct{}
seenTypes *typeutil.Map
nodeCounter uint64
// local cache for the map in Graph
typeNodes typeutil.Map
}
func NewGraph() *Graph {
g := &Graph{}
g := &Graph{
Nodes: map[interface{}]*Node{},
objNodes: map[objNodeKey]*Node{},
}
g.Root = g.newNode(&context{}, nil)
return g
}
@ -844,49 +841,48 @@ type Node struct {
}
func (g *Graph) nodeMaybe(obj types.Object) (*Node, bool) {
if node, ok := g.Nodes.Load(obj); ok {
return node.(*Node), true
g.mu.Lock()
defer g.mu.Unlock()
if node, ok := g.Nodes[obj]; ok {
return node, true
}
return nil, false
}
func (g *Graph) node(ctx *context, obj interface{}) (node *Node, new bool) {
if t, ok := obj.(types.Type); ok {
if v := ctx.typeNodes.At(t); v != nil {
g.mu.Lock()
defer g.mu.Unlock()
switch obj := obj.(type) {
case types.Type:
if v := g.TypeNodes.At(obj); v != nil {
return v.(*Node), false
}
g.mu.Lock()
defer g.mu.Unlock()
if v := g.TypeNodes.At(t); v != nil {
return v.(*Node), false
}
node := g.newNode(ctx, t)
g.TypeNodes.Set(t, node)
ctx.typeNodes.Set(t, node)
node := g.newNode(ctx, obj)
g.TypeNodes.Set(obj, node)
return node, true
}
case types.Object:
if node, ok := g.Nodes[obj]; ok {
return node, false
}
if node, ok := g.Nodes.Load(obj); ok {
return node.(*Node), false
}
if obj, ok := obj.(types.Object); ok {
key := objNodeKeyFor(g.fset, obj)
if o, ok := g.objNodes.Load(key); ok {
onode := o.(*Node)
if onode, ok := g.objNodes[key]; ok {
return onode, false
}
node = g.newNode(ctx, obj)
g.Nodes.Store(obj, node)
g.objNodes.Store(key, node)
g.Nodes[obj] = node
g.objNodes[key] = node
return node, true
default:
if node, ok := g.Nodes[obj]; ok {
return node, false
}
node = g.newNode(ctx, obj)
g.Nodes[obj] = node
return node, true
}
node = g.newNode(ctx, obj)
g.Nodes.Store(obj, node)
return node, true
}
func (g *Graph) newNode(ctx *context, obj interface{}) *Node {
@ -1067,7 +1063,7 @@ func (g *Graph) entry(pkg *pkg) {
ctx.seenTypes = &typeutil.Map{}
}
scopes := map[*types.Scope]*ssa.Function{}
scopes := map[*types.Scope]*ir.Function{}
for _, fn := range pkg.SrcFuncs {
if fn.Object() != nil {
scope := fn.Object().(*types.Func).Scope()
@ -1087,12 +1083,12 @@ func (g *Graph) entry(pkg *pkg) {
// (1.8) packages use symbols linked via go:linkname
fields := strings.Fields(c.Text)
if len(fields) == 3 {
if m, ok := pkg.SSA.Members[fields[1]]; ok {
if m, ok := pkg.IR.Members[fields[1]]; ok {
var obj types.Object
switch m := m.(type) {
case *ssa.Global:
case *ir.Global:
obj = m.Object()
case *ssa.Function:
case *ir.Function:
obj = m.Object()
default:
panic(fmt.Sprintf("unhandled type: %T", m))
@ -1106,7 +1102,7 @@ func (g *Graph) entry(pkg *pkg) {
}
}
surroundingFunc := func(obj types.Object) *ssa.Function {
surroundingFunc := func(obj types.Object) *ir.Function {
scope := obj.Parent()
for scope != nil {
if fn := scopes[scope]; fn != nil {
@ -1117,10 +1113,10 @@ func (g *Graph) entry(pkg *pkg) {
return nil
}
// SSA form won't tell us about locally scoped types that aren't
// IR form won't tell us about locally scoped types that aren't
// being used. Walk the list of Defs to get all named types.
//
// SSA form also won't tell us about constants; use Defs and Uses
// IR form also won't tell us about constants; use Defs and Uses
// to determine which constants exist and which are being used.
for _, obj := range pkg.TypesInfo.Defs {
switch obj := obj.(type) {
@ -1143,7 +1139,7 @@ func (g *Graph) entry(pkg *pkg) {
if fn.Object() != nil {
ctx.see(fn.Object())
}
node := fn.Syntax()
node := fn.Source()
if node == nil {
continue
}
@ -1221,7 +1217,7 @@ func (g *Graph) entry(pkg *pkg) {
case *ast.GenDecl:
switch n.Tok {
case token.CONST:
groups := lintdsl.GroupSpecs(pkg.Fset, n.Specs)
groups := code.GroupSpecs(pkg.Fset, n.Specs)
for _, specs := range groups {
if len(specs) > 1 {
cg := &ConstGroup{}
@ -1293,11 +1289,11 @@ func (g *Graph) entry(pkg *pkg) {
})
}
for _, m := range pkg.SSA.Members {
for _, m := range pkg.IR.Members {
switch m := m.(type) {
case *ssa.NamedConst:
case *ir.NamedConst:
// nothing to do, we collect all constants from Defs
case *ssa.Global:
case *ir.Global:
if m.Object() != nil {
ctx.see(m.Object())
if g.trackExportedIdentifier(ctx, m.Object()) {
@ -1305,7 +1301,7 @@ func (g *Graph) entry(pkg *pkg) {
ctx.use(m.Object(), nil, edgeExportedVariable)
}
}
case *ssa.Function:
case *ir.Function:
mObj := owningObject(m)
if mObj != nil {
ctx.see(mObj)
@ -1331,8 +1327,8 @@ func (g *Graph) entry(pkg *pkg) {
// (9.8) runtime functions that may be called from user code via the compiler
ctx.use(mObj, nil, edgeRuntimeFunction)
}
if m.Syntax() != nil {
doc := m.Syntax().(*ast.FuncDecl).Doc
if m.Source() != nil {
doc := m.Source().(*ast.FuncDecl).Doc
if doc != nil {
for _, cmt := range doc.List {
if strings.HasPrefix(cmt.Text, "//go:cgo_export_") {
@ -1343,7 +1339,7 @@ func (g *Graph) entry(pkg *pkg) {
}
}
g.function(ctx, m)
case *ssa.Type:
case *ir.Type:
if m.Object() != nil {
ctx.see(m.Object())
if g.trackExportedIdentifier(ctx, m.Object()) {
@ -1383,7 +1379,7 @@ func (g *Graph) entry(pkg *pkg) {
// (8.0) handle interfaces
for _, t := range notIfaces {
ms := pkg.SSA.Prog.MethodSets.MethodSet(t)
ms := pkg.IR.Prog.MethodSets.MethodSet(t)
for _, iface := range ifaces {
if sels, ok := g.implements(t, iface, ms); ok {
for _, sel := range sels {
@ -1400,19 +1396,19 @@ func (g *Graph) useMethod(ctx *context, t types.Type, sel *types.Selection, by i
path := sel.Index()
assert(obj != nil)
if len(path) > 1 {
base := lintdsl.Dereference(t).Underlying().(*types.Struct)
base := code.Dereference(t).Underlying().(*types.Struct)
for _, idx := range path[:len(path)-1] {
next := base.Field(idx)
// (6.3) structs use embedded fields that help implement interfaces
ctx.see(base)
ctx.seeAndUse(next, base, edgeProvidesMethod)
base, _ = lintdsl.Dereference(next.Type()).Underlying().(*types.Struct)
base, _ = code.Dereference(next.Type()).Underlying().(*types.Struct)
}
}
ctx.seeAndUse(obj, by, kind)
}
func owningObject(fn *ssa.Function) types.Object {
func owningObject(fn *ir.Function) types.Object {
if fn.Object() != nil {
return fn.Object()
}
@ -1422,8 +1418,8 @@ func owningObject(fn *ssa.Function) types.Object {
return nil
}
func (g *Graph) function(ctx *context, fn *ssa.Function) {
if fn.Package() != nil && fn.Package() != ctx.pkg.SSA {
func (g *Graph) function(ctx *context, fn *ir.Function) {
if fn.Package() != nil && fn.Package() != ctx.pkg.IR {
return
}
@ -1503,7 +1499,7 @@ func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) {
// the pointer type to get the full method set
T = types.NewPointer(T)
}
ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(T)
ms := ctx.pkg.IR.Prog.MethodSets.MethodSet(T)
for j := 0; j < ms.Len(); j++ {
if ms.At(j).Obj().Exported() {
// (6.4) structs use embedded fields that have exported methods (recursively)
@ -1516,7 +1512,7 @@ func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) {
seen := map[*types.Struct]struct{}{}
var hasExportedField func(t types.Type) bool
hasExportedField = func(T types.Type) bool {
t, ok := lintdsl.Dereference(T).Underlying().(*types.Struct)
t, ok := code.Dereference(T).Underlying().(*types.Struct)
if !ok {
return false
}
@ -1565,7 +1561,7 @@ func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) {
// (2.1) named types use exported methods
ctx.use(t.Method(i), t, edgeExportedMethod)
}
g.function(ctx, ctx.pkg.SSA.Prog.FuncValue(t.Method(i)))
g.function(ctx, ctx.pkg.IR.Prog.FuncValue(t.Method(i)))
}
g.typ(ctx, t.Underlying(), t)
@ -1646,22 +1642,22 @@ func (g *Graph) signature(ctx *context, sig *types.Signature, fn types.Object) {
}
}
func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
func (g *Graph) instructions(ctx *context, fn *ir.Function) {
fnObj := owningObject(fn)
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
ops := instr.Operands(nil)
switch instr.(type) {
case *ssa.Store:
case *ir.Store:
// (9.7) variable _reads_ use variables, writes do not
ops = ops[1:]
case *ssa.DebugRef:
case *ir.DebugRef:
ops = nil
}
for _, arg := range ops {
walkPhi(*arg, func(v ssa.Value) {
walkPhi(*arg, func(v ir.Value) {
switch v := v.(type) {
case *ssa.Function:
case *ir.Function:
// (4.3) functions use closures and bound methods.
// (4.5) functions use functions they call
// (9.5) instructions use their operands
@ -1670,11 +1666,11 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
ctx.seeAndUse(owningObject(v), fnObj, edgeInstructionOperand)
}
g.function(ctx, v)
case *ssa.Const:
case *ir.Const:
// (9.6) instructions use their operands' types
ctx.seeAndUse(v.Type(), fnObj, edgeType)
g.typ(ctx, v.Type(), nil)
case *ssa.Global:
case *ir.Global:
if v.Object() != nil {
// (9.5) instructions use their operands
ctx.seeAndUse(v.Object(), fnObj, edgeInstructionOperand)
@ -1682,8 +1678,8 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
}
})
}
if v, ok := instr.(ssa.Value); ok {
if _, ok := v.(*ssa.Range); !ok {
if v, ok := instr.(ir.Value); ok {
if _, ok := v.(*ir.Range); !ok {
// See https://github.com/golang/go/issues/19670
// (4.8) instructions use their types
@ -1693,29 +1689,29 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
}
}
switch instr := instr.(type) {
case *ssa.Field:
case *ir.Field:
st := instr.X.Type().Underlying().(*types.Struct)
field := st.Field(instr.Field)
// (4.7) functions use fields they access
ctx.seeAndUse(field, fnObj, edgeFieldAccess)
case *ssa.FieldAddr:
st := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct)
case *ir.FieldAddr:
st := code.Dereference(instr.X.Type()).Underlying().(*types.Struct)
field := st.Field(instr.Field)
// (4.7) functions use fields they access
ctx.seeAndUse(field, fnObj, edgeFieldAccess)
case *ssa.Store:
case *ir.Store:
// nothing to do, handled generically by operands
case *ssa.Call:
case *ir.Call:
c := instr.Common()
if !c.IsInvoke() {
// handled generically as an instruction operand
if g.wholeProgram {
// (e3) special case known reflection-based method callers
switch lintdsl.CallName(c) {
switch code.CallName(c) {
case "net/rpc.Register", "net/rpc.RegisterName", "(*net/rpc.Server).Register", "(*net/rpc.Server).RegisterName":
var arg ssa.Value
switch lintdsl.CallName(c) {
var arg ir.Value
switch code.CallName(c) {
case "net/rpc.Register":
arg = c.Args[0]
case "net/rpc.RegisterName":
@ -1725,10 +1721,10 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
case "(*net/rpc.Server).RegisterName":
arg = c.Args[2]
}
walkPhi(arg, func(v ssa.Value) {
if v, ok := v.(*ssa.MakeInterface); ok {
walkPhi(v.X, func(vv ssa.Value) {
ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(vv.Type())
walkPhi(arg, func(v ir.Value) {
if v, ok := v.(*ir.MakeInterface); ok {
walkPhi(v.X, func(vv ir.Value) {
ms := ctx.pkg.IR.Prog.MethodSets.MethodSet(vv.Type())
for i := 0; i < ms.Len(); i++ {
if ms.At(i).Obj().Exported() {
g.useMethod(ctx, vv.Type(), ms.At(i), fnObj, edgeNetRPCRegister)
@ -1743,13 +1739,13 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
// (4.5) functions use functions/interface methods they call
ctx.seeAndUse(c.Method, fnObj, edgeInterfaceCall)
}
case *ssa.Return:
case *ir.Return:
// nothing to do, handled generically by operands
case *ssa.ChangeType:
case *ir.ChangeType:
// conversion type handled generically
s1, ok1 := lintdsl.Dereference(instr.Type()).Underlying().(*types.Struct)
s2, ok2 := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct)
s1, ok1 := code.Dereference(instr.Type()).Underlying().(*types.Struct)
s2, ok2 := code.Dereference(instr.X.Type()).Underlying().(*types.Struct)
if ok1 && ok2 {
// Converting between two structs. The fields are
// relevant for the conversion, but only if the
@ -1768,13 +1764,13 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
ctx.seeAndUse(s2.Field(i), s1.Field(i), edgeStructConversion)
}
}
case *ssa.MakeInterface:
case *ir.MakeInterface:
// nothing to do, handled generically by operands
case *ssa.Slice:
case *ir.Slice:
// nothing to do, handled generically by operands
case *ssa.RunDefers:
case *ir.RunDefers:
// nothing to do, the deferred functions are already marked use by defering them.
case *ssa.Convert:
case *ir.Convert:
// to unsafe.Pointer
if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
if ptr, ok := instr.X.Type().Underlying().(*types.Pointer); ok {
@ -1797,61 +1793,79 @@ func (g *Graph) instructions(ctx *context, fn *ssa.Function) {
}
}
}
case *ssa.TypeAssert:
case *ir.TypeAssert:
// nothing to do, handled generically by instruction
// type (possibly a tuple, which contains the asserted
// to type). redundantly handled by the type of
// ssa.Extract, too
case *ssa.MakeClosure:
// ir.Extract, too
case *ir.MakeClosure:
// nothing to do, handled generically by operands
case *ssa.Alloc:
case *ir.Alloc:
// nothing to do
case *ssa.UnOp:
case *ir.UnOp:
// nothing to do
case *ssa.BinOp:
case *ir.BinOp:
// nothing to do
case *ssa.If:
case *ir.If:
// nothing to do
case *ssa.Jump:
case *ir.Jump:
// nothing to do
case *ssa.IndexAddr:
case *ir.Unreachable:
// nothing to do
case *ssa.Extract:
case *ir.IndexAddr:
// nothing to do
case *ssa.Panic:
case *ir.Extract:
// nothing to do
case *ssa.DebugRef:
case *ir.Panic:
// nothing to do
case *ssa.BlankStore:
case *ir.DebugRef:
// nothing to do
case *ssa.Phi:
case *ir.BlankStore:
// nothing to do
case *ssa.MakeMap:
case *ir.Phi:
// nothing to do
case *ssa.MapUpdate:
case *ir.Sigma:
// nothing to do
case *ssa.Lookup:
case *ir.MakeMap:
// nothing to do
case *ssa.MakeSlice:
case *ir.MapUpdate:
// nothing to do
case *ssa.Send:
case *ir.MapLookup:
// nothing to do
case *ssa.MakeChan:
case *ir.StringLookup:
// nothing to do
case *ssa.Range:
case *ir.MakeSlice:
// nothing to do
case *ssa.Next:
case *ir.Send:
// nothing to do
case *ssa.Index:
case *ir.MakeChan:
// nothing to do
case *ssa.Select:
case *ir.Range:
// nothing to do
case *ssa.ChangeInterface:
case *ir.Next:
// nothing to do
case *ir.Index:
// nothing to do
case *ir.Select:
// nothing to do
case *ir.ChangeInterface:
// nothing to do
case *ir.Load:
// nothing to do
case *ir.Go:
// nothing to do
case *ir.Defer:
// nothing to do
case *ir.Parameter:
// nothing to do
case *ir.Const:
// nothing to do
case *ir.Recv:
// nothing to do
case *ir.TypeSwitch:
// nothing to do
case *ir.ConstantSwitch:
// nothing to do
case *ssa.Go:
// nothing to do, handled generically by operands
case *ssa.Defer:
// nothing to do, handled generically by operands
default:
panic(fmt.Sprintf("unreachable: %T", instr))
}
@ -1892,22 +1906,22 @@ func isNoCopyType(typ types.Type) bool {
return true
}
func walkPhi(v ssa.Value, fn func(v ssa.Value)) {
phi, ok := v.(*ssa.Phi)
func walkPhi(v ir.Value, fn func(v ir.Value)) {
phi, ok := v.(*ir.Phi)
if !ok {
fn(v)
return
}
seen := map[ssa.Value]struct{}{}
var impl func(v *ssa.Phi)
impl = func(v *ssa.Phi) {
seen := map[ir.Value]struct{}{}
var impl func(v *ir.Phi)
impl = func(v *ir.Phi) {
if _, ok := seen[v]; ok {
return
}
seen[v] = struct{}{}
for _, e := range v.Edges {
if ev, ok := e.(*ssa.Phi); ok {
if ev, ok := e.(*ir.Phi); ok {
impl(ev)
} else {
fn(e)