parse.go 26 KB

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