1
0

Update module spf13/viper to v1.7.0 (#494)

Update module spf13/viper to v1.7.0

Reviewed-on: https://kolaente.dev/vikunja/api/pulls/494
This commit is contained in:
renovate
2020-05-09 13:44:17 +00:00
committed by konrad
parent 18f6e31b54
commit def2362682
89 changed files with 9018 additions and 4502 deletions

View File

@ -1,56 +0,0 @@
package functions
import (
"go/token"
"go/types"
"honnef.co/go/tools/ssa"
)
func concreteReturnTypes(fn *ssa.Function) []*types.Tuple {
res := fn.Signature.Results()
if res == nil {
return nil
}
ifaces := make([]bool, res.Len())
any := false
for i := 0; i < res.Len(); i++ {
_, ifaces[i] = res.At(i).Type().Underlying().(*types.Interface)
any = any || ifaces[i]
}
if !any {
return []*types.Tuple{res}
}
var out []*types.Tuple
for _, block := range fn.Blocks {
if len(block.Instrs) == 0 {
continue
}
ret, ok := block.Instrs[len(block.Instrs)-1].(*ssa.Return)
if !ok {
continue
}
vars := make([]*types.Var, res.Len())
for i, v := range ret.Results {
var typ types.Type
if !ifaces[i] {
typ = res.At(i).Type()
} else if mi, ok := v.(*ssa.MakeInterface); ok {
// TODO(dh): if mi.X is a function call that returns
// an interface, call concreteReturnTypes on that
// function (or, really, go through Descriptions,
// avoid infinite recursion etc, just like nil error
// detection)
// TODO(dh): support Phi nodes
typ = mi.X.Type()
} else {
typ = res.At(i).Type()
}
vars[i] = types.NewParam(token.NoPos, nil, "", typ)
}
out = append(out, types.NewTuple(vars...))
}
// TODO(dh): deduplicate out
return out
}

View File

@ -1,150 +0,0 @@
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
}

View File

@ -2,9 +2,9 @@ package functions
import "honnef.co/go/tools/ssa"
type Loop map[*ssa.BasicBlock]bool
type Loop struct{ ssa.BlockSet }
func findLoops(fn *ssa.Function) []Loop {
func FindLoops(fn *ssa.Function) []Loop {
if fn.Blocks == nil {
return nil
}
@ -18,12 +18,16 @@ func findLoops(fn *ssa.Function) []Loop {
// n is a back-edge to h
// h is the loop header
if n == h {
sets = append(sets, Loop{n: true})
set := Loop{}
set.Add(n)
sets = append(sets, set)
continue
}
set := Loop{h: true, n: true}
set := Loop{}
set.Add(h)
set.Add(n)
for _, b := range allPredsBut(n, h, nil) {
set[b] = true
set.Add(b)
}
sets = append(sets, set)
}

View File

@ -1,26 +1,31 @@
package functions
import (
"go/token"
"go/types"
"honnef.co/go/tools/callgraph"
"honnef.co/go/tools/lint/lintdsl"
"honnef.co/go/tools/ssa"
)
func filterDebug(instr []ssa.Instruction) []ssa.Instruction {
var out []ssa.Instruction
for _, ins := range instr {
if _, ok := ins.(*ssa.DebugRef); !ok {
out = append(out, ins)
}
}
return out
}
// IsStub reports whether a function is a stub. A function is
// considered a stub if it has no instructions or exactly one
// instruction, which must be either returning only constant values or
// a panic.
func (d *Descriptions) IsStub(fn *ssa.Function) bool {
func IsStub(fn *ssa.Function) bool {
if len(fn.Blocks) == 0 {
return true
}
if len(fn.Blocks) > 1 {
return false
}
instrs := lintdsl.FilterDebug(fn.Blocks[0].Instrs)
instrs := filterDebug(fn.Blocks[0].Instrs)
if len(instrs) != 1 {
return false
}
@ -39,85 +44,3 @@ func (d *Descriptions) IsStub(fn *ssa.Function) bool {
return false
}
}
func (d *Descriptions) IsPure(fn *ssa.Function) bool {
if fn.Signature.Results().Len() == 0 {
// A function with no return values is empty or is doing some
// work we cannot see (for example because of build tags);
// don't consider it pure.
return false
}
for _, param := range fn.Params {
if _, ok := param.Type().Underlying().(*types.Basic); !ok {
return false
}
}
if fn.Blocks == nil {
return false
}
checkCall := func(common *ssa.CallCommon) bool {
if common.IsInvoke() {
return false
}
builtin, ok := common.Value.(*ssa.Builtin)
if !ok {
if common.StaticCallee() != fn {
if common.StaticCallee() == nil {
return false
}
// TODO(dh): ideally, IsPure wouldn't be responsible
// for avoiding infinite recursion, but
// FunctionDescriptions would be.
node := d.CallGraph.CreateNode(common.StaticCallee())
if callgraph.PathSearch(node, func(other *callgraph.Node) bool {
return other.Func == fn
}) != nil {
return false
}
if !d.Get(common.StaticCallee()).Pure {
return false
}
}
} else {
switch builtin.Name() {
case "len", "cap", "make", "new":
default:
return false
}
}
return true
}
for _, b := range fn.Blocks {
for _, ins := range b.Instrs {
switch ins := ins.(type) {
case *ssa.Call:
if !checkCall(ins.Common()) {
return false
}
case *ssa.Defer:
if !checkCall(&ins.Call) {
return false
}
case *ssa.Select:
return false
case *ssa.Send:
return false
case *ssa.Go:
return false
case *ssa.Panic:
return false
case *ssa.Store:
return false
case *ssa.FieldAddr:
return false
case *ssa.UnOp:
if ins.Op == token.MUL || ins.Op == token.AND {
return false
}
}
}
}
return true
}

View File

@ -2,10 +2,10 @@ package functions
import "honnef.co/go/tools/ssa"
// terminates reports whether fn is supposed to return, that is if it
// Terminates reports whether fn is supposed to return, that is if it
// has at least one theoretic path that returns from the function.
// Explicit panics do not count as terminating.
func terminates(fn *ssa.Function) bool {
func Terminates(fn *ssa.Function) bool {
if fn.Blocks == nil {
// assuming that a function terminates is the conservative
// choice