Add staticcheck tool

This commit is contained in:
dwrz
2026-06-06 01:17:20 +00:00
parent 6fb63c8c90
commit ad58cd78ff
315 changed files with 56337 additions and 4 deletions

View File

@@ -0,0 +1,149 @@
package unused
import (
"go/types"
)
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) {
if name != "_" {
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
if sameId(m, pkg, name) {
return i, m
}
}
}
return -1, nil
}
func sameId(obj types.Object, pkg *types.Package, name string) bool {
// spec:
// "Two identifiers are different if they are spelled differently,
// or if they appear in different packages and are not exported.
// Otherwise, they are the same."
if name != obj.Name() {
return false
}
// obj.Name == name
if obj.Exported() {
return true
}
// not exported, so packages must be the same (pkg == nil for
// fields in Universe scope; this can only happen for types
// introduced via Eval)
if pkg == nil || obj.Pkg() == nil {
return pkg == obj.Pkg()
}
// pkg != nil && obj.pkg != nil
return pkg.Path() == obj.Pkg().Path()
}
func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {
// fast path for common case
if T.Empty() {
return nil, true
}
if ityp, _ := V.Underlying().(*types.Interface); ityp != nil {
// TODO(dh): is this code reachable?
for m := range T.Methods() {
_, obj := lookupMethod(ityp, m.Pkg(), m.Name())
switch {
case obj == nil:
return nil, false
case !types.Identical(obj.Type(), m.Type()):
return nil, false
}
}
return nil, true
}
// A concrete type implements T if it implements all methods of T.
var sels []*types.Selection
var c methodsChecker
for m := range T.Methods() {
sel := msV.Lookup(m.Pkg(), m.Name())
if sel == nil {
return nil, false
}
f, _ := sel.Obj().(*types.Func)
if f == nil {
return nil, false
}
if !c.methodIsCompatible(f, m) {
return nil, false
}
sels = append(sels, sel)
}
return sels, true
}
type methodsChecker struct {
typeParams map[*types.TypeParam]types.Type
}
// Currently, this doesn't support methods like `foo(x []T)`.
func (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool {
if types.Identical(implFunc.Type(), interfaceFunc.Type()) {
return true
}
implSig, implOk := implFunc.Type().(*types.Signature)
interfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature)
if !implOk || !interfaceOk {
// probably not reachable. handle conservatively.
return false
}
if !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) {
return false
}
if !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) {
return false
}
return true
}
func (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool {
if implTypes.Len() != interfaceTypes.Len() {
return false
}
for i := 0; i < implTypes.Len(); i++ {
if !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) {
return false
}
}
return true
}
func (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool {
if types.Identical(implType, interfaceType) {
return true
}
// We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet.
tp, ok := interfaceType.(*types.TypeParam)
if !ok {
return false
}
if c.typeParams == nil {
c.typeParams = make(map[*types.TypeParam]types.Type)
}
if c.typeParams[tp] == nil {
if !satisfiesConstraint(implType, tp) {
return false
}
c.typeParams[tp] = implType
return true
}
return types.Identical(c.typeParams[tp], implType)
}
func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool {
bound := tp.Constraint().Underlying().(*types.Interface)
return types.Satisfies(t, bound)
}

View File

@@ -0,0 +1,331 @@
package unused
// Functions defined in the Go runtime that may be called through
// compiler magic or via assembly.
var runtimeFuncs = map[string]bool{
// Copied from cmd/compile/internal/typecheck/builtin.go, var runtimeDecls
"newobject": true,
"panicindex": true,
"panicslice": true,
"panicdivide": true,
"panicmakeslicelen": true,
"throwinit": true,
"panicwrap": true,
"gopanic": true,
"gorecover": true,
"goschedguarded": true,
"printbool": true,
"printfloat": true,
"printint": true,
"printhex": true,
"printuint": true,
"printcomplex": true,
"printstring": true,
"printpointer": true,
"printiface": true,
"printeface": true,
"printslice": true,
"printnl": true,
"printsp": true,
"printlock": true,
"printunlock": true,
"concatstring2": true,
"concatstring3": true,
"concatstring4": true,
"concatstring5": true,
"concatstrings": true,
"cmpstring": true,
"intstring": true,
"slicebytetostring": true,
"slicebytetostringtmp": true,
"slicerunetostring": true,
"stringtoslicebyte": true,
"stringtoslicerune": true,
"slicecopy": true,
"slicestringcopy": true,
"decoderune": true,
"countrunes": true,
"convI2I": true,
"convT16": true,
"convT32": true,
"convT64": true,
"convTstring": true,
"convTslice": true,
"convT2E": true,
"convT2Enoptr": true,
"convT2I": true,
"convT2Inoptr": true,
"assertE2I": true,
"assertE2I2": true,
"assertI2I": true,
"assertI2I2": true,
"panicdottypeE": true,
"panicdottypeI": true,
"panicnildottype": true,
"ifaceeq": true,
"efaceeq": true,
"fastrand": true,
"makemap64": true,
"makemap": true,
"makemap_small": true,
"mapaccess1": true,
"mapaccess1_fast32": true,
"mapaccess1_fast64": true,
"mapaccess1_faststr": true,
"mapaccess1_fat": true,
"mapaccess2": true,
"mapaccess2_fast32": true,
"mapaccess2_fast64": true,
"mapaccess2_faststr": true,
"mapaccess2_fat": true,
"mapassign": true,
"mapassign_fast32": true,
"mapassign_fast32ptr": true,
"mapassign_fast64": true,
"mapassign_fast64ptr": true,
"mapassign_faststr": true,
"mapiterinit": true,
"mapdelete": true,
"mapdelete_fast32": true,
"mapdelete_fast64": true,
"mapdelete_faststr": true,
"mapiternext": true,
"mapclear": true,
"makechan64": true,
"makechan": true,
"chanrecv1": true,
"chanrecv2": true,
"chansend1": true,
"closechan": true,
"writeBarrier": true,
"typedmemmove": true,
"typedmemclr": true,
"typedslicecopy": true,
"selectnbsend": true,
"selectnbrecv": true,
"selectnbrecv2": true,
"selectsetpc": true,
"selectgo": true,
"block": true,
"makeslice": true,
"makeslice64": true,
"growslice": true,
"memmove": true,
"memclrNoHeapPointers": true,
"memclrHasPointers": true,
"memequal": true,
"memequal8": true,
"memequal16": true,
"memequal32": true,
"memequal64": true,
"memequal128": true,
"int64div": true,
"uint64div": true,
"int64mod": true,
"uint64mod": true,
"float64toint64": true,
"float64touint64": true,
"float64touint32": true,
"int64tofloat64": true,
"uint64tofloat64": true,
"uint32tofloat64": true,
"complex128div": true,
"racefuncenter": true,
"racefuncenterfp": true,
"racefuncexit": true,
"raceread": true,
"racewrite": true,
"racereadrange": true,
"racewriterange": true,
"msanread": true,
"msanwrite": true,
"x86HasPOPCNT": true,
"x86HasSSE41": true,
"arm64HasATOMICS": true,
"mallocgc": true,
"panicshift": true,
"panicmakeslicecap": true,
"goPanicIndex": true,
"goPanicIndexU": true,
"goPanicSliceAlen": true,
"goPanicSliceAlenU": true,
"goPanicSliceAcap": true,
"goPanicSliceAcapU": true,
"goPanicSliceB": true,
"goPanicSliceBU": true,
"goPanicSlice3Alen": true,
"goPanicSlice3AlenU": true,
"goPanicSlice3Acap": true,
"goPanicSlice3AcapU": true,
"goPanicSlice3B": true,
"goPanicSlice3BU": true,
"goPanicSlice3C": true,
"goPanicSlice3CU": true,
"goPanicSliceConvert": true,
"printuintptr": true,
"convT": true,
"convTnoptr": true,
"makeslicecopy": true,
"unsafeslicecheckptr": true,
"panicunsafeslicelen": true,
"panicunsafeslicenilptr": true,
"unsafestringcheckptr": true,
"panicunsafestringlen": true,
"panicunsafestringnilptr": true,
"mulUintptr": true,
"memequal0": true,
"f32equal": true,
"f64equal": true,
"c64equal": true,
"c128equal": true,
"strequal": true,
"interequal": true,
"nilinterequal": true,
"memhash": true,
"memhash0": true,
"memhash8": true,
"memhash16": true,
"memhash32": true,
"memhash64": true,
"memhash128": true,
"f32hash": true,
"f64hash": true,
"c64hash": true,
"c128hash": true,
"strhash": true,
"interhash": true,
"nilinterhash": true,
"int64tofloat32": true,
"uint64tofloat32": true,
"getcallerpc": true,
"getcallersp": true,
"msanmove": true,
"asanread": true,
"asanwrite": true,
"checkptrAlignment": true,
"checkptrArithmetic": true,
"libfuzzerTraceCmp1": true,
"libfuzzerTraceCmp2": true,
"libfuzzerTraceCmp4": true,
"libfuzzerTraceCmp8": true,
"libfuzzerTraceConstCmp1": true,
"libfuzzerTraceConstCmp2": true,
"libfuzzerTraceConstCmp4": true,
"libfuzzerTraceConstCmp8": true,
"libfuzzerHookStrCmp": true,
"libfuzzerHookEqualFold": true,
"addCovMeta": true,
"x86HasFMA": true,
"armHasVFPv4": true,
// Extracted from assembly code in the standard library, with the exception of the runtime package itself
"abort": true,
"aeshashbody": true,
"args": true,
"asminit": true,
"badctxt": true,
"badmcall2": true,
"badmcall": true,
"badmorestackg0": true,
"badmorestackgsignal": true,
"badsignal2": true,
"callbackasm1": true,
"callCfunction": true,
"cgocallback_gofunc": true,
"cgocallbackg": true,
"checkgoarm": true,
"check": true,
"debugCallCheck": true,
"debugCallWrap": true,
"emptyfunc": true,
"entersyscall": true,
"exit": true,
"exits": true,
"exitsyscall": true,
"externalthreadhandler": true,
"findnull": true,
"goexit1": true,
"gostring": true,
"i386_set_ldt": true,
"_initcgo": true,
"init_thread_tls": true,
"ldt0setup": true,
"libpreinit": true,
"load_g": true,
"morestack": true,
"mstart": true,
"nacl_sysinfo": true,
"nanotimeQPC": true,
"nanotime": true,
"newosproc0": true,
"newproc": true,
"newstack": true,
"noted": true,
"nowQPC": true,
"osinit": true,
"printf": true,
"racecallback": true,
"reflectcallmove": true,
"reginit": true,
"rt0_go": true,
"save_g": true,
"schedinit": true,
"setldt": true,
"settls": true,
"sighandler": true,
"sigprofNonGo": true,
"sigtrampgo": true,
"_sigtramp": true,
"sigtramp": true,
"stackcheck": true,
"syscall_chdir": true,
"syscall_chroot": true,
"syscall_close": true,
"syscall_dup2": true,
"syscall_execve": true,
"syscall_exit": true,
"syscall_fcntl": true,
"syscall_forkx": true,
"syscall_gethostname": true,
"syscall_getpid": true,
"syscall_ioctl": true,
"syscall_pipe": true,
"syscall_rawsyscall6": true,
"syscall_rawSyscall6": true,
"syscall_rawsyscall": true,
"syscall_RawSyscall": true,
"syscall_rawsysvicall6": true,
"syscall_setgid": true,
"syscall_setgroups": true,
"syscall_setpgid": true,
"syscall_setsid": true,
"syscall_setuid": true,
"syscall_syscall6": true,
"syscall_syscall": true,
"syscall_Syscall": true,
"syscall_sysvicall6": true,
"syscall_wait4": true,
"syscall_write": true,
"traceback": true,
"tstart": true,
"usplitR0": true,
"wbBufFlush": true,
"write": true,
// Other runtime functions that can get called in non-standard ways
"bgsweep": true,
"memhash_varlen": true,
"strhashFallback": true,
"asanregisterglobals": true,
"cgoUse": true,
"cgoCheckPointer": true,
"cgoCheckResult": true,
"_cgo_panic_internal": true,
"addExitHook": true,
}
var runtimeCoverageFuncs = map[string]bool{
"initHook": true,
"markProfileEmitted": true,
"processCoverTestDir": true,
}

View File

@@ -0,0 +1,99 @@
package unused
import (
"fmt"
"go/token"
"os"
"golang.org/x/tools/go/types/objectpath"
)
type ObjectPath struct {
PkgPath string
ObjPath objectpath.Path
}
// XXX make sure that node 0 always exists and is always the root
type SerializedGraph struct {
nodes []Node
nodesByPath map[ObjectPath]NodeID
// XXX deduplicating on position is dubious for `switch x := foo.(type)`, where x will be declared many times for
// the different types, but all at the same position. On the other hand, merging these nodes is probably fine.
nodesByPosition map[token.Position]NodeID
}
func trace(f string, args ...any) {
fmt.Fprintf(os.Stderr, f, args...)
fmt.Fprintln(os.Stderr)
}
func (g *SerializedGraph) Merge(nodes []Node) {
if g.nodesByPath == nil {
g.nodesByPath = map[ObjectPath]NodeID{}
}
if g.nodesByPosition == nil {
g.nodesByPosition = map[token.Position]NodeID{}
}
if len(g.nodes) == 0 {
// Seed nodes with a root node
g.nodes = append(g.nodes, Node{})
}
// OPT(dh): reuse storage between calls to Merge
remapping := make([]NodeID, len(nodes))
// First pass: compute remapping of IDs of to-be-merged nodes
for _, n := range nodes {
// XXX Column is never 0. it's 1 if there is no column information in the export data. which sucks, because
// objects can also genuinely be in column 1.
if n.id != 0 && n.obj.Path == (ObjectPath{}) && n.obj.Position.Column == 0 {
// If the object has no path, then it couldn't have come from export data, which means it needs to have full
// position information including a column.
panic(fmt.Sprintf("object %q has no path but also no column information", n.obj.Name))
}
if orig, ok := g.nodesByPath[n.obj.Path]; ok {
// We already have a node for this object
trace("deduplicating %d -> %d based on path %s", n.id, orig, n.obj.Path)
remapping[n.id] = orig
} else if orig, ok := g.nodesByPosition[n.obj.Position]; ok && n.obj.Position.Column != 0 {
// We already have a node for this object
trace("deduplicating %d -> %d based on position %s", n.id, orig, n.obj.Position)
remapping[n.id] = orig
} else {
// This object is new to us; change ID to avoid collision
newID := NodeID(len(g.nodes))
trace("new node, remapping %d -> %d", n.id, newID)
remapping[n.id] = newID
g.nodes = append(g.nodes, Node{
id: newID,
obj: n.obj,
uses: make([]NodeID, 0, len(n.uses)),
owns: make([]NodeID, 0, len(n.owns)),
})
if n.id == 0 {
// Our root uses all the roots of the subgraphs
g.nodes[0].uses = append(g.nodes[0].uses, newID)
}
if n.obj.Path != (ObjectPath{}) {
g.nodesByPath[n.obj.Path] = newID
}
if n.obj.Position.Column != 0 {
g.nodesByPosition[n.obj.Position] = newID
}
}
}
// Second step: apply remapping
for _, n := range nodes {
n.id = remapping[n.id]
for i := range n.uses {
n.uses[i] = remapping[n.uses[i]]
}
for i := range n.owns {
n.owns[i] = remapping[n.owns[i]]
}
g.nodes[n.id].uses = append(g.nodes[n.id].uses, n.uses...)
g.nodes[n.id].owns = append(g.nodes[n.id].owns, n.owns...)
}
}

1725
vendor/honnef.co/go/tools/unused/unused.go vendored Normal file

File diff suppressed because it is too large Load Diff