parse.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package parser
  14. import (
  15. "fmt"
  16. "go/ast"
  17. "go/build"
  18. "go/parser"
  19. "go/token"
  20. tc "go/types"
  21. "io/ioutil"
  22. "os"
  23. "os/exec"
  24. "path"
  25. "path/filepath"
  26. "sort"
  27. "strings"
  28. "k8s.io/gengo/types"
  29. "k8s.io/klog"
  30. )
  31. // This clarifies when a pkg path has been canonicalized.
  32. type importPathString string
  33. // Builder lets you add all the go files in all the packages that you care
  34. // about, then constructs the type source data.
  35. type Builder struct {
  36. context *build.Context
  37. // If true, include *_test.go
  38. IncludeTestFiles bool
  39. // Map of package names to more canonical information about the package.
  40. // This might hold the same value for multiple names, e.g. if someone
  41. // referenced ./pkg/name or in the case of vendoring, which canonicalizes
  42. // differently that what humans would type.
  43. buildPackages map[string]*build.Package
  44. fset *token.FileSet
  45. // map of package path to list of parsed files
  46. parsed map[importPathString][]parsedFile
  47. // map of package path to absolute path (to prevent overlap)
  48. absPaths map[importPathString]string
  49. // Set by typeCheckPackage(), used by importPackage() and friends.
  50. typeCheckedPackages map[importPathString]*tc.Package
  51. // Map of package path to whether the user requested it or it was from
  52. // an import.
  53. userRequested map[importPathString]bool
  54. // All comments from everywhere in every parsed file.
  55. endLineToCommentGroup map[fileLine]*ast.CommentGroup
  56. // map of package to list of packages it imports.
  57. importGraph map[importPathString]map[string]struct{}
  58. }
  59. // parsedFile is for tracking files with name
  60. type parsedFile struct {
  61. name string
  62. file *ast.File
  63. }
  64. // key type for finding comments.
  65. type fileLine struct {
  66. file string
  67. line int
  68. }
  69. // New constructs a new builder.
  70. func New() *Builder {
  71. c := build.Default
  72. if c.GOROOT == "" {
  73. if p, err := exec.Command("which", "go").CombinedOutput(); err == nil {
  74. // The returned string will have some/path/bin/go, so remove the last two elements.
  75. c.GOROOT = filepath.Dir(filepath.Dir(strings.Trim(string(p), "\n")))
  76. } else {
  77. klog.Warningf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err)
  78. }
  79. }
  80. // Force this to off, since we don't properly parse CGo. All symbols must
  81. // have non-CGo equivalents.
  82. c.CgoEnabled = false
  83. return &Builder{
  84. context: &c,
  85. buildPackages: map[string]*build.Package{},
  86. typeCheckedPackages: map[importPathString]*tc.Package{},
  87. fset: token.NewFileSet(),
  88. parsed: map[importPathString][]parsedFile{},
  89. absPaths: map[importPathString]string{},
  90. userRequested: map[importPathString]bool{},
  91. endLineToCommentGroup: map[fileLine]*ast.CommentGroup{},
  92. importGraph: map[importPathString]map[string]struct{}{},
  93. }
  94. }
  95. // AddBuildTags adds the specified build tags to the parse context.
  96. func (b *Builder) AddBuildTags(tags ...string) {
  97. b.context.BuildTags = append(b.context.BuildTags, tags...)
  98. }
  99. // Get package information from the go/build package. Automatically excludes
  100. // e.g. test files and files for other platforms-- there is quite a bit of
  101. // logic of that nature in the build package.
  102. func (b *Builder) importBuildPackage(dir string) (*build.Package, error) {
  103. if buildPkg, ok := b.buildPackages[dir]; ok {
  104. return buildPkg, nil
  105. }
  106. // This validates the `package foo // github.com/bar/foo` comments.
  107. buildPkg, err := b.importWithMode(dir, build.ImportComment)
  108. if err != nil {
  109. if _, ok := err.(*build.NoGoError); !ok {
  110. return nil, fmt.Errorf("unable to import %q: %v", dir, err)
  111. }
  112. }
  113. if buildPkg == nil {
  114. // Might be an empty directory. Try to just find the dir.
  115. buildPkg, err = b.importWithMode(dir, build.FindOnly)
  116. if err != nil {
  117. return nil, err
  118. }
  119. }
  120. // Remember it under the user-provided name.
  121. klog.V(5).Infof("saving buildPackage %s", dir)
  122. b.buildPackages[dir] = buildPkg
  123. canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
  124. if dir != string(canonicalPackage) {
  125. // Since `dir` is not the canonical name, see if we knew it under another name.
  126. if buildPkg, ok := b.buildPackages[string(canonicalPackage)]; ok {
  127. return buildPkg, nil
  128. }
  129. // Must be new, save it under the canonical name, too.
  130. klog.V(5).Infof("saving buildPackage %s", canonicalPackage)
  131. b.buildPackages[string(canonicalPackage)] = buildPkg
  132. }
  133. return buildPkg, nil
  134. }
  135. // AddFileForTest adds a file to the set, without verifying that the provided
  136. // pkg actually exists on disk. The pkg must be of the form "canonical/pkg/path"
  137. // and the path must be the absolute path to the file. Because this bypasses
  138. // the normal recursive finding of package dependencies (on disk), test should
  139. // sort their test files topologically first, so all deps are resolved by the
  140. // time we need them.
  141. func (b *Builder) AddFileForTest(pkg string, path string, src []byte) error {
  142. if err := b.addFile(importPathString(pkg), path, src, true); err != nil {
  143. return err
  144. }
  145. if _, err := b.typeCheckPackage(importPathString(pkg)); err != nil {
  146. return err
  147. }
  148. return nil
  149. }
  150. // addFile adds a file to the set. The pkgPath must be of the form
  151. // "canonical/pkg/path" and the path must be the absolute path to the file. A
  152. // flag indicates whether this file was user-requested or just from following
  153. // the import graph.
  154. func (b *Builder) addFile(pkgPath importPathString, path string, src []byte, userRequested bool) error {
  155. for _, p := range b.parsed[pkgPath] {
  156. if path == p.name {
  157. klog.V(5).Infof("addFile %s %s already parsed, skipping", pkgPath, path)
  158. return nil
  159. }
  160. }
  161. klog.V(6).Infof("addFile %s %s", pkgPath, path)
  162. p, err := parser.ParseFile(b.fset, path, src, parser.DeclarationErrors|parser.ParseComments)
  163. if err != nil {
  164. return err
  165. }
  166. // This is redundant with addDir, but some tests call AddFileForTest, which
  167. // call into here without calling addDir.
  168. b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
  169. b.parsed[pkgPath] = append(b.parsed[pkgPath], parsedFile{path, p})
  170. for _, c := range p.Comments {
  171. position := b.fset.Position(c.End())
  172. b.endLineToCommentGroup[fileLine{position.Filename, position.Line}] = c
  173. }
  174. // We have to get the packages from this specific file, in case the
  175. // user added individual files instead of entire directories.
  176. if b.importGraph[pkgPath] == nil {
  177. b.importGraph[pkgPath] = map[string]struct{}{}
  178. }
  179. for _, im := range p.Imports {
  180. importedPath := strings.Trim(im.Path.Value, `"`)
  181. b.importGraph[pkgPath][importedPath] = struct{}{}
  182. }
  183. return nil
  184. }
  185. // AddDir adds an entire directory, scanning it for go files. 'dir' should have
  186. // a single go package in it. GOPATH, GOROOT, and the location of your go
  187. // binary (`which go`) will all be searched if dir doesn't literally resolve.
  188. func (b *Builder) AddDir(dir string) error {
  189. _, err := b.importPackage(dir, true)
  190. return err
  191. }
  192. // AddDirRecursive is just like AddDir, but it also recursively adds
  193. // subdirectories; it returns an error only if the path couldn't be resolved;
  194. // any directories recursed into without go source are ignored.
  195. func (b *Builder) AddDirRecursive(dir string) error {
  196. // Add the root.
  197. if _, err := b.importPackage(dir, true); err != nil {
  198. klog.Warningf("Ignoring directory %v: %v", dir, err)
  199. }
  200. // filepath.Walk does not follow symlinks. We therefore evaluate symlinks and use that with
  201. // filepath.Walk.
  202. realPath, err := filepath.EvalSymlinks(b.buildPackages[dir].Dir)
  203. if err != nil {
  204. return err
  205. }
  206. fn := func(filePath string, info os.FileInfo, err error) error {
  207. if info != nil && info.IsDir() {
  208. rel := filepath.ToSlash(strings.TrimPrefix(filePath, realPath))
  209. if rel != "" {
  210. // Make a pkg path.
  211. pkg := path.Join(string(canonicalizeImportPath(b.buildPackages[dir].ImportPath)), rel)
  212. // Add it.
  213. if _, err := b.importPackage(pkg, true); err != nil {
  214. klog.Warningf("Ignoring child directory %v: %v", pkg, err)
  215. }
  216. }
  217. }
  218. return nil
  219. }
  220. if err := filepath.Walk(realPath, fn); err != nil {
  221. return err
  222. }
  223. return nil
  224. }
  225. // AddDirTo adds an entire directory to a given Universe. Unlike AddDir, this
  226. // processes the package immediately, which makes it safe to use from within a
  227. // generator (rather than just at init time. 'dir' must be a single go package.
  228. // GOPATH, GOROOT, and the location of your go binary (`which go`) will all be
  229. // searched if dir doesn't literally resolve.
  230. // Deprecated. Please use AddDirectoryTo.
  231. func (b *Builder) AddDirTo(dir string, u *types.Universe) error {
  232. // We want all types from this package, as if they were directly added
  233. // by the user. They WERE added by the user, in effect.
  234. if _, err := b.importPackage(dir, true); err != nil {
  235. return err
  236. }
  237. return b.findTypesIn(canonicalizeImportPath(b.buildPackages[dir].ImportPath), u)
  238. }
  239. // AddDirectoryTo adds an entire directory to a given Universe. Unlike AddDir,
  240. // this processes the package immediately, which makes it safe to use from
  241. // within a generator (rather than just at init time. 'dir' must be a single go
  242. // package. GOPATH, GOROOT, and the location of your go binary (`which go`)
  243. // will all be searched if dir doesn't literally resolve.
  244. func (b *Builder) AddDirectoryTo(dir string, u *types.Universe) (*types.Package, error) {
  245. // We want all types from this package, as if they were directly added
  246. // by the user. They WERE added by the user, in effect.
  247. if _, err := b.importPackage(dir, true); err != nil {
  248. return nil, err
  249. }
  250. path := canonicalizeImportPath(b.buildPackages[dir].ImportPath)
  251. if err := b.findTypesIn(path, u); err != nil {
  252. return nil, err
  253. }
  254. return u.Package(string(path)), nil
  255. }
  256. // The implementation of AddDir. A flag indicates whether this directory was
  257. // user-requested or just from following the import graph.
  258. func (b *Builder) addDir(dir string, userRequested bool) error {
  259. klog.V(5).Infof("addDir %s", dir)
  260. buildPkg, err := b.importBuildPackage(dir)
  261. if err != nil {
  262. return err
  263. }
  264. canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
  265. pkgPath := canonicalPackage
  266. if dir != string(canonicalPackage) {
  267. klog.V(5).Infof("addDir %s, canonical path is %s", dir, pkgPath)
  268. }
  269. // Sanity check the pkg dir has not changed.
  270. if prev, found := b.absPaths[pkgPath]; found {
  271. if buildPkg.Dir != prev {
  272. return fmt.Errorf("package %q (%s) previously resolved to %s", pkgPath, buildPkg.Dir, prev)
  273. }
  274. } else {
  275. b.absPaths[pkgPath] = buildPkg.Dir
  276. }
  277. files := []string{}
  278. files = append(files, buildPkg.GoFiles...)
  279. if b.IncludeTestFiles {
  280. files = append(files, buildPkg.TestGoFiles...)
  281. }
  282. for _, file := range files {
  283. if !strings.HasSuffix(file, ".go") {
  284. continue
  285. }
  286. absPath := filepath.Join(buildPkg.Dir, file)
  287. data, err := ioutil.ReadFile(absPath)
  288. if err != nil {
  289. return fmt.Errorf("while loading %q: %v", absPath, err)
  290. }
  291. err = b.addFile(pkgPath, absPath, data, userRequested)
  292. if err != nil {
  293. return fmt.Errorf("while parsing %q: %v", absPath, err)
  294. }
  295. }
  296. return nil
  297. }
  298. // importPackage is a function that will be called by the type check package when it
  299. // needs to import a go package. 'path' is the import path.
  300. func (b *Builder) importPackage(dir string, userRequested bool) (*tc.Package, error) {
  301. klog.V(5).Infof("importPackage %s", dir)
  302. var pkgPath = importPathString(dir)
  303. // Get the canonical path if we can.
  304. if buildPkg := b.buildPackages[dir]; buildPkg != nil {
  305. canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
  306. klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
  307. pkgPath = canonicalPackage
  308. }
  309. // If we have not seen this before, process it now.
  310. ignoreError := false
  311. if _, found := b.parsed[pkgPath]; !found {
  312. // Ignore errors in paths that we're importing solely because
  313. // they're referenced by other packages.
  314. ignoreError = true
  315. // Add it.
  316. if err := b.addDir(dir, userRequested); err != nil {
  317. return nil, err
  318. }
  319. // Get the canonical path now that it has been added.
  320. if buildPkg := b.buildPackages[dir]; buildPkg != nil {
  321. canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
  322. klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
  323. pkgPath = canonicalPackage
  324. }
  325. }
  326. // If it was previously known, just check that the user-requestedness hasn't
  327. // changed.
  328. b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
  329. // Run the type checker. We may end up doing this to pkgs that are already
  330. // done, or are in the queue to be done later, but it will short-circuit,
  331. // and we can't miss pkgs that are only depended on.
  332. pkg, err := b.typeCheckPackage(pkgPath)
  333. if err != nil {
  334. switch {
  335. case ignoreError && pkg != nil:
  336. klog.V(2).Infof("type checking encountered some issues in %q, but ignoring.\n", pkgPath)
  337. case !ignoreError && pkg != nil:
  338. klog.V(2).Infof("type checking encountered some errors in %q\n", pkgPath)
  339. return nil, err
  340. default:
  341. return nil, err
  342. }
  343. }
  344. return pkg, nil
  345. }
  346. type importAdapter struct {
  347. b *Builder
  348. }
  349. func (a importAdapter) Import(path string) (*tc.Package, error) {
  350. return a.b.importPackage(path, false)
  351. }
  352. // typeCheckPackage will attempt to return the package even if there are some
  353. // errors, so you may check whether the package is nil or not even if you get
  354. // an error.
  355. func (b *Builder) typeCheckPackage(pkgPath importPathString) (*tc.Package, error) {
  356. klog.V(5).Infof("typeCheckPackage %s", pkgPath)
  357. if pkg, ok := b.typeCheckedPackages[pkgPath]; ok {
  358. if pkg != nil {
  359. klog.V(6).Infof("typeCheckPackage %s already done", pkgPath)
  360. return pkg, nil
  361. }
  362. // We store a nil right before starting work on a package. So
  363. // if we get here and it's present and nil, that means there's
  364. // another invocation of this function on the call stack
  365. // already processing this package.
  366. return nil, fmt.Errorf("circular dependency for %q", pkgPath)
  367. }
  368. parsedFiles, ok := b.parsed[pkgPath]
  369. if !ok {
  370. return nil, fmt.Errorf("No files for pkg %q", pkgPath)
  371. }
  372. files := make([]*ast.File, len(parsedFiles))
  373. for i := range parsedFiles {
  374. files[i] = parsedFiles[i].file
  375. }
  376. b.typeCheckedPackages[pkgPath] = nil
  377. c := tc.Config{
  378. IgnoreFuncBodies: true,
  379. // Note that importAdapter can call b.importPackage which calls this
  380. // method. So there can't be cycles in the import graph.
  381. Importer: importAdapter{b},
  382. Error: func(err error) {
  383. klog.V(2).Infof("type checker: %v\n", err)
  384. },
  385. }
  386. pkg, err := c.Check(string(pkgPath), b.fset, files, nil)
  387. b.typeCheckedPackages[pkgPath] = pkg // record the result whether or not there was an error
  388. return pkg, err
  389. }
  390. // FindPackages fetches a list of the user-imported packages.
  391. // Note that you need to call b.FindTypes() first.
  392. func (b *Builder) FindPackages() []string {
  393. // Iterate packages in a predictable order.
  394. pkgPaths := []string{}
  395. for k := range b.typeCheckedPackages {
  396. pkgPaths = append(pkgPaths, string(k))
  397. }
  398. sort.Strings(pkgPaths)
  399. result := []string{}
  400. for _, pkgPath := range pkgPaths {
  401. if b.userRequested[importPathString(pkgPath)] {
  402. // Since walkType is recursive, all types that are in packages that
  403. // were directly mentioned will be included. We don't need to
  404. // include all types in all transitive packages, though.
  405. result = append(result, pkgPath)
  406. }
  407. }
  408. return result
  409. }
  410. // FindTypes finalizes the package imports, and searches through all the
  411. // packages for types.
  412. func (b *Builder) FindTypes() (types.Universe, error) {
  413. // Take a snapshot of pkgs to iterate, since this will recursively mutate
  414. // b.parsed. Iterate in a predictable order.
  415. pkgPaths := []string{}
  416. for pkgPath := range b.parsed {
  417. pkgPaths = append(pkgPaths, string(pkgPath))
  418. }
  419. sort.Strings(pkgPaths)
  420. u := types.Universe{}
  421. for _, pkgPath := range pkgPaths {
  422. if err := b.findTypesIn(importPathString(pkgPath), &u); err != nil {
  423. return nil, err
  424. }
  425. }
  426. return u, nil
  427. }
  428. // findTypesIn finalizes the package import and searches through the package
  429. // for types.
  430. func (b *Builder) findTypesIn(pkgPath importPathString, u *types.Universe) error {
  431. klog.V(5).Infof("findTypesIn %s", pkgPath)
  432. pkg := b.typeCheckedPackages[pkgPath]
  433. if pkg == nil {
  434. return fmt.Errorf("findTypesIn(%s): package is not known", pkgPath)
  435. }
  436. if !b.userRequested[pkgPath] {
  437. // Since walkType is recursive, all types that the
  438. // packages they asked for depend on will be included.
  439. // But we don't need to include all types in all
  440. // *packages* they depend on.
  441. klog.V(5).Infof("findTypesIn %s: package is not user requested", pkgPath)
  442. return nil
  443. }
  444. // We're keeping this package. This call will create the record.
  445. u.Package(string(pkgPath)).Name = pkg.Name()
  446. u.Package(string(pkgPath)).Path = pkg.Path()
  447. u.Package(string(pkgPath)).SourcePath = b.absPaths[pkgPath]
  448. for _, f := range b.parsed[pkgPath] {
  449. if _, fileName := filepath.Split(f.name); fileName == "doc.go" {
  450. tp := u.Package(string(pkgPath))
  451. // findTypesIn might be called multiple times. Clean up tp.Comments
  452. // to avoid repeatedly fill same comments to it.
  453. tp.Comments = []string{}
  454. for i := range f.file.Comments {
  455. tp.Comments = append(tp.Comments, splitLines(f.file.Comments[i].Text())...)
  456. }
  457. if f.file.Doc != nil {
  458. tp.DocComments = splitLines(f.file.Doc.Text())
  459. }
  460. }
  461. }
  462. s := pkg.Scope()
  463. for _, n := range s.Names() {
  464. obj := s.Lookup(n)
  465. tn, ok := obj.(*tc.TypeName)
  466. if ok {
  467. t := b.walkType(*u, nil, tn.Type())
  468. c1 := b.priorCommentLines(obj.Pos(), 1)
  469. // c1.Text() is safe if c1 is nil
  470. t.CommentLines = splitLines(c1.Text())
  471. if c1 == nil {
  472. t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text())
  473. } else {
  474. t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text())
  475. }
  476. }
  477. tf, ok := obj.(*tc.Func)
  478. // We only care about functions, not concrete/abstract methods.
  479. if ok && tf.Type() != nil && tf.Type().(*tc.Signature).Recv() == nil {
  480. t := b.addFunction(*u, nil, tf)
  481. c1 := b.priorCommentLines(obj.Pos(), 1)
  482. // c1.Text() is safe if c1 is nil
  483. t.CommentLines = splitLines(c1.Text())
  484. if c1 == nil {
  485. t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text())
  486. } else {
  487. t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text())
  488. }
  489. }
  490. tv, ok := obj.(*tc.Var)
  491. if ok && !tv.IsField() {
  492. b.addVariable(*u, nil, tv)
  493. }
  494. tconst, ok := obj.(*tc.Const)
  495. if ok {
  496. b.addConstant(*u, nil, tconst)
  497. }
  498. }
  499. importedPkgs := []string{}
  500. for k := range b.importGraph[pkgPath] {
  501. importedPkgs = append(importedPkgs, string(k))
  502. }
  503. sort.Strings(importedPkgs)
  504. for _, p := range importedPkgs {
  505. u.AddImports(string(pkgPath), p)
  506. }
  507. return nil
  508. }
  509. func (b *Builder) importWithMode(dir string, mode build.ImportMode) (*build.Package, error) {
  510. // This is a bit of a hack. The srcDir argument to Import() should
  511. // properly be the dir of the file which depends on the package to be
  512. // imported, so that vendoring can work properly and local paths can
  513. // resolve. We assume that there is only one level of vendoring, and that
  514. // the CWD is inside the GOPATH, so this should be safe. Nobody should be
  515. // using local (relative) paths except on the CLI, so CWD is also
  516. // sufficient.
  517. cwd, err := os.Getwd()
  518. if err != nil {
  519. return nil, fmt.Errorf("unable to get current directory: %v", err)
  520. }
  521. buildPkg, err := b.context.Import(dir, cwd, mode)
  522. if err != nil {
  523. return nil, err
  524. }
  525. return buildPkg, nil
  526. }
  527. // if there's a comment on the line `lines` before pos, return its text, otherwise "".
  528. func (b *Builder) priorCommentLines(pos token.Pos, lines int) *ast.CommentGroup {
  529. position := b.fset.Position(pos)
  530. key := fileLine{position.Filename, position.Line - lines}
  531. return b.endLineToCommentGroup[key]
  532. }
  533. func splitLines(str string) []string {
  534. return strings.Split(strings.TrimRight(str, "\n"), "\n")
  535. }
  536. func tcFuncNameToName(in string) types.Name {
  537. name := strings.TrimPrefix(in, "func ")
  538. nameParts := strings.Split(name, "(")
  539. return tcNameToName(nameParts[0])
  540. }
  541. func tcVarNameToName(in string) types.Name {
  542. nameParts := strings.Split(in, " ")
  543. // nameParts[0] is "var".
  544. // nameParts[2:] is the type of the variable, we ignore it for now.
  545. return tcNameToName(nameParts[1])
  546. }
  547. func tcNameToName(in string) types.Name {
  548. // Detect anonymous type names. (These may have '.' characters because
  549. // embedded types may have packages, so we detect them specially.)
  550. if strings.HasPrefix(in, "struct{") ||
  551. strings.HasPrefix(in, "<-chan") ||
  552. strings.HasPrefix(in, "chan<-") ||
  553. strings.HasPrefix(in, "chan ") ||
  554. strings.HasPrefix(in, "func(") ||
  555. strings.HasPrefix(in, "*") ||
  556. strings.HasPrefix(in, "map[") ||
  557. strings.HasPrefix(in, "[") {
  558. return types.Name{Name: in}
  559. }
  560. // Otherwise, if there are '.' characters present, the name has a
  561. // package path in front.
  562. nameParts := strings.Split(in, ".")
  563. name := types.Name{Name: in}
  564. if n := len(nameParts); n >= 2 {
  565. // The final "." is the name of the type--previous ones must
  566. // have been in the package path.
  567. name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1]
  568. }
  569. return name
  570. }
  571. func (b *Builder) convertSignature(u types.Universe, t *tc.Signature) *types.Signature {
  572. signature := &types.Signature{}
  573. for i := 0; i < t.Params().Len(); i++ {
  574. signature.Parameters = append(signature.Parameters, b.walkType(u, nil, t.Params().At(i).Type()))
  575. }
  576. for i := 0; i < t.Results().Len(); i++ {
  577. signature.Results = append(signature.Results, b.walkType(u, nil, t.Results().At(i).Type()))
  578. }
  579. if r := t.Recv(); r != nil {
  580. signature.Receiver = b.walkType(u, nil, r.Type())
  581. }
  582. signature.Variadic = t.Variadic()
  583. return signature
  584. }
  585. // walkType adds the type, and any necessary child types.
  586. func (b *Builder) walkType(u types.Universe, useName *types.Name, in tc.Type) *types.Type {
  587. // Most of the cases are underlying types of the named type.
  588. name := tcNameToName(in.String())
  589. if useName != nil {
  590. name = *useName
  591. }
  592. switch t := in.(type) {
  593. case *tc.Struct:
  594. out := u.Type(name)
  595. if out.Kind != types.Unknown {
  596. return out
  597. }
  598. out.Kind = types.Struct
  599. for i := 0; i < t.NumFields(); i++ {
  600. f := t.Field(i)
  601. m := types.Member{
  602. Name: f.Name(),
  603. Embedded: f.Anonymous(),
  604. Tags: t.Tag(i),
  605. Type: b.walkType(u, nil, f.Type()),
  606. CommentLines: splitLines(b.priorCommentLines(f.Pos(), 1).Text()),
  607. }
  608. out.Members = append(out.Members, m)
  609. }
  610. return out
  611. case *tc.Map:
  612. out := u.Type(name)
  613. if out.Kind != types.Unknown {
  614. return out
  615. }
  616. out.Kind = types.Map
  617. out.Elem = b.walkType(u, nil, t.Elem())
  618. out.Key = b.walkType(u, nil, t.Key())
  619. return out
  620. case *tc.Pointer:
  621. out := u.Type(name)
  622. if out.Kind != types.Unknown {
  623. return out
  624. }
  625. out.Kind = types.Pointer
  626. out.Elem = b.walkType(u, nil, t.Elem())
  627. return out
  628. case *tc.Slice:
  629. out := u.Type(name)
  630. if out.Kind != types.Unknown {
  631. return out
  632. }
  633. out.Kind = types.Slice
  634. out.Elem = b.walkType(u, nil, t.Elem())
  635. return out
  636. case *tc.Array:
  637. out := u.Type(name)
  638. if out.Kind != types.Unknown {
  639. return out
  640. }
  641. out.Kind = types.Array
  642. out.Elem = b.walkType(u, nil, t.Elem())
  643. // TODO: need to store array length, otherwise raw type name
  644. // cannot be properly written.
  645. return out
  646. case *tc.Chan:
  647. out := u.Type(name)
  648. if out.Kind != types.Unknown {
  649. return out
  650. }
  651. out.Kind = types.Chan
  652. out.Elem = b.walkType(u, nil, t.Elem())
  653. // TODO: need to store direction, otherwise raw type name
  654. // cannot be properly written.
  655. return out
  656. case *tc.Basic:
  657. out := u.Type(types.Name{
  658. Package: "",
  659. Name: t.Name(),
  660. })
  661. if out.Kind != types.Unknown {
  662. return out
  663. }
  664. out.Kind = types.Unsupported
  665. return out
  666. case *tc.Signature:
  667. out := u.Type(name)
  668. if out.Kind != types.Unknown {
  669. return out
  670. }
  671. out.Kind = types.Func
  672. out.Signature = b.convertSignature(u, t)
  673. return out
  674. case *tc.Interface:
  675. out := u.Type(name)
  676. if out.Kind != types.Unknown {
  677. return out
  678. }
  679. out.Kind = types.Interface
  680. t.Complete()
  681. for i := 0; i < t.NumMethods(); i++ {
  682. if out.Methods == nil {
  683. out.Methods = map[string]*types.Type{}
  684. }
  685. out.Methods[t.Method(i).Name()] = b.walkType(u, nil, t.Method(i).Type())
  686. }
  687. return out
  688. case *tc.Named:
  689. var out *types.Type
  690. switch t.Underlying().(type) {
  691. case *tc.Named, *tc.Basic, *tc.Map, *tc.Slice:
  692. name := tcNameToName(t.String())
  693. out = u.Type(name)
  694. if out.Kind != types.Unknown {
  695. return out
  696. }
  697. out.Kind = types.Alias
  698. out.Underlying = b.walkType(u, nil, t.Underlying())
  699. default:
  700. // tc package makes everything "named" with an
  701. // underlying anonymous type--we remove that annoying
  702. // "feature" for users. This flattens those types
  703. // together.
  704. name := tcNameToName(t.String())
  705. if out := u.Type(name); out.Kind != types.Unknown {
  706. return out // short circuit if we've already made this.
  707. }
  708. out = b.walkType(u, &name, t.Underlying())
  709. }
  710. // If the underlying type didn't already add methods, add them.
  711. // (Interface types will have already added methods.)
  712. if len(out.Methods) == 0 {
  713. for i := 0; i < t.NumMethods(); i++ {
  714. if out.Methods == nil {
  715. out.Methods = map[string]*types.Type{}
  716. }
  717. method := t.Method(i)
  718. mt := b.walkType(u, nil, method.Type())
  719. mt.CommentLines = splitLines(b.priorCommentLines(method.Pos(), 1).Text())
  720. out.Methods[method.Name()] = mt
  721. }
  722. }
  723. return out
  724. default:
  725. out := u.Type(name)
  726. if out.Kind != types.Unknown {
  727. return out
  728. }
  729. out.Kind = types.Unsupported
  730. klog.Warningf("Making unsupported type entry %q for: %#v\n", out, t)
  731. return out
  732. }
  733. }
  734. func (b *Builder) addFunction(u types.Universe, useName *types.Name, in *tc.Func) *types.Type {
  735. name := tcFuncNameToName(in.String())
  736. if useName != nil {
  737. name = *useName
  738. }
  739. out := u.Function(name)
  740. out.Kind = types.DeclarationOf
  741. out.Underlying = b.walkType(u, nil, in.Type())
  742. return out
  743. }
  744. func (b *Builder) addVariable(u types.Universe, useName *types.Name, in *tc.Var) *types.Type {
  745. name := tcVarNameToName(in.String())
  746. if useName != nil {
  747. name = *useName
  748. }
  749. out := u.Variable(name)
  750. out.Kind = types.DeclarationOf
  751. out.Underlying = b.walkType(u, nil, in.Type())
  752. return out
  753. }
  754. func (b *Builder) addConstant(u types.Universe, useName *types.Name, in *tc.Const) *types.Type {
  755. name := tcVarNameToName(in.String())
  756. if useName != nil {
  757. name = *useName
  758. }
  759. out := u.Constant(name)
  760. out.Kind = types.DeclarationOf
  761. out.Underlying = b.walkType(u, nil, in.Type())
  762. return out
  763. }
  764. // canonicalizeImportPath takes an import path and returns the actual package.
  765. // It doesn't support nested vendoring.
  766. func canonicalizeImportPath(importPath string) importPathString {
  767. if !strings.Contains(importPath, "/vendor/") {
  768. return importPathString(importPath)
  769. }
  770. return importPathString(importPath[strings.Index(importPath, "/vendor/")+len("/vendor/"):])
  771. }