loader.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package loader
  2. import (
  3. "fmt"
  4. "go/ast"
  5. "go/parser"
  6. "go/scanner"
  7. "go/token"
  8. "go/types"
  9. "log"
  10. "os"
  11. "sync"
  12. "golang.org/x/tools/go/gcexportdata"
  13. "golang.org/x/tools/go/packages"
  14. )
  15. type Loader struct {
  16. exportMu sync.RWMutex
  17. }
  18. // Graph resolves patterns and returns packages with all the
  19. // information required to later load type information, and optionally
  20. // syntax trees.
  21. //
  22. // The provided config can set any setting with the exception of Mode.
  23. func (ld *Loader) 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...)
  26. if err != nil {
  27. return nil, err
  28. }
  29. fset := token.NewFileSet()
  30. packages.Visit(pkgs, nil, func(pkg *packages.Package) {
  31. pkg.Fset = fset
  32. })
  33. return pkgs, nil
  34. }
  35. // LoadFromExport loads a package from export data. All of its
  36. // dependencies must have been loaded already.
  37. func (ld *Loader) LoadFromExport(pkg *packages.Package) error {
  38. ld.exportMu.Lock()
  39. defer ld.exportMu.Unlock()
  40. pkg.IllTyped = true
  41. for path, pkg := range pkg.Imports {
  42. if pkg.Types == nil {
  43. return fmt.Errorf("dependency %q hasn't been loaded yet", path)
  44. }
  45. }
  46. if pkg.ExportFile == "" {
  47. return fmt.Errorf("no export data for %q", pkg.ID)
  48. }
  49. f, err := os.Open(pkg.ExportFile)
  50. if err != nil {
  51. return err
  52. }
  53. defer f.Close()
  54. r, err := gcexportdata.NewReader(f)
  55. if err != nil {
  56. return err
  57. }
  58. view := make(map[string]*types.Package) // view seen by gcexportdata
  59. seen := make(map[*packages.Package]bool) // all visited packages
  60. var visit func(pkgs map[string]*packages.Package)
  61. visit = func(pkgs map[string]*packages.Package) {
  62. for _, pkg := range pkgs {
  63. if !seen[pkg] {
  64. seen[pkg] = true
  65. view[pkg.PkgPath] = pkg.Types
  66. visit(pkg.Imports)
  67. }
  68. }
  69. }
  70. visit(pkg.Imports)
  71. tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath)
  72. if err != nil {
  73. return err
  74. }
  75. pkg.Types = tpkg
  76. pkg.IllTyped = false
  77. return nil
  78. }
  79. // LoadFromSource loads a package from source. All of its dependencies
  80. // must have been loaded already.
  81. func (ld *Loader) LoadFromSource(pkg *packages.Package) error {
  82. ld.exportMu.RLock()
  83. defer ld.exportMu.RUnlock()
  84. pkg.IllTyped = true
  85. pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
  86. // OPT(dh): many packages have few files, much fewer than there
  87. // are CPU cores. Additionally, parsing each individual file is
  88. // very fast. A naive parallel implementation of this loop won't
  89. // be faster, and tends to be slower due to extra scheduling,
  90. // bookkeeping and potentially false sharing of cache lines.
  91. pkg.Syntax = make([]*ast.File, len(pkg.CompiledGoFiles))
  92. for i, file := range pkg.CompiledGoFiles {
  93. f, err := parser.ParseFile(pkg.Fset, file, nil, parser.ParseComments)
  94. if err != nil {
  95. pkg.Errors = append(pkg.Errors, convertError(err)...)
  96. return err
  97. }
  98. pkg.Syntax[i] = f
  99. }
  100. pkg.TypesInfo = &types.Info{
  101. Types: make(map[ast.Expr]types.TypeAndValue),
  102. Defs: make(map[*ast.Ident]types.Object),
  103. Uses: make(map[*ast.Ident]types.Object),
  104. Implicits: make(map[ast.Node]types.Object),
  105. Scopes: make(map[ast.Node]*types.Scope),
  106. Selections: make(map[*ast.SelectorExpr]*types.Selection),
  107. }
  108. importer := func(path string) (*types.Package, error) {
  109. if path == "unsafe" {
  110. return types.Unsafe, nil
  111. }
  112. imp := pkg.Imports[path]
  113. if imp == nil {
  114. return nil, nil
  115. }
  116. if len(imp.Errors) > 0 {
  117. return nil, imp.Errors[0]
  118. }
  119. return imp.Types, nil
  120. }
  121. tc := &types.Config{
  122. Importer: importerFunc(importer),
  123. Error: func(err error) {
  124. pkg.Errors = append(pkg.Errors, convertError(err)...)
  125. },
  126. }
  127. err := types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)
  128. if err != nil {
  129. return err
  130. }
  131. pkg.IllTyped = false
  132. return nil
  133. }
  134. func convertError(err error) []packages.Error {
  135. var errs []packages.Error
  136. // taken from go/packages
  137. switch err := err.(type) {
  138. case packages.Error:
  139. // from driver
  140. errs = append(errs, err)
  141. case *os.PathError:
  142. // from parser
  143. errs = append(errs, packages.Error{
  144. Pos: err.Path + ":1",
  145. Msg: err.Err.Error(),
  146. Kind: packages.ParseError,
  147. })
  148. case scanner.ErrorList:
  149. // from parser
  150. for _, err := range err {
  151. errs = append(errs, packages.Error{
  152. Pos: err.Pos.String(),
  153. Msg: err.Msg,
  154. Kind: packages.ParseError,
  155. })
  156. }
  157. case types.Error:
  158. // from type checker
  159. errs = append(errs, packages.Error{
  160. Pos: err.Fset.Position(err.Pos).String(),
  161. Msg: err.Msg,
  162. Kind: packages.TypeError,
  163. })
  164. default:
  165. // unexpected impoverished error from parser?
  166. errs = append(errs, packages.Error{
  167. Pos: "-",
  168. Msg: err.Error(),
  169. Kind: packages.UnknownError,
  170. })
  171. // If you see this error message, please file a bug.
  172. log.Printf("internal error: error %q (%T) without position", err, err)
  173. }
  174. return errs
  175. }
  176. type importerFunc func(path string) (*types.Package, error)
  177. func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }