aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/lint
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/lint
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/lint')
-rw-r--r--vendor/honnef.co/go/tools/lint/LICENSE28
-rw-r--r--vendor/honnef.co/go/tools/lint/lint.go844
-rw-r--r--vendor/honnef.co/go/tools/lint/lintutil/util.go349
3 files changed, 1221 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/lint/LICENSE b/vendor/honnef.co/go/tools/lint/LICENSE
new file mode 100644
index 0000000..796130a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2013 The Go Authors. All rights reserved.
+Copyright (c) 2016 Dominik Honnef. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/honnef.co/go/tools/lint/lint.go b/vendor/honnef.co/go/tools/lint/lint.go
new file mode 100644
index 0000000..75a5198
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lint.go
@@ -0,0 +1,844 @@
+// Copyright (c) 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+// Package lint provides the foundation for tools like gosimple.
+package lint // import "honnef.co/go/tools/lint"
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/constant"
+ "go/printer"
+ "go/token"
+ "go/types"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "unicode"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/go/loader"
+ "honnef.co/go/tools/ssa"
+ "honnef.co/go/tools/ssa/ssautil"
+)
+
+type Job struct {
+ Program *Program
+
+ checker string
+ check string
+ problems []Problem
+}
+
+type Ignore interface {
+ Match(p Problem) bool
+}
+
+type LineIgnore struct {
+ File string
+ Line int
+ Checks []string
+ matched bool
+ pos token.Pos
+}
+
+func (li *LineIgnore) Match(p Problem) bool {
+ if p.Position.Filename != li.File || p.Position.Line != li.Line {
+ return false
+ }
+ for _, c := range li.Checks {
+ if m, _ := filepath.Match(c, p.Check); m {
+ li.matched = true
+ return true
+ }
+ }
+ return false
+}
+
+func (li *LineIgnore) String() string {
+ matched := "not matched"
+ if li.matched {
+ matched = "matched"
+ }
+ return fmt.Sprintf("%s:%d %s (%s)", li.File, li.Line, strings.Join(li.Checks, ", "), matched)
+}
+
+type FileIgnore struct {
+ File string
+ Checks []string
+}
+
+func (fi *FileIgnore) Match(p Problem) bool {
+ if p.Position.Filename != fi.File {
+ return false
+ }
+ for _, c := range fi.Checks {
+ if m, _ := filepath.Match(c, p.Check); m {
+ return true
+ }
+ }
+ return false
+}
+
+type GlobIgnore struct {
+ Pattern string
+ Checks []string
+}
+
+func (gi *GlobIgnore) Match(p Problem) bool {
+ if gi.Pattern != "*" {
+ pkgpath := p.Package.Path()
+ if strings.HasSuffix(pkgpath, "_test") {
+ pkgpath = pkgpath[:len(pkgpath)-len("_test")]
+ }
+ name := filepath.Join(pkgpath, filepath.Base(p.Position.Filename))
+ if m, _ := filepath.Match(gi.Pattern, name); !m {
+ return false
+ }
+ }
+ for _, c := range gi.Checks {
+ if m, _ := filepath.Match(c, p.Check); m {
+ return true
+ }
+ }
+ return false
+}
+
+type Program struct {
+ SSA *ssa.Program
+ Prog *loader.Program
+ // TODO(dh): Rename to InitialPackages?
+ Packages []*Pkg
+ InitialFunctions []*ssa.Function
+ AllFunctions []*ssa.Function
+ Files []*ast.File
+ Info *types.Info
+ GoVersion int
+
+ tokenFileMap map[*token.File]*ast.File
+ astFileMap map[*ast.File]*Pkg
+}
+
+type Func func(*Job)
+
+// Problem represents a problem in some source code.
+type Problem struct {
+ pos token.Pos
+ Position token.Position // position in source file
+ Text string // the prose that describes the problem
+ Check string
+ Checker string
+ Package *types.Package
+ Ignored bool
+}
+
+func (p *Problem) String() string {
+ if p.Check == "" {
+ return p.Text
+ }
+ return fmt.Sprintf("%s (%s)", p.Text, p.Check)
+}
+
+type Checker interface {
+ Name() string
+ Prefix() string
+ Init(*Program)
+ Funcs() map[string]Func
+}
+
+// A Linter lints Go source code.
+type Linter struct {
+ Checker Checker
+ Ignores []Ignore
+ GoVersion int
+ ReturnIgnored bool
+
+ automaticIgnores []Ignore
+}
+
+func (l *Linter) ignore(p Problem) bool {
+ ignored := false
+ for _, ig := range l.automaticIgnores {
+ // We cannot short-circuit these, as we want to record, for
+ // each ignore, whether it matched or not.
+ if ig.Match(p) {
+ ignored = true
+ }
+ }
+ if ignored {
+ // no need to execute other ignores if we've already had a
+ // match.
+ return true
+ }
+ for _, ig := range l.Ignores {
+ // We can short-circuit here, as we aren't tracking any
+ // information.
+ if ig.Match(p) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (prog *Program) File(node Positioner) *ast.File {
+ return prog.tokenFileMap[prog.SSA.Fset.File(node.Pos())]
+}
+
+func (j *Job) File(node Positioner) *ast.File {
+ return j.Program.File(node)
+}
+
+// TODO(dh): switch to sort.Slice when Go 1.9 lands.
+type byPosition struct {
+ fset *token.FileSet
+ ps []Problem
+}
+
+func (ps byPosition) Len() int {
+ return len(ps.ps)
+}
+
+func (ps byPosition) Less(i int, j int) bool {
+ pi, pj := ps.ps[i].Position, ps.ps[j].Position
+
+ if pi.Filename != pj.Filename {
+ return pi.Filename < pj.Filename
+ }
+ if pi.Line != pj.Line {
+ return pi.Line < pj.Line
+ }
+ if pi.Column != pj.Column {
+ return pi.Column < pj.Column
+ }
+
+ return ps.ps[i].Text < ps.ps[j].Text
+}
+
+func (ps byPosition) Swap(i int, j int) {
+ ps.ps[i], ps.ps[j] = ps.ps[j], ps.ps[i]
+}
+
+func parseDirective(s string) (cmd string, args []string) {
+ if !strings.HasPrefix(s, "//lint:") {
+ return "", nil
+ }
+ s = strings.TrimPrefix(s, "//lint:")
+ fields := strings.Split(s, " ")
+ return fields[0], fields[1:]
+}
+
+func (l *Linter) Lint(lprog *loader.Program, conf *loader.Config) []Problem {
+ ssaprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+ ssaprog.Build()
+ pkgMap := map[*ssa.Package]*Pkg{}
+ var pkgs []*Pkg
+ for _, pkginfo := range lprog.InitialPackages() {
+ ssapkg := ssaprog.Package(pkginfo.Pkg)
+ var bp *build.Package
+ if len(pkginfo.Files) != 0 {
+ path := lprog.Fset.Position(pkginfo.Files[0].Pos()).Filename
+ dir := filepath.Dir(path)
+ var err error
+ ctx := conf.Build
+ if ctx == nil {
+ ctx = &build.Default
+ }
+ bp, err = ctx.ImportDir(dir, 0)
+ if err != nil {
+ // shouldn't happen
+ }
+ }
+ pkg := &Pkg{
+ Package: ssapkg,
+ Info: pkginfo,
+ BuildPkg: bp,
+ }
+ pkgMap[ssapkg] = pkg
+ pkgs = append(pkgs, pkg)
+ }
+ prog := &Program{
+ SSA: ssaprog,
+ Prog: lprog,
+ Packages: pkgs,
+ Info: &types.Info{},
+ GoVersion: l.GoVersion,
+ tokenFileMap: map[*token.File]*ast.File{},
+ astFileMap: map[*ast.File]*Pkg{},
+ }
+
+ initial := map[*types.Package]struct{}{}
+ for _, pkg := range pkgs {
+ initial[pkg.Info.Pkg] = struct{}{}
+ }
+ for fn := range ssautil.AllFunctions(ssaprog) {
+ if fn.Pkg == nil {
+ continue
+ }
+ prog.AllFunctions = append(prog.AllFunctions, fn)
+ if _, ok := initial[fn.Pkg.Pkg]; ok {
+ prog.InitialFunctions = append(prog.InitialFunctions, fn)
+ }
+ }
+ for _, pkg := range pkgs {
+ prog.Files = append(prog.Files, pkg.Info.Files...)
+
+ ssapkg := ssaprog.Package(pkg.Info.Pkg)
+ for _, f := range pkg.Info.Files {
+ prog.astFileMap[f] = pkgMap[ssapkg]
+ }
+ }
+
+ for _, pkginfo := range lprog.AllPackages {
+ for _, f := range pkginfo.Files {
+ tf := lprog.Fset.File(f.Pos())
+ prog.tokenFileMap[tf] = f
+ }
+ }
+
+ var out []Problem
+ l.automaticIgnores = nil
+ for _, pkginfo := range lprog.InitialPackages() {
+ for _, f := range pkginfo.Files {
+ cm := ast.NewCommentMap(lprog.Fset, f, f.Comments)
+ for node, cgs := range cm {
+ for _, cg := range cgs {
+ for _, c := range cg.List {
+ if !strings.HasPrefix(c.Text, "//lint:") {
+ continue
+ }
+ cmd, args := parseDirective(c.Text)
+ switch cmd {
+ case "ignore", "file-ignore":
+ if len(args) < 2 {
+ // FIXME(dh): this causes duplicated warnings when using megacheck
+ p := Problem{
+ pos: c.Pos(),
+ Position: prog.DisplayPosition(c.Pos()),
+ Text: "malformed linter directive; missing the required reason field?",
+ Check: "",
+ Checker: l.Checker.Name(),
+ Package: nil,
+ }
+ out = append(out, p)
+ continue
+ }
+ default:
+ // unknown directive, ignore
+ continue
+ }
+ checks := strings.Split(args[0], ",")
+ pos := prog.DisplayPosition(node.Pos())
+ var ig Ignore
+ switch cmd {
+ case "ignore":
+ ig = &LineIgnore{
+ File: pos.Filename,
+ Line: pos.Line,
+ Checks: checks,
+ pos: c.Pos(),
+ }
+ case "file-ignore":
+ ig = &FileIgnore{
+ File: pos.Filename,
+ Checks: checks,
+ }
+ }
+ l.automaticIgnores = append(l.automaticIgnores, ig)
+ }
+ }
+ }
+ }
+ }
+
+ sizes := struct {
+ types int
+ defs int
+ uses int
+ implicits int
+ selections int
+ scopes int
+ }{}
+ for _, pkg := range pkgs {
+ sizes.types += len(pkg.Info.Info.Types)
+ sizes.defs += len(pkg.Info.Info.Defs)
+ sizes.uses += len(pkg.Info.Info.Uses)
+ sizes.implicits += len(pkg.Info.Info.Implicits)
+ sizes.selections += len(pkg.Info.Info.Selections)
+ sizes.scopes += len(pkg.Info.Info.Scopes)
+ }
+ prog.Info.Types = make(map[ast.Expr]types.TypeAndValue, sizes.types)
+ prog.Info.Defs = make(map[*ast.Ident]types.Object, sizes.defs)
+ prog.Info.Uses = make(map[*ast.Ident]types.Object, sizes.uses)
+ prog.Info.Implicits = make(map[ast.Node]types.Object, sizes.implicits)
+ prog.Info.Selections = make(map[*ast.SelectorExpr]*types.Selection, sizes.selections)
+ prog.Info.Scopes = make(map[ast.Node]*types.Scope, sizes.scopes)
+ for _, pkg := range pkgs {
+ for k, v := range pkg.Info.Info.Types {
+ prog.Info.Types[k] = v
+ }
+ for k, v := range pkg.Info.Info.Defs {
+ prog.Info.Defs[k] = v
+ }
+ for k, v := range pkg.Info.Info.Uses {
+ prog.Info.Uses[k] = v
+ }
+ for k, v := range pkg.Info.Info.Implicits {
+ prog.Info.Implicits[k] = v
+ }
+ for k, v := range pkg.Info.Info.Selections {
+ prog.Info.Selections[k] = v
+ }
+ for k, v := range pkg.Info.Info.Scopes {
+ prog.Info.Scopes[k] = v
+ }
+ }
+ l.Checker.Init(prog)
+
+ funcs := l.Checker.Funcs()
+ var keys []string
+ for k := range funcs {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ var jobs []*Job
+ for _, k := range keys {
+ j := &Job{
+ Program: prog,
+ checker: l.Checker.Name(),
+ check: k,
+ }
+ jobs = append(jobs, j)
+ }
+ wg := &sync.WaitGroup{}
+ for _, j := range jobs {
+ wg.Add(1)
+ go func(j *Job) {
+ defer wg.Done()
+ fn := funcs[j.check]
+ if fn == nil {
+ return
+ }
+ fn(j)
+ }(j)
+ }
+ wg.Wait()
+
+ for _, j := range jobs {
+ for _, p := range j.problems {
+ p.Ignored = l.ignore(p)
+ if l.ReturnIgnored || !p.Ignored {
+ out = append(out, p)
+ }
+ }
+ }
+
+ for _, ig := range l.automaticIgnores {
+ ig, ok := ig.(*LineIgnore)
+ if !ok {
+ continue
+ }
+ if ig.matched {
+ continue
+ }
+ for _, c := range ig.Checks {
+ idx := strings.IndexFunc(c, func(r rune) bool {
+ return unicode.IsNumber(r)
+ })
+ if idx == -1 {
+ // malformed check name, backing out
+ continue
+ }
+ if c[:idx] != l.Checker.Prefix() {
+ // not for this checker
+ continue
+ }
+ p := Problem{
+ pos: ig.pos,
+ Position: prog.DisplayPosition(ig.pos),
+ Text: "this linter directive didn't match anything; should it be removed?",
+ Check: "",
+ Checker: l.Checker.Name(),
+ Package: nil,
+ }
+ out = append(out, p)
+ }
+ }
+
+ sort.Sort(byPosition{lprog.Fset, out})
+ return out
+}
+
+// Pkg represents a package being linted.
+type Pkg struct {
+ *ssa.Package
+ Info *loader.PackageInfo
+ BuildPkg *build.Package
+}
+
+type packager interface {
+ Package() *ssa.Package
+}
+
+func IsExample(fn *ssa.Function) bool {
+ if !strings.HasPrefix(fn.Name(), "Example") {
+ return false
+ }
+ f := fn.Prog.Fset.File(fn.Pos())
+ if f == nil {
+ return false
+ }
+ return strings.HasSuffix(f.Name(), "_test.go")
+}
+
+func (j *Job) IsInTest(node Positioner) bool {
+ f := j.Program.SSA.Fset.File(node.Pos())
+ return f != nil && strings.HasSuffix(f.Name(), "_test.go")
+}
+
+func (j *Job) IsInMain(node Positioner) bool {
+ if node, ok := node.(packager); ok {
+ return node.Package().Pkg.Name() == "main"
+ }
+ pkg := j.NodePackage(node)
+ if pkg == nil {
+ return false
+ }
+ return pkg.Pkg.Name() == "main"
+}
+
+type Positioner interface {
+ Pos() token.Pos
+}
+
+func (prog *Program) DisplayPosition(p token.Pos) token.Position {
+ // The //line compiler directive can be used to change the file
+ // name and line numbers associated with code. This can, for
+ // example, be used by code generation tools. The most prominent
+ // example is 'go tool cgo', which uses //line directives to refer
+ // back to the original source code.
+ //
+ // In the context of our linters, we need to treat these
+ // directives differently depending on context. For cgo files, we
+ // want to honour the directives, so that line numbers are
+ // adjusted correctly. For all other files, we want to ignore the
+ // directives, so that problems are reported at their actual
+ // position and not, for example, a yacc grammar file. This also
+ // affects the ignore mechanism, since it operates on the position
+ // information stored within problems. With this implementation, a
+ // user will ignore foo.go, not foo.y
+
+ pkg := prog.astFileMap[prog.tokenFileMap[prog.Prog.Fset.File(p)]]
+ bp := pkg.BuildPkg
+ adjPos := prog.Prog.Fset.Position(p)
+ if bp == nil {
+ // couldn't find the package for some reason (deleted? faulty
+ // file system?)
+ return adjPos
+ }
+ base := filepath.Base(adjPos.Filename)
+ for _, f := range bp.CgoFiles {
+ if f == base {
+ // this is a cgo file, use the adjusted position
+ return adjPos
+ }
+ }
+ // not a cgo file, ignore //line directives
+ return prog.Prog.Fset.PositionFor(p, false)
+}
+
+func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem {
+ tf := j.Program.SSA.Fset.File(n.Pos())
+ f := j.Program.tokenFileMap[tf]
+ pkg := j.Program.astFileMap[f].Pkg
+
+ pos := j.Program.DisplayPosition(n.Pos())
+ problem := Problem{
+ pos: n.Pos(),
+ Position: pos,
+ Text: fmt.Sprintf(format, args...),
+ Check: j.check,
+ Checker: j.checker,
+ Package: pkg,
+ }
+ j.problems = append(j.problems, problem)
+ return &j.problems[len(j.problems)-1]
+}
+
+func (j *Job) Render(x interface{}) string {
+ fset := j.Program.SSA.Fset
+ var buf bytes.Buffer
+ if err := printer.Fprint(&buf, fset, x); err != nil {
+ panic(err)
+ }
+ return buf.String()
+}
+
+func (j *Job) RenderArgs(args []ast.Expr) string {
+ var ss []string
+ for _, arg := range args {
+ ss = append(ss, j.Render(arg))
+ }
+ return strings.Join(ss, ", ")
+}
+
+func IsIdent(expr ast.Expr, ident string) bool {
+ id, ok := expr.(*ast.Ident)
+ return ok && id.Name == ident
+}
+
+// isBlank returns whether id is the blank identifier "_".
+// If id == nil, the answer is false.
+func IsBlank(id ast.Expr) bool {
+ ident, ok := id.(*ast.Ident)
+ return ok && ident.Name == "_"
+}
+
+func IsZero(expr ast.Expr) bool {
+ lit, ok := expr.(*ast.BasicLit)
+ return ok && lit.Kind == token.INT && lit.Value == "0"
+}
+
+func (j *Job) IsNil(expr ast.Expr) bool {
+ return j.Program.Info.Types[expr].IsNil()
+}
+
+func (j *Job) BoolConst(expr ast.Expr) bool {
+ val := j.Program.Info.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
+ return constant.BoolVal(val)
+}
+
+func (j *Job) IsBoolConst(expr ast.Expr) bool {
+ // We explicitly don't support typed bools because more often than
+ // not, custom bool types are used as binary enums and the
+ // explicit comparison is desired.
+
+ ident, ok := expr.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ obj := j.Program.Info.ObjectOf(ident)
+ c, ok := obj.(*types.Const)
+ if !ok {
+ return false
+ }
+ basic, ok := c.Type().(*types.Basic)
+ if !ok {
+ return false
+ }
+ if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
+ return false
+ }
+ return true
+}
+
+func (j *Job) ExprToInt(expr ast.Expr) (int64, bool) {
+ tv := j.Program.Info.Types[expr]
+ if tv.Value == nil {
+ return 0, false
+ }
+ if tv.Value.Kind() != constant.Int {
+ return 0, false
+ }
+ return constant.Int64Val(tv.Value)
+}
+
+func (j *Job) ExprToString(expr ast.Expr) (string, bool) {
+ val := j.Program.Info.Types[expr].Value
+ if val == nil {
+ return "", false
+ }
+ if val.Kind() != constant.String {
+ return "", false
+ }
+ return constant.StringVal(val), true
+}
+
+func (j *Job) NodePackage(node Positioner) *Pkg {
+ f := j.File(node)
+ return j.Program.astFileMap[f]
+}
+
+func IsGenerated(f *ast.File) bool {
+ comments := f.Comments
+ if len(comments) > 0 {
+ comment := comments[0].Text()
+ return strings.Contains(comment, "Code generated by") ||
+ strings.Contains(comment, "DO NOT EDIT")
+ }
+ return false
+}
+
+func Preamble(f *ast.File) string {
+ cutoff := f.Package
+ if f.Doc != nil {
+ cutoff = f.Doc.Pos()
+ }
+ var out []string
+ for _, cmt := range f.Comments {
+ if cmt.Pos() >= cutoff {
+ break
+ }
+ out = append(out, cmt.Text())
+ }
+ return strings.Join(out, "\n")
+}
+
+func IsPointerLike(T types.Type) bool {
+ switch T := T.Underlying().(type) {
+ case *types.Interface, *types.Chan, *types.Map, *types.Pointer:
+ return true
+ case *types.Basic:
+ return T.Kind() == types.UnsafePointer
+ }
+ return false
+}
+
+func (j *Job) IsGoVersion(minor int) bool {
+ return j.Program.GoVersion >= minor
+}
+
+func (j *Job) IsCallToAST(node ast.Node, name string) bool {
+ call, ok := node.(*ast.CallExpr)
+ if !ok {
+ return false
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+ fn, ok := j.Program.Info.ObjectOf(sel.Sel).(*types.Func)
+ return ok && fn.FullName() == name
+}
+
+func (j *Job) IsCallToAnyAST(node ast.Node, names ...string) bool {
+ for _, name := range names {
+ if j.IsCallToAST(node, name) {
+ return true
+ }
+ }
+ return false
+}
+
+func CallName(call *ssa.CallCommon) string {
+ if call.IsInvoke() {
+ return ""
+ }
+ switch v := call.Value.(type) {
+ case *ssa.Function:
+ fn, ok := v.Object().(*types.Func)
+ if !ok {
+ return ""
+ }
+ return fn.FullName()
+ case *ssa.Builtin:
+ return v.Name()
+ }
+ return ""
+}
+
+func IsCallTo(call *ssa.CallCommon, name string) bool {
+ return CallName(call) == name
+}
+
+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
+}
+
+func NodeFns(pkgs []*Pkg) map[ast.Node]*ssa.Function {
+ out := map[ast.Node]*ssa.Function{}
+
+ wg := &sync.WaitGroup{}
+ chNodeFns := make(chan map[ast.Node]*ssa.Function, runtime.NumCPU()*2)
+ for _, pkg := range pkgs {
+ pkg := pkg
+ wg.Add(1)
+ go func() {
+ m := map[ast.Node]*ssa.Function{}
+ for _, f := range pkg.Info.Files {
+ ast.Walk(&globalVisitor{m, pkg, f}, f)
+ }
+ chNodeFns <- m
+ wg.Done()
+ }()
+ }
+ go func() {
+ wg.Wait()
+ close(chNodeFns)
+ }()
+
+ for nodeFns := range chNodeFns {
+ for k, v := range nodeFns {
+ out[k] = v
+ }
+ }
+
+ return out
+}
+
+type globalVisitor struct {
+ m map[ast.Node]*ssa.Function
+ pkg *Pkg
+ f *ast.File
+}
+
+func (v *globalVisitor) Visit(node ast.Node) ast.Visitor {
+ switch node := node.(type) {
+ case *ast.CallExpr:
+ v.m[node] = v.pkg.Func("init")
+ return v
+ case *ast.FuncDecl, *ast.FuncLit:
+ nv := &fnVisitor{v.m, v.f, v.pkg, nil}
+ return nv.Visit(node)
+ default:
+ return v
+ }
+}
+
+type fnVisitor struct {
+ m map[ast.Node]*ssa.Function
+ f *ast.File
+ pkg *Pkg
+ ssafn *ssa.Function
+}
+
+func (v *fnVisitor) Visit(node ast.Node) ast.Visitor {
+ switch node := node.(type) {
+ case *ast.FuncDecl:
+ var ssafn *ssa.Function
+ ssafn = v.pkg.Prog.FuncValue(v.pkg.Info.ObjectOf(node.Name).(*types.Func))
+ v.m[node] = ssafn
+ if ssafn == nil {
+ return nil
+ }
+ return &fnVisitor{v.m, v.f, v.pkg, ssafn}
+ case *ast.FuncLit:
+ var ssafn *ssa.Function
+ path, _ := astutil.PathEnclosingInterval(v.f, node.Pos(), node.Pos())
+ ssafn = ssa.EnclosingFunction(v.pkg.Package, path)
+ v.m[node] = ssafn
+ if ssafn == nil {
+ return nil
+ }
+ return &fnVisitor{v.m, v.f, v.pkg, ssafn}
+ case nil:
+ return nil
+ default:
+ v.m[node] = v.ssafn
+ return v
+ }
+}
diff --git a/vendor/honnef.co/go/tools/lint/lintutil/util.go b/vendor/honnef.co/go/tools/lint/lintutil/util.go
new file mode 100644
index 0000000..0bb1426
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintutil/util.go
@@ -0,0 +1,349 @@
+// Copyright (c) 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+// Package lintutil provides helpers for writing linter command lines.
+package lintutil // import "honnef.co/go/tools/lint/lintutil"
+
+import (
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "honnef.co/go/tools/lint"
+ "honnef.co/go/tools/version"
+
+ "github.com/kisielk/gotool"
+ "golang.org/x/tools/go/loader"
+)
+
+type OutputFormatter interface {
+ Format(p lint.Problem)
+}
+
+type TextOutput struct {
+ w io.Writer
+}
+
+func (o TextOutput) Format(p lint.Problem) {
+ fmt.Fprintf(o.w, "%v: %s\n", relativePositionString(p.Position), p.String())
+}
+
+type JSONOutput struct {
+ w io.Writer
+}
+
+func (o JSONOutput) Format(p lint.Problem) {
+ type location struct {
+ File string `json:"file"`
+ Line int `json:"line"`
+ Column int `json:"column"`
+ }
+ jp := struct {
+ Checker string `json:"checker"`
+ Code string `json:"code"`
+ Severity string `json:"severity,omitempty"`
+ Location location `json:"location"`
+ Message string `json:"message"`
+ Ignored bool `json:"ignored"`
+ }{
+ p.Checker,
+ p.Check,
+ "", // TODO(dh): support severity
+ location{
+ p.Position.Filename,
+ p.Position.Line,
+ p.Position.Column,
+ },
+ p.Text,
+ p.Ignored,
+ }
+ _ = json.NewEncoder(o.w).Encode(jp)
+}
+func usage(name string, flags *flag.FlagSet) func() {
+ return func() {
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
+ fmt.Fprintf(os.Stderr, "Flags:\n")
+ flags.PrintDefaults()
+ }
+}
+
+type runner struct {
+ checker lint.Checker
+ tags []string
+ ignores []lint.Ignore
+ version int
+ returnIgnored bool
+}
+
+func resolveRelative(importPaths []string, tags []string) (goFiles bool, err error) {
+ if len(importPaths) == 0 {
+ return false, nil
+ }
+ if strings.HasSuffix(importPaths[0], ".go") {
+ // User is specifying a package in terms of .go files, don't resolve
+ return true, nil
+ }
+ wd, err := os.Getwd()
+ if err != nil {
+ return false, err
+ }
+ ctx := build.Default
+ ctx.BuildTags = tags
+ for i, path := range importPaths {
+ bpkg, err := ctx.Import(path, wd, build.FindOnly)
+ if err != nil {
+ return false, fmt.Errorf("can't load package %q: %v", path, err)
+ }
+ importPaths[i] = bpkg.ImportPath
+ }
+ return false, nil
+}
+
+func parseIgnore(s string) ([]lint.Ignore, error) {
+ var out []lint.Ignore
+ if len(s) == 0 {
+ return nil, nil
+ }
+ for _, part := range strings.Fields(s) {
+ p := strings.Split(part, ":")
+ if len(p) != 2 {
+ return nil, errors.New("malformed ignore string")
+ }
+ path := p[0]
+ checks := strings.Split(p[1], ",")
+ out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
+ }
+ return out, nil
+}
+
+type versionFlag int
+
+func (v *versionFlag) String() string {
+ return fmt.Sprintf("1.%d", *v)
+}
+
+func (v *versionFlag) Set(s string) error {
+ if len(s) < 3 {
+ return errors.New("invalid Go version")
+ }
+ if s[0] != '1' {
+ return errors.New("invalid Go version")
+ }
+ if s[1] != '.' {
+ return errors.New("invalid Go version")
+ }
+ i, err := strconv.Atoi(s[2:])
+ *v = versionFlag(i)
+ return err
+}
+
+func (v *versionFlag) Get() interface{} {
+ return int(*v)
+}
+
+func FlagSet(name string) *flag.FlagSet {
+ flags := flag.NewFlagSet("", flag.ExitOnError)
+ flags.Usage = usage(name, flags)
+ flags.Float64("min_confidence", 0, "Deprecated; use -ignore instead")
+ flags.String("tags", "", "List of `build tags`")
+ flags.String("ignore", "", "Space separated list of checks to ignore, in the following format: 'import/path/file.go:Check1,Check2,...' Both the import path and file name sections support globbing, e.g. 'os/exec/*_test.go'")
+ flags.Bool("tests", true, "Include tests")
+ flags.Bool("version", false, "Print version and exit")
+ flags.Bool("show-ignored", false, "Don't filter ignored problems")
+ flags.String("f", "text", "Output `format` (valid choices are 'text' and 'json')")
+
+ tags := build.Default.ReleaseTags
+ v := tags[len(tags)-1][2:]
+ version := new(versionFlag)
+ if err := version.Set(v); err != nil {
+ panic(fmt.Sprintf("internal error: %s", err))
+ }
+
+ flags.Var(version, "go", "Target Go `version` in the format '1.x'")
+ return flags
+}
+
+type CheckerConfig struct {
+ Checker lint.Checker
+ ExitNonZero bool
+}
+
+func ProcessFlagSet(confs []CheckerConfig, fs *flag.FlagSet) {
+ tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
+ ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
+ tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
+ goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
+ format := fs.Lookup("f").Value.(flag.Getter).Get().(string)
+ printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
+ showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
+
+ if printVersion {
+ version.Print()
+ os.Exit(0)
+ }
+
+ var cs []lint.Checker
+ for _, conf := range confs {
+ cs = append(cs, conf.Checker)
+ }
+ pss, err := Lint(cs, fs.Args(), &Options{
+ Tags: strings.Fields(tags),
+ LintTests: tests,
+ Ignores: ignore,
+ GoVersion: goVersion,
+ ReturnIgnored: showIgnored,
+ })
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ var ps []lint.Problem
+ for _, p := range pss {
+ ps = append(ps, p...)
+ }
+
+ var f OutputFormatter
+ switch format {
+ case "text":
+ f = TextOutput{os.Stdout}
+ case "json":
+ f = JSONOutput{os.Stdout}
+ default:
+ fmt.Fprintf(os.Stderr, "unsupported output format %q\n", format)
+ os.Exit(2)
+ }
+
+ for _, p := range ps {
+ f.Format(p)
+ }
+ for i, p := range pss {
+ if len(p) != 0 && confs[i].ExitNonZero {
+ os.Exit(1)
+ }
+ }
+}
+
+type Options struct {
+ Tags []string
+ LintTests bool
+ Ignores string
+ GoVersion int
+ ReturnIgnored bool
+}
+
+func Lint(cs []lint.Checker, pkgs []string, opt *Options) ([][]lint.Problem, error) {
+ if opt == nil {
+ opt = &Options{}
+ }
+ ignores, err := parseIgnore(opt.Ignores)
+ if err != nil {
+ return nil, err
+ }
+ paths := gotool.ImportPaths(pkgs)
+ goFiles, err := resolveRelative(paths, opt.Tags)
+ if err != nil {
+ return nil, err
+ }
+ ctx := build.Default
+ ctx.BuildTags = opt.Tags
+ hadError := false
+ conf := &loader.Config{
+ Build: &ctx,
+ ParserMode: parser.ParseComments,
+ ImportPkgs: map[string]bool{},
+ TypeChecker: types.Config{
+ Error: func(err error) {
+ // Only print the first error found
+ if hadError {
+ return
+ }
+ hadError = true
+ fmt.Fprintln(os.Stderr, err)
+ },
+ },
+ }
+ if goFiles {
+ conf.CreateFromFilenames("adhoc", paths...)
+ } else {
+ for _, path := range paths {
+ conf.ImportPkgs[path] = opt.LintTests
+ }
+ }
+ lprog, err := conf.Load()
+ if err != nil {
+ return nil, err
+ }
+
+ var problems [][]lint.Problem
+ for _, c := range cs {
+ runner := &runner{
+ checker: c,
+ tags: opt.Tags,
+ ignores: ignores,
+ version: opt.GoVersion,
+ returnIgnored: opt.ReturnIgnored,
+ }
+ problems = append(problems, runner.lint(lprog, conf))
+ }
+ return problems, nil
+}
+
+func shortPath(path string) string {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return path
+ }
+ if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
+ return rel
+ }
+ return path
+}
+
+func relativePositionString(pos token.Position) string {
+ s := shortPath(pos.Filename)
+ if pos.IsValid() {
+ if s != "" {
+ s += ":"
+ }
+ s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
+ }
+ if s == "" {
+ s = "-"
+ }
+ return s
+}
+
+func ProcessArgs(name string, cs []CheckerConfig, args []string) {
+ flags := FlagSet(name)
+ flags.Parse(args)
+
+ ProcessFlagSet(cs, flags)
+}
+
+func (runner *runner) lint(lprog *loader.Program, conf *loader.Config) []lint.Problem {
+ l := &lint.Linter{
+ Checker: runner.checker,
+ Ignores: runner.ignores,
+ GoVersion: runner.version,
+ ReturnIgnored: runner.returnIgnored,
+ }
+ return l.Lint(lprog, conf)
+}