goparser.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Package goparse contains logic for parsing Go files. Specifically it parses
  2. // source and test files into domain model for generating tests.
  3. package goparser
  4. import (
  5. "errors"
  6. "fmt"
  7. "go/ast"
  8. "go/parser"
  9. "go/token"
  10. "go/types"
  11. "io/ioutil"
  12. "strings"
  13. "go-common/app/tool/gorpc/model"
  14. )
  15. // ErrEmptyFile represents an empty file error.
  16. var ErrEmptyFile = errors.New("file is empty")
  17. // Result representats a parsed Go file.
  18. type Result struct {
  19. // The package name and imports of a Go file.
  20. Header *model.Header
  21. // All the functions and methods in a Go file.
  22. Funcs []*model.Function
  23. }
  24. // Parser can parse Go files.
  25. type Parser struct {
  26. // The importer to resolve packages from import paths.
  27. Importer types.Importer
  28. }
  29. // Parse parses a given Go file at srcPath, along any files that share the same
  30. // package, into a domain model for generating tests.
  31. func (p *Parser) Parse(srcPath string, files []model.Path) (*Result, error) {
  32. b, err := p.readFile(srcPath)
  33. if err != nil {
  34. return nil, err
  35. }
  36. fset := token.NewFileSet()
  37. f, err := p.parseFile(fset, srcPath)
  38. if err != nil {
  39. return nil, err
  40. }
  41. fs, err := p.parseFiles(fset, f, files)
  42. if err != nil {
  43. return nil, err
  44. }
  45. return &Result{
  46. Header: &model.Header{
  47. Comments: parseComment(f, f.Package),
  48. Package: f.Name.String(),
  49. Imports: parseImports(f.Imports),
  50. Code: goCode(b, f),
  51. },
  52. Funcs: p.parseFunctions(fset, f, fs),
  53. }, nil
  54. }
  55. func (p *Parser) readFile(srcPath string) ([]byte, error) {
  56. b, err := ioutil.ReadFile(srcPath)
  57. if err != nil {
  58. return nil, fmt.Errorf("ioutil.ReadFile: %v", err)
  59. }
  60. if len(b) == 0 {
  61. return nil, ErrEmptyFile
  62. }
  63. return b, nil
  64. }
  65. func (p *Parser) parseFile(fset *token.FileSet, srcPath string) (*ast.File, error) {
  66. f, err := parser.ParseFile(fset, srcPath, nil, parser.ParseComments)
  67. if err != nil {
  68. return nil, fmt.Errorf("target parser.ParseFile(): %v", err)
  69. }
  70. return f, nil
  71. }
  72. func (p *Parser) parseFiles(fset *token.FileSet, f *ast.File, files []model.Path) ([]*ast.File, error) {
  73. pkg := f.Name.String()
  74. var fs []*ast.File
  75. for _, file := range files {
  76. ff, err := parser.ParseFile(fset, string(file), nil, 0)
  77. if err != nil {
  78. return nil, fmt.Errorf("other file parser.ParseFile: %v", err)
  79. }
  80. if name := ff.Name.String(); name != pkg {
  81. continue
  82. }
  83. fs = append(fs, ff)
  84. }
  85. return fs, nil
  86. }
  87. func (p *Parser) parseFunctions(fset *token.FileSet, f *ast.File, fs []*ast.File) []*model.Function {
  88. ul, el := p.parseTypes(fset, fs)
  89. var funcs []*model.Function
  90. for _, d := range f.Decls {
  91. fDecl, ok := d.(*ast.FuncDecl)
  92. if !ok {
  93. continue
  94. }
  95. funcs = append(funcs, parseFunc(fDecl, ul, el))
  96. }
  97. return funcs
  98. }
  99. func (p *Parser) parseTypes(fset *token.FileSet, fs []*ast.File) (map[string]types.Type, map[*types.Struct]ast.Expr) {
  100. conf := &types.Config{
  101. Importer: p.Importer,
  102. // Adding a NO-OP error function ignores errors and performs best-effort
  103. // type checking. https://godoc.org/golang.org/x/tools/go/types#Config
  104. Error: func(error) {},
  105. }
  106. ti := &types.Info{
  107. Types: make(map[ast.Expr]types.TypeAndValue),
  108. }
  109. // Note: conf.Check can fail, but since Info is not required data, it's ok.
  110. conf.Check("", fset, fs, ti)
  111. ul := make(map[string]types.Type)
  112. el := make(map[*types.Struct]ast.Expr)
  113. for e, t := range ti.Types {
  114. // Collect the underlying types.
  115. ul[t.Type.String()] = t.Type.Underlying()
  116. // Collect structs to determine the fields of a receiver.
  117. if v, ok := t.Type.(*types.Struct); ok {
  118. el[v] = e
  119. }
  120. }
  121. return ul, el
  122. }
  123. func parseComment(f *ast.File, pkgPos token.Pos) []string {
  124. var comments []string
  125. var count int
  126. for _, comment := range f.Comments {
  127. if comment.End() < pkgPos && comment != f.Doc {
  128. for _, c := range comment.List {
  129. count += len(c.Text) + 1 // +1 for '\n'
  130. if count < int(c.End()) {
  131. n := int(c.End()) - count
  132. comments = append(comments, strings.Repeat("\n", n))
  133. count++ // for last of '\n'
  134. }
  135. comments = append(comments, c.Text)
  136. }
  137. }
  138. }
  139. return comments
  140. }
  141. // Returns the Go code below the imports block.
  142. func goCode(b []byte, f *ast.File) []byte {
  143. furthestPos := f.Name.End()
  144. for _, node := range f.Imports {
  145. if pos := node.End(); pos > furthestPos {
  146. furthestPos = pos
  147. }
  148. }
  149. if furthestPos < token.Pos(len(b)) {
  150. furthestPos++
  151. }
  152. return b[furthestPos:]
  153. }
  154. func parseFunc(fDecl *ast.FuncDecl, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *model.Function {
  155. f := &model.Function{
  156. Name: fDecl.Name.String(),
  157. IsExported: fDecl.Name.IsExported(),
  158. Receiver: parseReceiver(fDecl.Recv, ul, el),
  159. Parameters: parseFieldList(fDecl.Type.Params, ul),
  160. }
  161. fs := parseFieldList(fDecl.Type.Results, ul)
  162. i := 0
  163. for _, fi := range fs {
  164. if fi.Type.String() == "error" {
  165. f.ReturnsError = true
  166. continue
  167. }
  168. fi.Index = i
  169. f.Results = append(f.Results, fi)
  170. i++
  171. }
  172. return f
  173. }
  174. func parseImports(imps []*ast.ImportSpec) []*model.Import {
  175. var is []*model.Import
  176. for _, imp := range imps {
  177. var n string
  178. if imp.Name != nil {
  179. n = imp.Name.String()
  180. }
  181. is = append(is, &model.Import{
  182. Name: n,
  183. Path: imp.Path.Value,
  184. })
  185. }
  186. return is
  187. }
  188. func parseReceiver(fl *ast.FieldList, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *model.Receiver {
  189. if fl == nil {
  190. return nil
  191. }
  192. r := &model.Receiver{
  193. Field: parseFieldList(fl, ul)[0],
  194. }
  195. t, ok := ul[r.Type.Value]
  196. if !ok {
  197. return r
  198. }
  199. s, ok := t.(*types.Struct)
  200. if !ok {
  201. return r
  202. }
  203. st := el[s].(*ast.StructType)
  204. r.Fields = append(r.Fields, parseFieldList(st.Fields, ul)...)
  205. for i, f := range r.Fields {
  206. f.Name = s.Field(i).Name()
  207. }
  208. return r
  209. }
  210. func parseFieldList(fl *ast.FieldList, ul map[string]types.Type) []*model.Field {
  211. if fl == nil {
  212. return nil
  213. }
  214. i := 0
  215. var fs []*model.Field
  216. for _, f := range fl.List {
  217. for _, pf := range parseFields(f, ul) {
  218. pf.Index = i
  219. fs = append(fs, pf)
  220. i++
  221. }
  222. }
  223. return fs
  224. }
  225. func parseFields(f *ast.Field, ul map[string]types.Type) []*model.Field {
  226. t := parseExpr(f.Type, ul)
  227. if len(f.Names) == 0 {
  228. return []*model.Field{{
  229. Type: t,
  230. }}
  231. }
  232. var fs []*model.Field
  233. for _, n := range f.Names {
  234. fs = append(fs, &model.Field{
  235. Name: n.Name,
  236. Type: t,
  237. })
  238. }
  239. return fs
  240. }
  241. func parseExpr(e ast.Expr, ul map[string]types.Type) *model.Expression {
  242. switch v := e.(type) {
  243. case *ast.StarExpr:
  244. val := types.ExprString(v.X)
  245. return &model.Expression{
  246. Value: val,
  247. IsStar: true,
  248. Underlying: underlying(val, ul),
  249. }
  250. case *ast.Ellipsis:
  251. exp := parseExpr(v.Elt, ul)
  252. return &model.Expression{
  253. Value: exp.Value,
  254. IsStar: exp.IsStar,
  255. IsVariadic: true,
  256. Underlying: underlying(exp.Value, ul),
  257. }
  258. default:
  259. val := types.ExprString(e)
  260. return &model.Expression{
  261. Value: val,
  262. Underlying: underlying(val, ul),
  263. IsWriter: val == "io.Writer",
  264. }
  265. }
  266. }
  267. func underlying(val string, ul map[string]types.Type) string {
  268. if ul[val] != nil {
  269. return ul[val].String()
  270. }
  271. return ""
  272. }