func (c *Suggester) analyzePackage(importer types.Importer, filename string, data []byte, cursor int) (*token.FileSet, token.Pos, *types.Package) { // If we're in trailing white space at the end of a scope, // sometimes go/types doesn't recognize that variables should // still be in scope there. filesemi := bytes.Join([][]byte{data[:cursor], []byte(";"), data[cursor:]}, nil) fset := token.NewFileSet() fileAST, err := parser.ParseFile(fset, filename, filesemi, parser.AllErrors) if err != nil && c.debug { logParseError("Error parsing input file (outer block)", err) } pos := fset.File(fileAST.Pos()).Pos(cursor) var otherASTs []*ast.File for _, otherName := range c.findOtherPackageFiles(filename, fileAST.Name.Name) { ast, err := parser.ParseFile(fset, otherName, nil, 0) if err != nil && c.debug { logParseError("Error parsing other file", err) } otherASTs = append(otherASTs, ast) } var cfg types.Config cfg.Importer = importer cfg.Error = func(err error) {} var info types.Info info.Scopes = make(map[ast.Node]*types.Scope) pkg, _ := cfg.Check("", fset, append(otherASTs, fileAST), &info) // Workaround golang.org/issue/15686. for node, scope := range info.Scopes { switch node := node.(type) { case *ast.RangeStmt: for _, name := range scope.Names() { setScopePos(scope.Lookup(name).(*types.Var), node.X.End()) } } } return fset, pos, pkg }
// NewPackage returns a new Package struct, which can be // used to generate code related to the package. The package // might be given as either an absolute path or an import path. // If the package can't be found or the package is not compilable, // this function returns an error. func NewPackage(path string) (*Package, error) { p := &_package{Path: path, fset: token.NewFileSet()} pkg, err := findPackage(path) if err != nil { return nil, fmt.Errorf("could not find package: %s", err) } fileNames := packageFiles(pkg) if len(fileNames) == 0 { return nil, fmt.Errorf("no go files") } p.astFiles = make([]*ast.File, len(fileNames)) p.files = make(map[string]*file, len(fileNames)) for ii, v := range fileNames { f, err := parseFile(p.fset, v) if err != nil { return nil, fmt.Errorf("could not parse %s: %s", v, err) } p.files[v] = f p.astFiles[ii] = f.ast } context := &types.Config{ IgnoreFuncBodies: true, FakeImportC: true, Error: errorHandler, } ipath := pkg.ImportPath if ipath == "." { // Check won't accept a "." import abs, err := filepath.Abs(pkg.Dir) if err != nil { return nil, err } for _, v := range strings.Split(build.Default.GOPATH, ":") { src := filepath.Join(v, "src") if strings.HasPrefix(abs, src) { ipath = abs[len(src)+1:] break } } } var info types.Info info.Types = make(map[ast.Expr]types.TypeAndValue) info.Defs = make(map[*ast.Ident]types.Object) info.Uses = make(map[*ast.Ident]types.Object) info.Implicits = make(map[ast.Node]types.Object) info.Selections = make(map[*ast.SelectorExpr]*types.Selection) info.Scopes = make(map[ast.Node]*types.Scope) tpkg, err := context.Check(ipath, p.fset, p.astFiles, &info) if err != nil { // This error is caused by using fields in C structs, ignore it if !strings.Contains(err.Error(), "invalid type") { return nil, fmt.Errorf("error checking package: %s", err) } } return &Package{ Package: tpkg, dir: pkg.Dir, pkg: p, info: &info, }, nil }