aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/lint/lint.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/lint/lint.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/lint/lint.go')
-rw-r--r--vendor/honnef.co/go/tools/lint/lint.go844
1 files changed, 844 insertions, 0 deletions
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
+ }
+}