14 "golang.org/x/tools/go/gcexportdata"
15 "golang.org/x/tools/go/packages"
18 // Graph resolves patterns and returns packages with all the
19 // information required to later load type information, and optionally
22 // The provided config can set any setting with the exception of Mode.
23 func Graph(cfg packages.Config, patterns ...string) ([]*packages.Package, error) {
24 cfg.Mode = packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedTypesSizes
25 pkgs, err := packages.Load(&cfg, patterns...)
29 fset := token.NewFileSet()
30 packages.Visit(pkgs, nil, func(pkg *packages.Package) {
35 for _, pkg := range pkgs {
36 if len(pkg.CompiledGoFiles) == 0 && len(pkg.Errors) == 0 && pkg.PkgPath != "unsafe" {
37 // If a package consists only of test files, then
38 // go/packages incorrectly(?) returns an empty package for
39 // the non-test variant. Get rid of those packages. See
42 // Do not, however, skip packages that have errors. Those,
43 // too, may have no files, but we want to print the
53 // LoadFromExport loads a package from export data. All of its
54 // dependencies must have been loaded already.
55 func LoadFromExport(pkg *packages.Package) error {
57 for path, pkg := range pkg.Imports {
59 return fmt.Errorf("dependency %q hasn't been loaded yet", path)
62 if pkg.ExportFile == "" {
63 return fmt.Errorf("no export data for %q", pkg.ID)
65 f, err := os.Open(pkg.ExportFile)
71 r, err := gcexportdata.NewReader(f)
76 view := make(map[string]*types.Package) // view seen by gcexportdata
77 seen := make(map[*packages.Package]bool) // all visited packages
78 var visit func(pkgs map[string]*packages.Package)
79 visit = func(pkgs map[string]*packages.Package) {
80 for _, pkg := range pkgs {
83 view[pkg.PkgPath] = pkg.Types
89 tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath)
98 // LoadFromSource loads a package from source. All of its dependencies
99 // must have been loaded already.
100 func LoadFromSource(pkg *packages.Package) error {
102 pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
104 // OPT(dh): many packages have few files, much fewer than there
105 // are CPU cores. Additionally, parsing each individual file is
106 // very fast. A naive parallel implementation of this loop won't
107 // be faster, and tends to be slower due to extra scheduling,
108 // bookkeeping and potentially false sharing of cache lines.
109 pkg.Syntax = make([]*ast.File, len(pkg.CompiledGoFiles))
110 for i, file := range pkg.CompiledGoFiles {
111 f, err := parser.ParseFile(pkg.Fset, file, nil, parser.ParseComments)
113 pkg.Errors = append(pkg.Errors, convertError(err)...)
118 pkg.TypesInfo = &types.Info{
119 Types: make(map[ast.Expr]types.TypeAndValue),
120 Defs: make(map[*ast.Ident]types.Object),
121 Uses: make(map[*ast.Ident]types.Object),
122 Implicits: make(map[ast.Node]types.Object),
123 Scopes: make(map[ast.Node]*types.Scope),
124 Selections: make(map[*ast.SelectorExpr]*types.Selection),
127 importer := func(path string) (*types.Package, error) {
128 if path == "unsafe" {
129 return types.Unsafe, nil
132 // go/packages doesn't tell us that cgo preprocessing
133 // failed. When we subsequently try to parse the package,
134 // we'll encounter the raw C import.
135 return nil, errors.New("cgo preprocessing failed")
137 imp := pkg.Imports[path]
141 if len(imp.Errors) > 0 {
142 return nil, imp.Errors[0]
144 return imp.Types, nil
147 Importer: importerFunc(importer),
148 Error: func(err error) {
149 pkg.Errors = append(pkg.Errors, convertError(err)...)
152 err := types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)
160 func convertError(err error) []packages.Error {
161 var errs []packages.Error
162 // taken from go/packages
163 switch err := err.(type) {
166 errs = append(errs, err)
170 errs = append(errs, packages.Error{
171 Pos: err.Path + ":1",
172 Msg: err.Err.Error(),
173 Kind: packages.ParseError,
176 case scanner.ErrorList:
178 for _, err := range err {
179 errs = append(errs, packages.Error{
180 Pos: err.Pos.String(),
182 Kind: packages.ParseError,
188 errs = append(errs, packages.Error{
189 Pos: err.Fset.Position(err.Pos).String(),
191 Kind: packages.TypeError,
195 // unexpected impoverished error from parser?
196 errs = append(errs, packages.Error{
199 Kind: packages.UnknownError,
202 // If you see this error message, please file a bug.
203 log.Printf("internal error: error %q (%T) without position", err, err)
208 type importerFunc func(path string) (*types.Package, error)
210 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }