Add labels to tasks (#45)
This commit is contained in:
150
vendor/honnef.co/go/tools/functions/functions.go
vendored
Normal file
150
vendor/honnef.co/go/tools/functions/functions.go
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
package functions
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync"
|
||||
|
||||
"honnef.co/go/tools/callgraph"
|
||||
"honnef.co/go/tools/callgraph/static"
|
||||
"honnef.co/go/tools/ssa"
|
||||
"honnef.co/go/tools/staticcheck/vrp"
|
||||
)
|
||||
|
||||
var stdlibDescs = map[string]Description{
|
||||
"errors.New": {Pure: true},
|
||||
|
||||
"fmt.Errorf": {Pure: true},
|
||||
"fmt.Sprintf": {Pure: true},
|
||||
"fmt.Sprint": {Pure: true},
|
||||
|
||||
"sort.Reverse": {Pure: true},
|
||||
|
||||
"strings.Map": {Pure: true},
|
||||
"strings.Repeat": {Pure: true},
|
||||
"strings.Replace": {Pure: true},
|
||||
"strings.Title": {Pure: true},
|
||||
"strings.ToLower": {Pure: true},
|
||||
"strings.ToLowerSpecial": {Pure: true},
|
||||
"strings.ToTitle": {Pure: true},
|
||||
"strings.ToTitleSpecial": {Pure: true},
|
||||
"strings.ToUpper": {Pure: true},
|
||||
"strings.ToUpperSpecial": {Pure: true},
|
||||
"strings.Trim": {Pure: true},
|
||||
"strings.TrimFunc": {Pure: true},
|
||||
"strings.TrimLeft": {Pure: true},
|
||||
"strings.TrimLeftFunc": {Pure: true},
|
||||
"strings.TrimPrefix": {Pure: true},
|
||||
"strings.TrimRight": {Pure: true},
|
||||
"strings.TrimRightFunc": {Pure: true},
|
||||
"strings.TrimSpace": {Pure: true},
|
||||
"strings.TrimSuffix": {Pure: true},
|
||||
|
||||
"(*net/http.Request).WithContext": {Pure: true},
|
||||
|
||||
"math/rand.Read": {NilError: true},
|
||||
"(*math/rand.Rand).Read": {NilError: true},
|
||||
}
|
||||
|
||||
type Description struct {
|
||||
// The function is known to be pure
|
||||
Pure bool
|
||||
// The function is known to be a stub
|
||||
Stub bool
|
||||
// The function is known to never return (panics notwithstanding)
|
||||
Infinite bool
|
||||
// Variable ranges
|
||||
Ranges vrp.Ranges
|
||||
Loops []Loop
|
||||
// Function returns an error as its last argument, but it is
|
||||
// always nil
|
||||
NilError bool
|
||||
ConcreteReturnTypes []*types.Tuple
|
||||
}
|
||||
|
||||
type descriptionEntry struct {
|
||||
ready chan struct{}
|
||||
result Description
|
||||
}
|
||||
|
||||
type Descriptions struct {
|
||||
CallGraph *callgraph.Graph
|
||||
mu sync.Mutex
|
||||
cache map[*ssa.Function]*descriptionEntry
|
||||
}
|
||||
|
||||
func NewDescriptions(prog *ssa.Program) *Descriptions {
|
||||
return &Descriptions{
|
||||
CallGraph: static.CallGraph(prog),
|
||||
cache: map[*ssa.Function]*descriptionEntry{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Descriptions) Get(fn *ssa.Function) Description {
|
||||
d.mu.Lock()
|
||||
fd := d.cache[fn]
|
||||
if fd == nil {
|
||||
fd = &descriptionEntry{
|
||||
ready: make(chan struct{}),
|
||||
}
|
||||
d.cache[fn] = fd
|
||||
d.mu.Unlock()
|
||||
|
||||
{
|
||||
fd.result = stdlibDescs[fn.RelString(nil)]
|
||||
fd.result.Pure = fd.result.Pure || d.IsPure(fn)
|
||||
fd.result.Stub = fd.result.Stub || d.IsStub(fn)
|
||||
fd.result.Infinite = fd.result.Infinite || !terminates(fn)
|
||||
fd.result.Ranges = vrp.BuildGraph(fn).Solve()
|
||||
fd.result.Loops = findLoops(fn)
|
||||
fd.result.NilError = fd.result.NilError || IsNilError(fn)
|
||||
fd.result.ConcreteReturnTypes = concreteReturnTypes(fn)
|
||||
}
|
||||
|
||||
close(fd.ready)
|
||||
} else {
|
||||
d.mu.Unlock()
|
||||
<-fd.ready
|
||||
}
|
||||
return fd.result
|
||||
}
|
||||
|
||||
func IsNilError(fn *ssa.Function) bool {
|
||||
// TODO(dh): This is very simplistic, as we only look for constant
|
||||
// nil returns. A more advanced approach would work transitively.
|
||||
// An even more advanced approach would be context-aware and
|
||||
// determine nil errors based on inputs (e.g. io.WriteString to a
|
||||
// bytes.Buffer will always return nil, but an io.WriteString to
|
||||
// an os.File might not). Similarly, an os.File opened for reading
|
||||
// won't error on Close, but other files will.
|
||||
res := fn.Signature.Results()
|
||||
if res.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
last := res.At(res.Len() - 1)
|
||||
if types.TypeString(last.Type(), nil) != "error" {
|
||||
return false
|
||||
}
|
||||
|
||||
if fn.Blocks == nil {
|
||||
return false
|
||||
}
|
||||
for _, block := range fn.Blocks {
|
||||
if len(block.Instrs) == 0 {
|
||||
continue
|
||||
}
|
||||
ins := block.Instrs[len(block.Instrs)-1]
|
||||
ret, ok := ins.(*ssa.Return)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
v := ret.Results[len(ret.Results)-1]
|
||||
c, ok := v.(*ssa.Const)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !c.IsNil() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
Reference in New Issue
Block a user