pbast.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. package parser
  2. import (
  3. "errors"
  4. "fmt"
  5. "go/ast"
  6. "go/parser"
  7. "go/token"
  8. "sort"
  9. "strings"
  10. "github.com/tal-tech/go-zero/core/lang"
  11. sx "github.com/tal-tech/go-zero/core/stringx"
  12. "github.com/tal-tech/go-zero/tools/goctl/util"
  13. "github.com/tal-tech/go-zero/tools/goctl/util/console"
  14. "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
  15. )
  16. const (
  17. flagStar = "*"
  18. suffixServer = "Server"
  19. referenceContext = "context."
  20. unknownPrefix = "XXX_"
  21. ignoreJsonTagExpression = `json:"-"`
  22. )
  23. var (
  24. errorParseError = errors.New("pb parse error")
  25. typeTemplate = `type (
  26. {{.types}}
  27. )`
  28. structTemplate = `{{if .type}}type {{end}}{{.name}} struct {
  29. {{.fields}}
  30. }`
  31. fieldTemplate = `{{if .hasDoc}}{{.doc}}
  32. {{end}}{{.name}} {{.type}} {{.tag}}{{if .hasComment}}{{.comment}}{{end}}`
  33. objectM = make(map[string]*Struct)
  34. )
  35. type (
  36. astParser struct {
  37. golang []byte
  38. filterStruct map[string]lang.PlaceholderType
  39. console.Console
  40. fileSet *token.FileSet
  41. }
  42. Field struct {
  43. Name stringx.String
  44. TypeName string
  45. JsonTag string
  46. Document []string
  47. Comment []string
  48. }
  49. Struct struct {
  50. Name stringx.String
  51. Document []string
  52. Comment []string
  53. Field []*Field
  54. }
  55. Func struct {
  56. Name stringx.String
  57. InType string
  58. InTypeName string // remove *Context,such as LoginRequest、UserRequest
  59. OutTypeName string // remove *Context
  60. OutType string
  61. Document []string
  62. }
  63. RpcService struct {
  64. Name stringx.String
  65. Funcs []*Func
  66. }
  67. // parsing for rpc
  68. PbAst struct {
  69. Package string
  70. // external reference
  71. Imports map[string]string
  72. Strcuts map[string]*Struct
  73. // rpc server's functions,not all functions
  74. Service []*RpcService
  75. }
  76. )
  77. func NewAstParser(golang []byte, filterStruct map[string]lang.PlaceholderType, log console.Console) *astParser {
  78. return &astParser{
  79. golang: golang,
  80. filterStruct: filterStruct,
  81. Console: log,
  82. fileSet: token.NewFileSet(),
  83. }
  84. }
  85. func (a *astParser) Parse() (*PbAst, error) {
  86. fSet := a.fileSet
  87. f, err := parser.ParseFile(fSet, "", a.golang, parser.ParseComments)
  88. if err != nil {
  89. return nil, err
  90. }
  91. commentMap := ast.NewCommentMap(fSet, f, f.Comments)
  92. f.Comments = commentMap.Filter(f).Comments()
  93. var pbAst PbAst
  94. pbAst.Package = a.mustGetIndentName(f.Name)
  95. imports := make(map[string]string)
  96. for _, item := range f.Imports {
  97. if item == nil {
  98. continue
  99. }
  100. if item.Path == nil {
  101. continue
  102. }
  103. key := a.mustGetIndentName(item.Name)
  104. value := item.Path.Value
  105. imports[key] = value
  106. }
  107. structs, funcs := a.mustScope(f.Scope)
  108. pbAst.Imports = imports
  109. pbAst.Strcuts = structs
  110. pbAst.Service = funcs
  111. return &pbAst, nil
  112. }
  113. func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcService) {
  114. if scope == nil {
  115. return nil, nil
  116. }
  117. objects := scope.Objects
  118. structs := make(map[string]*Struct)
  119. serviceList := make([]*RpcService, 0)
  120. for name, obj := range objects {
  121. decl := obj.Decl
  122. if decl == nil {
  123. continue
  124. }
  125. typeSpec, ok := decl.(*ast.TypeSpec)
  126. if !ok {
  127. continue
  128. }
  129. tp := typeSpec.Type
  130. switch v := tp.(type) {
  131. case *ast.StructType:
  132. st, err := a.parseObject(name, v)
  133. a.Must(err)
  134. structs[st.Name.Lower()] = st
  135. case *ast.InterfaceType:
  136. if !strings.HasSuffix(name, suffixServer) {
  137. continue
  138. }
  139. list := a.mustServerFunctions(v)
  140. serviceList = append(serviceList, &RpcService{
  141. Name: stringx.From(strings.TrimSuffix(name, suffixServer)),
  142. Funcs: list,
  143. })
  144. }
  145. }
  146. targetStruct := make(map[string]*Struct)
  147. for st := range a.filterStruct {
  148. lower := strings.ToLower(st)
  149. targetStruct[lower] = structs[lower]
  150. }
  151. return targetStruct, serviceList
  152. }
  153. func (a *astParser) mustServerFunctions(v *ast.InterfaceType) []*Func {
  154. funcs := make([]*Func, 0)
  155. methodObject := v.Methods
  156. if methodObject == nil {
  157. return nil
  158. }
  159. for _, method := range methodObject.List {
  160. var item Func
  161. name := a.mustGetIndentName(method.Names[0])
  162. doc := a.parseCommentOrDoc(method.Doc)
  163. item.Name = stringx.From(name)
  164. item.Document = doc
  165. types := method.Type
  166. if types == nil {
  167. funcs = append(funcs, &item)
  168. continue
  169. }
  170. v, ok := types.(*ast.FuncType)
  171. if !ok {
  172. continue
  173. }
  174. params := v.Params
  175. if params != nil {
  176. inList, err := a.parseFields(params.List, true)
  177. a.Must(err)
  178. for _, data := range inList {
  179. if strings.HasPrefix(data.TypeName, referenceContext) {
  180. continue
  181. }
  182. // currently,does not support external references
  183. item.InTypeName = data.TypeName
  184. item.InType = strings.TrimPrefix(data.TypeName, flagStar)
  185. break
  186. }
  187. }
  188. results := v.Results
  189. if results != nil {
  190. outList, err := a.parseFields(results.List, true)
  191. a.Must(err)
  192. for _, data := range outList {
  193. if strings.HasPrefix(data.TypeName, referenceContext) {
  194. continue
  195. }
  196. // currently,does not support external references
  197. item.OutTypeName = data.TypeName
  198. item.OutType = strings.TrimPrefix(data.TypeName, flagStar)
  199. break
  200. }
  201. }
  202. funcs = append(funcs, &item)
  203. }
  204. return funcs
  205. }
  206. func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct, error) {
  207. if data, ok := objectM[structName]; ok {
  208. return data, nil
  209. }
  210. var st Struct
  211. st.Name = stringx.From(structName)
  212. if tp == nil {
  213. return &st, nil
  214. }
  215. fields := tp.Fields
  216. if fields == nil {
  217. objectM[structName] = &st
  218. return &st, nil
  219. }
  220. fieldList := fields.List
  221. members, err := a.parseFields(fieldList, false)
  222. if err != nil {
  223. return nil, err
  224. }
  225. for _, m := range members {
  226. var field Field
  227. field.Name = m.Name
  228. field.TypeName = m.TypeName
  229. field.JsonTag = m.JsonTag
  230. field.Document = m.Document
  231. field.Comment = m.Comment
  232. st.Field = append(st.Field, &field)
  233. }
  234. objectM[structName] = &st
  235. return &st, nil
  236. }
  237. func (a *astParser) parseFields(fields []*ast.Field, onlyType bool) ([]*Field, error) {
  238. ret := make([]*Field, 0)
  239. for _, field := range fields {
  240. var item Field
  241. tag := a.parseTag(field.Tag)
  242. if tag == "" && !onlyType {
  243. continue
  244. }
  245. if tag == ignoreJsonTagExpression {
  246. continue
  247. }
  248. item.JsonTag = tag
  249. name := a.parseName(field.Names)
  250. if strings.HasPrefix(name, unknownPrefix) {
  251. continue
  252. }
  253. item.Name = stringx.From(name)
  254. typeName, err := a.parseType(field.Type)
  255. if err != nil {
  256. return nil, err
  257. }
  258. item.TypeName = typeName
  259. if onlyType {
  260. ret = append(ret, &item)
  261. continue
  262. }
  263. docs := a.parseCommentOrDoc(field.Doc)
  264. comments := a.parseCommentOrDoc(field.Comment)
  265. item.Document = docs
  266. item.Comment = comments
  267. isInline := name == ""
  268. if isInline {
  269. return nil, a.wrapError(field.Pos(), "unexpected inline type:%s", name)
  270. }
  271. ret = append(ret, &item)
  272. }
  273. return ret, nil
  274. }
  275. func (a *astParser) parseTag(basicLit *ast.BasicLit) string {
  276. if basicLit == nil {
  277. return ""
  278. }
  279. value := basicLit.Value
  280. splits := strings.Split(value, " ")
  281. if len(splits) == 1 {
  282. return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[0], "`", ""))
  283. } else {
  284. return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[1], "`", ""))
  285. }
  286. }
  287. // returns
  288. // resp1:type's string expression,like int、string、[]int64、map[string]User、*User
  289. // resp2:error
  290. func (a *astParser) parseType(expr ast.Expr) (string, error) {
  291. if expr == nil {
  292. return "", errorParseError
  293. }
  294. switch v := expr.(type) {
  295. case *ast.StarExpr:
  296. stringExpr, err := a.parseType(v.X)
  297. if err != nil {
  298. return "", err
  299. }
  300. e := fmt.Sprintf("*%s", stringExpr)
  301. return e, nil
  302. case *ast.Ident:
  303. return a.mustGetIndentName(v), nil
  304. case *ast.MapType:
  305. keyStringExpr, err := a.parseType(v.Key)
  306. if err != nil {
  307. return "", err
  308. }
  309. valueStringExpr, err := a.parseType(v.Value)
  310. if err != nil {
  311. return "", err
  312. }
  313. e := fmt.Sprintf("map[%s]%s", keyStringExpr, valueStringExpr)
  314. return e, nil
  315. case *ast.ArrayType:
  316. stringExpr, err := a.parseType(v.Elt)
  317. if err != nil {
  318. return "", err
  319. }
  320. e := fmt.Sprintf("[]%s", stringExpr)
  321. return e, nil
  322. case *ast.InterfaceType:
  323. return "interface{}", nil
  324. case *ast.SelectorExpr:
  325. join := make([]string, 0)
  326. xIdent, ok := v.X.(*ast.Ident)
  327. xIndentName := a.mustGetIndentName(xIdent)
  328. if ok {
  329. join = append(join, xIndentName)
  330. }
  331. sel := v.Sel
  332. join = append(join, a.mustGetIndentName(sel))
  333. return strings.Join(join, "."), nil
  334. case *ast.ChanType:
  335. return "", a.wrapError(v.Pos(), "unexpected type 'chan'")
  336. case *ast.FuncType:
  337. return "", a.wrapError(v.Pos(), "unexpected type 'func'")
  338. case *ast.StructType:
  339. return "", a.wrapError(v.Pos(), "unexpected inline struct type")
  340. default:
  341. return "", a.wrapError(v.Pos(), "unexpected type '%v'", v)
  342. }
  343. }
  344. func (a *astParser) parseName(names []*ast.Ident) string {
  345. if len(names) == 0 {
  346. return ""
  347. }
  348. name := names[0]
  349. return a.mustGetIndentName(name)
  350. }
  351. func (a *astParser) parseCommentOrDoc(cg *ast.CommentGroup) []string {
  352. if cg == nil {
  353. return nil
  354. }
  355. comments := make([]string, 0)
  356. for _, comment := range cg.List {
  357. if comment == nil {
  358. continue
  359. }
  360. text := strings.TrimSpace(comment.Text)
  361. if text == "" {
  362. continue
  363. }
  364. comments = append(comments, text)
  365. }
  366. return comments
  367. }
  368. func (a *astParser) mustGetIndentName(ident *ast.Ident) string {
  369. if ident == nil {
  370. return ""
  371. }
  372. return ident.Name
  373. }
  374. func (a *astParser) wrapError(pos token.Pos, format string, arg ...interface{}) error {
  375. file := a.fileSet.Position(pos)
  376. return fmt.Errorf("line %v: %s", file.Line, fmt.Sprintf(format, arg...))
  377. }
  378. func (a *PbAst) GenTypesCode() (string, error) {
  379. types := make([]string, 0)
  380. sts := make([]*Struct, 0)
  381. for _, item := range a.Strcuts {
  382. sts = append(sts, item)
  383. }
  384. sort.Slice(sts, func(i, j int) bool {
  385. return sts[i].Name.Source() < sts[j].Name.Source()
  386. })
  387. for _, s := range sts {
  388. structCode, err := s.genCode(false)
  389. if err != nil {
  390. return "", err
  391. }
  392. if structCode == "" {
  393. continue
  394. }
  395. types = append(types, structCode)
  396. }
  397. buffer, err := util.With("type").Parse(typeTemplate).Execute(map[string]interface{}{
  398. "types": strings.Join(types, "\n\n"),
  399. })
  400. if err != nil {
  401. return "", err
  402. }
  403. return buffer.String(), nil
  404. }
  405. func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
  406. fields := make([]string, 0)
  407. for _, f := range s.Field {
  408. var comment, doc string
  409. if len(f.Comment) > 0 {
  410. comment = f.Comment[0]
  411. }
  412. doc = strings.Join(f.Document, "\n")
  413. buffer, err := util.With(sx.Rand()).Parse(fieldTemplate).Execute(map[string]interface{}{
  414. "name": f.Name.Title(),
  415. "type": f.TypeName,
  416. "tag": f.JsonTag,
  417. "hasDoc": len(f.Document) > 0,
  418. "doc": doc,
  419. "hasComment": len(f.Comment) > 0,
  420. "comment": comment,
  421. })
  422. if err != nil {
  423. return "", err
  424. }
  425. fields = append(fields, buffer.String())
  426. }
  427. buffer, err := util.With("struct").Parse(structTemplate).Execute(map[string]interface{}{
  428. "type": containsTypeStatement,
  429. "name": s.Name.Title(),
  430. "fields": strings.Join(fields, "\n"),
  431. })
  432. if err != nil {
  433. return "", err
  434. }
  435. return buffer.String(), nil
  436. }