func cleanFilename(path string) string {
	const prefix = "$GOROOT"
	if len(path) < len(prefix) || !strings.EqualFold(prefix, path[:len(prefix)]) {
		return path
	}
	return runtime.GOROOT() + path[len(prefix):]
}
cleanFilename normalizes any file names that come out of the fileset.
package main
import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	rpast "github.com/rogpeppe/godef/go/ast"
	rpprinter "github.com/rogpeppe/godef/go/printer"
	rptypes "github.com/rogpeppe/godef/go/types"
	gotoken "go/token"
	gotypes "go/types"
	"golang.org/x/tools/go/packages"
)
var forcePackages triBool
func init() {
	flag.Var(&forcePackages, "new-implementation", "force godef to use the new go/packages implentation")
}
type triBool int
const (
	unset = triBool(iota)
	on
	off
)
func (b *triBool) Set(s string) error {
	v, err := strconv.ParseBool(s)
	if v {
		*b = on
	} else {
		*b = off
	}
	return err
}
func (b *triBool) Get() interface{} {
	return *b
}
func (b *triBool) String() string {
	switch *b {
	case unset:
		return "default"
	case on:
		return "true"
	case off:
		return "false"
	default:
		return "invalid"
	}
}
func (b *triBool) IsBoolFlag() bool {
	return true
}
func detectModuleMode(cfg *packages.Config) bool {
	for _, e := range cfg.Env {
		switch e {
		case "GO111MODULE=off":
			return false
		case "GO111MODULE=on":
			return true
		}
	}
	if _, err := os.Stat(filepath.Join(cfg.Dir, "go.mod")); !os.IsNotExist(err) {
		return true
	}
	cmd := exec.Command("go", "env", "GOMOD")
	cmd.Env = cfg.Env
	cmd.Dir = cfg.Dir
	out, err := cmd.Output()
	if err == nil {
		return len(strings.TrimSpace(string(out))) > 0
	}
	return false
}
func adaptGodef(cfg *packages.Config, filename string, src []byte, searchpos int) (*Object, error) {
	usePackages := false
	switch forcePackages {
	case unset:
		usePackages = detectModuleMode(cfg)
	case on:
		usePackages = true
	case off:
		usePackages = false
	}
	if usePackages {
		fset, obj, err := godefPackages(cfg, filename, src, searchpos)
		if err != nil {
			return nil, err
		}
		return adaptGoObject(fset, obj)
	}
	obj, typ, err := godef(filename, src, searchpos)
	if err != nil {
		return nil, err
	}
	return adaptRPObject(obj, typ)
}
func adaptRPObject(obj *rpast.Object, typ rptypes.Type) (*Object, error) {
	pos := rptypes.FileSet.Position(rptypes.DeclPos(obj))
	result := &Object{
		Name: obj.Name,
		Pkg:  typ.Pkg,
		Position: Position{
			Filename: pos.Filename,
			Line:     pos.Line,
			Column:   pos.Column,
		},
		Type: typ,
	}
	switch obj.Kind {
	case rpast.Bad:
		result.Kind = BadKind
	case rpast.Fun:
		result.Kind = FuncKind
	case rpast.Var:
		result.Kind = VarKind
	case rpast.Pkg:
		result.Kind = ImportKind
		result.Type = nil
		if typ.Node != nil {
			result.Value = typ.Node.(*rpast.ImportSpec).Path.Value
		} else {
			result.Kind = PathKind
			result.Value = obj.Data.(string)
		}
	case rpast.Con:
		result.Kind = ConstKind
		if decl, ok := obj.Decl.(*rpast.ValueSpec); ok {
			result.Value = decl.Values[0]
		}
	case rpast.Lbl:
		result.Kind = LabelKind
		result.Type = nil
	case rpast.Typ:
		result.Kind = TypeKind
		result.Type = typ.Underlying(false)
	}
	for child := range typ.Iter() {
		m, err := adaptRPObject(child, rptypes.Type{})
		if err != nil {
			return nil, err
		}
		result.Members = append(result.Members, m)
	}
	sort.Sort(orderedObjects(result.Members))
	return result, nil
}
func adaptGoObject(fset *gotoken.FileSet, obj gotypes.Object) (*Object, error) {
	result := &Object{
		Name:     obj.Name(),
		Position: objToPos(fset, obj),
		Type:     obj.Type(),
	}
	switch obj := obj.(type) {
	case *gotypes.Func:
		result.Kind = FuncKind
	case *gotypes.Var:
		result.Kind = VarKind
	case *gotypes.PkgName:
		result.Kind = ImportKind
		result.Type = nil
		if obj.Pkg() != nil {
			result.Value = strconv.Quote(obj.Imported().Path())
		} else {
			result.Value = obj.Imported().Path()
			result.Kind = PathKind
		}
	case *gotypes.Const:
		result.Kind = ConstKind
		result.Value = obj.Val()
	case *gotypes.Label:
		result.Kind = LabelKind
		result.Type = nil
	case *gotypes.TypeName:
		result.Kind = TypeKind
		result.Type = obj.Type().Underlying()
	default:
		result.Kind = BadKind
	}
	return result, nil
}
func objToPos(fSet *gotoken.FileSet, obj gotypes.Object) Position {
	p := obj.Pos()
	f := fSet.File(p)
	goPos := f.Position(p)
	pos := Position{
		Filename: cleanFilename(goPos.Filename),
		Line:     goPos.Line,
		Column:   goPos.Column,
	}
	if pos.Column != 1 {
		return pos
	}
	named, ok := obj.(interface{ Name() string })
	if !ok {
		return pos
	}
	in, err := os.Open(f.Name())
	if err != nil {
		return pos
	}
	for l, scanner := 1, bufio.NewScanner(in); scanner.Scan(); l++ {
		if l < pos.Line {
			continue
		}
		col := bytes.Index([]byte(scanner.Text()), []byte(named.Name()))
		if col >= 0 {
			pos.Column = col + 1
		}
		break
	}
	return pos
}