func shouldTraverse(dir string, fi os.FileInfo) bool {
	if fi.IsDir() {
		return true
	}
	if fi.Mode()&os.ModeSymlink == 0 {
		return false
	}
	path := filepath.Join(dir, fi.Name())
	target, err := filepath.EvalSymlinks(path)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return false
	}
	ts, err := os.Stat(target)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return false
	}
	if !ts.IsDir() {
		return false
	}
	realParent, err := filepath.EvalSymlinks(dir)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return false
	}
	realPath := filepath.Join(realParent, fi.Name())
	visitedSymlinks.Lock()
	defer visitedSymlinks.Unlock()
	if visitedSymlinks.m == nil {
		visitedSymlinks.m = make(map[string]struct{})
	}
	if _, ok := visitedSymlinks.m[realPath]; ok {
		return false
	}
	visitedSymlinks.m[realPath] = struct{}{}
	return true
}
shouldTraverse checks if fi, found in dir, is a directory or a symlink to a directory.
It makes sure symlinks were never visited before to avoid symlink loops.
package imports
import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"golang.org/x/tools/go/ast/astutil"
)
var importToGroup = []func(importPath string) (num int, ok bool){
	func(importPath string) (num int, ok bool) {
		if strings.HasPrefix(importPath, "appengine") {
			return 2, true
		}
		return
	},
	func(importPath string) (num int, ok bool) {
		if strings.Contains(importPath, ".") {
			return 1, true
		}
		return
	},
}
func importGroup(importPath string) int {
	for _, fn := range importToGroup {
		if n, ok := fn(importPath); ok {
			return n
		}
	}
	return 0
}
func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) {
	refs := make(map[string]map[string]bool)
	decls := make(map[string]*ast.ImportSpec)
	abs, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}
	srcDir := path.Dir(abs)
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := importPathToName(strings.Trim(v.Path.Value, `\"`), srcDir)
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)
	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]string{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			name := ""
			if is.Name != nil {
				name = is.Name.Name
			}
			unusedImport[strings.Trim(is.Path.Value, `"`)] = name
		}
	}
	for ipath, name := range unusedImport {
		if ipath == "C" {
			continue
		}
		astutil.DeleteNamedImport(fset, f, name, ipath)
	}
	searches := 0
	type result struct {
		ipath string
		name  string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue  
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, rename, err := findImport(pkgName, symbols, filename)
			r := result{ipath: ipath, err: err}
			if rename {
				r.name = pkgName
			}
			results <- r
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return nil, result.err
		}
		if result.ipath != "" {
			if result.name != "" {
				astutil.AddNamedImport(fset, f, result.name, result.ipath)
			} else {
				astutil.AddImport(fset, f, result.ipath)
			}
			added = append(added, result.ipath)
		}
	}
	return added, nil
}
var importPathToName = importPathToNameGoPath
func importPathToNameBasic(importPath, srcDir string) (packageName string) {
	return path.Base(importPath)
}
func importPathToNameGoPath(importPath, srcDir string) (packageName string) {
	if buildPkg, err := build.Import(importPath, srcDir, 0); err == nil {
		return buildPkg.Name
	} else {
		return importPathToNameBasic(importPath, srcDir)
	}
}
type pkg struct {
	importpath string  
	dir        string  
}
var pkgIndexOnce = &sync.Once{}
var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg  
}
type gate chan struct{}
func (g gate) enter() { g <- struct{}{} }
func (g gate) leave() { <-g }
var fsgate = make(gate, 8)
var visitedSymlinks struct {
	sync.Mutex
	m map[string]struct{}
}