aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/functions/functions.go
diff options
context:
space:
mode:
authorJoseph Richey <joerichey94@gmail.com>2018-02-11 20:34:07 -0800
committerJoseph Richey <joerichey94@gmail.com>2018-02-11 20:34:07 -0800
commit23b8c7b4eab0375b3d59cf4b2a1f3d7356515f95 (patch)
tree6ec67f8d6b15420c3870d2b7c43b2b636fbb8349 /vendor/honnef.co/go/tools/functions/functions.go
parentfff13ea9041a3945e36d5f002c3c0a1e0e93c825 (diff)
vendor: include source for tools
This change vendors the source for all our build, formatting, and linting tools. Generated by running "dep ensure".
Diffstat (limited to 'vendor/honnef.co/go/tools/functions/functions.go')
-rw-r--r--vendor/honnef.co/go/tools/functions/functions.go150
1 files changed, 150 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/functions/functions.go b/vendor/honnef.co/go/tools/functions/functions.go
new file mode 100644
index 0000000..c5fe2d7
--- /dev/null
+++ b/vendor/honnef.co/go/tools/functions/functions.go
@@ -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": Description{Pure: true},
+
+ "fmt.Errorf": Description{Pure: true},
+ "fmt.Sprintf": Description{Pure: true},
+ "fmt.Sprint": Description{Pure: true},
+
+ "sort.Reverse": Description{Pure: true},
+
+ "strings.Map": Description{Pure: true},
+ "strings.Repeat": Description{Pure: true},
+ "strings.Replace": Description{Pure: true},
+ "strings.Title": Description{Pure: true},
+ "strings.ToLower": Description{Pure: true},
+ "strings.ToLowerSpecial": Description{Pure: true},
+ "strings.ToTitle": Description{Pure: true},
+ "strings.ToTitleSpecial": Description{Pure: true},
+ "strings.ToUpper": Description{Pure: true},
+ "strings.ToUpperSpecial": Description{Pure: true},
+ "strings.Trim": Description{Pure: true},
+ "strings.TrimFunc": Description{Pure: true},
+ "strings.TrimLeft": Description{Pure: true},
+ "strings.TrimLeftFunc": Description{Pure: true},
+ "strings.TrimPrefix": Description{Pure: true},
+ "strings.TrimRight": Description{Pure: true},
+ "strings.TrimRightFunc": Description{Pure: true},
+ "strings.TrimSpace": Description{Pure: true},
+ "strings.TrimSuffix": Description{Pure: true},
+
+ "(*net/http.Request).WithContext": Description{Pure: true},
+
+ "math/rand.Read": Description{NilError: true},
+ "(*math/rand.Rand).Read": Description{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
+}