pbast.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. package parser
  2. import (
  3. "errors"
  4. "fmt"
  5. "go/ast"
  6. "go/parser"
  7. "go/token"
  8. "io/ioutil"
  9. "sort"
  10. "strings"
  11. "github.com/tal-tech/go-zero/core/lang"
  12. sx "github.com/tal-tech/go-zero/core/stringx"
  13. "github.com/tal-tech/go-zero/tools/goctl/util"
  14. "github.com/tal-tech/go-zero/tools/goctl/util/console"
  15. "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
  16. )
  17. const (
  18. flagStar = "*"
  19. flagDot = "."
  20. suffixServer = "Server"
  21. referenceContext = "context"
  22. unknownPrefix = "XXX_"
  23. ignoreJsonTagExpression = `json:"-"`
  24. )
  25. var (
  26. errorParseError = errors.New("pb parse error")
  27. typeTemplate = `type (
  28. {{.types}}
  29. )`
  30. structTemplate = `{{if .type}}type {{end}}{{.name}} struct {
  31. {{.fields}}
  32. }`
  33. fieldTemplate = `{{if .hasDoc}}{{.doc}}
  34. {{end}}{{.name}} {{.type}} {{.tag}}{{if .hasComment}}{{.comment}}{{end}}`
  35. anyTypeTemplate = "Any struct {\n\tTypeUrl string `json:\"typeUrl\"`\n\tValue []byte `json:\"value\"`\n}"
  36. objectM = make(map[string]*Struct)
  37. )
  38. type (
  39. astParser struct {
  40. filterStruct map[string]lang.PlaceholderType
  41. filterEnum map[string]*Enum
  42. console.Console
  43. fileSet *token.FileSet
  44. proto *Proto
  45. }
  46. Field struct {
  47. Name stringx.String
  48. Type Type
  49. JsonTag string
  50. Document []string
  51. Comment []string
  52. }
  53. Struct struct {
  54. Name stringx.String
  55. Document []string
  56. Comment []string
  57. Field []*Field
  58. }
  59. ConstLit struct {
  60. Name stringx.String
  61. Document []string
  62. Comment []string
  63. Lit []*Lit
  64. }
  65. Lit struct {
  66. Key string
  67. Value int
  68. }
  69. Type struct {
  70. // eg:context.Context
  71. Expression string
  72. // eg: *context.Context
  73. StarExpression string
  74. // Invoke Type Expression
  75. InvokeTypeExpression string
  76. // eg:context
  77. Package string
  78. // eg:Context
  79. Name string
  80. }
  81. Func struct {
  82. Name stringx.String
  83. ParameterIn Type
  84. ParameterOut Type
  85. Document []string
  86. }
  87. RpcService struct {
  88. Name stringx.String
  89. Funcs []*Func
  90. }
  91. // parsing for rpc
  92. PbAst struct {
  93. // deprecated: containsAny will be removed in the feature
  94. ContainsAny bool
  95. Imports map[string]string
  96. Structure map[string]*Struct
  97. Service []*RpcService
  98. *Proto
  99. }
  100. )
  101. func MustNewAstParser(proto *Proto, log console.Console) *astParser {
  102. return &astParser{
  103. filterStruct: proto.Message,
  104. filterEnum: proto.Enum,
  105. Console: log,
  106. fileSet: token.NewFileSet(),
  107. proto: proto,
  108. }
  109. }
  110. func (a *astParser) Parse() (*PbAst, error) {
  111. var pbAst PbAst
  112. pbAst.ContainsAny = a.proto.ContainsAny
  113. pbAst.Proto = a.proto
  114. pbAst.Structure = make(map[string]*Struct)
  115. pbAst.Imports = make(map[string]string)
  116. structure, imports, services, err := a.parse(a.proto.PbSrc)
  117. if err != nil {
  118. return nil, err
  119. }
  120. dependencyStructure, err := a.parseExternalDependency()
  121. if err != nil {
  122. return nil, err
  123. }
  124. for k, v := range structure {
  125. pbAst.Structure[k] = v
  126. }
  127. for k, v := range dependencyStructure {
  128. pbAst.Structure[k] = v
  129. }
  130. for key, path := range imports {
  131. pbAst.Imports[key] = path
  132. }
  133. pbAst.Service = append(pbAst.Service, services...)
  134. return &pbAst, nil
  135. }
  136. func (a *astParser) parse(pbSrc string) (structure map[string]*Struct, imports map[string]string, services []*RpcService, retErr error) {
  137. structure = make(map[string]*Struct)
  138. imports = make(map[string]string)
  139. data, err := ioutil.ReadFile(pbSrc)
  140. if err != nil {
  141. retErr = err
  142. return
  143. }
  144. fSet := a.fileSet
  145. f, err := parser.ParseFile(fSet, "", data, parser.ParseComments)
  146. if err != nil {
  147. retErr = err
  148. return
  149. }
  150. commentMap := ast.NewCommentMap(fSet, f, f.Comments)
  151. f.Comments = commentMap.Filter(f).Comments()
  152. strucs, function := a.mustScope(f.Scope, a.mustGetIndentName(f.Name))
  153. for k, v := range strucs {
  154. if v == nil {
  155. continue
  156. }
  157. structure[k] = v
  158. }
  159. importList := f.Imports
  160. for _, item := range importList {
  161. name := a.mustGetIndentName(item.Name)
  162. if item.Path != nil {
  163. imports[name] = item.Path.Value
  164. }
  165. }
  166. services = append(services, function...)
  167. return
  168. }
  169. func (a *astParser) parseExternalDependency() (map[string]*Struct, error) {
  170. m := make(map[string]*Struct)
  171. for _, impo := range a.proto.Import {
  172. ret, _, _, err := a.parse(impo.OriginalPbPath)
  173. if err != nil {
  174. return nil, err
  175. }
  176. for k, v := range ret {
  177. m[k] = v
  178. }
  179. }
  180. return m, nil
  181. }
  182. func (a *astParser) mustScope(scope *ast.Scope, sourcePackage string) (map[string]*Struct, []*RpcService) {
  183. if scope == nil {
  184. return nil, nil
  185. }
  186. objects := scope.Objects
  187. structs := make(map[string]*Struct)
  188. serviceList := make([]*RpcService, 0)
  189. for name, obj := range objects {
  190. decl := obj.Decl
  191. if decl == nil {
  192. continue
  193. }
  194. typeSpec, ok := decl.(*ast.TypeSpec)
  195. if !ok {
  196. continue
  197. }
  198. tp := typeSpec.Type
  199. switch v := tp.(type) {
  200. case *ast.StructType:
  201. st, err := a.parseObject(name, v, sourcePackage)
  202. a.Must(err)
  203. structs[st.Name.Lower()] = st
  204. case *ast.InterfaceType:
  205. if !strings.HasSuffix(name, suffixServer) {
  206. continue
  207. }
  208. list := a.mustServerFunctions(v, sourcePackage)
  209. serviceList = append(serviceList, &RpcService{
  210. Name: stringx.From(strings.TrimSuffix(name, suffixServer)),
  211. Funcs: list,
  212. })
  213. }
  214. }
  215. targetStruct := make(map[string]*Struct)
  216. for st := range a.filterStruct {
  217. lower := strings.ToLower(st)
  218. targetStruct[lower] = structs[lower]
  219. }
  220. return targetStruct, serviceList
  221. }
  222. func (a *astParser) mustServerFunctions(v *ast.InterfaceType, sourcePackage string) []*Func {
  223. funcs := make([]*Func, 0)
  224. methodObject := v.Methods
  225. if methodObject == nil {
  226. return nil
  227. }
  228. for _, method := range methodObject.List {
  229. var item Func
  230. name := a.mustGetIndentName(method.Names[0])
  231. doc := a.parseCommentOrDoc(method.Doc)
  232. item.Name = stringx.From(name)
  233. item.Document = doc
  234. types := method.Type
  235. if types == nil {
  236. funcs = append(funcs, &item)
  237. continue
  238. }
  239. v, ok := types.(*ast.FuncType)
  240. if !ok {
  241. continue
  242. }
  243. params := v.Params
  244. if params != nil {
  245. inList, err := a.parseFields(params.List, true, sourcePackage)
  246. a.Must(err)
  247. for _, data := range inList {
  248. if data.Type.Package == referenceContext {
  249. continue
  250. }
  251. item.ParameterIn = data.Type
  252. break
  253. }
  254. }
  255. results := v.Results
  256. if results != nil {
  257. outList, err := a.parseFields(results.List, true, sourcePackage)
  258. a.Must(err)
  259. for _, data := range outList {
  260. if data.Type.Package == referenceContext {
  261. continue
  262. }
  263. item.ParameterOut = data.Type
  264. break
  265. }
  266. }
  267. funcs = append(funcs, &item)
  268. }
  269. return funcs
  270. }
  271. func (a *astParser) getFieldType(v string, sourcePackage string) Type {
  272. var pkg, name, expression, starExpression, invokeTypeExpression string
  273. if strings.Contains(v, ".") {
  274. starExpression = v
  275. if strings.Contains(v, "*") {
  276. leftIndex := strings.Index(v, "*")
  277. rightIndex := strings.Index(v, ".")
  278. if leftIndex >= 0 {
  279. invokeTypeExpression = v[0:leftIndex+1] + v[rightIndex+1:]
  280. } else {
  281. invokeTypeExpression = v[rightIndex+1:]
  282. }
  283. } else {
  284. if strings.HasPrefix(v, "map[") || strings.HasPrefix(v, "[]") {
  285. leftIndex := strings.Index(v, "]")
  286. rightIndex := strings.Index(v, ".")
  287. invokeTypeExpression = v[0:leftIndex+1] + v[rightIndex+1:]
  288. } else {
  289. rightIndex := strings.Index(v, ".")
  290. invokeTypeExpression = v[rightIndex+1:]
  291. }
  292. }
  293. } else {
  294. expression = strings.TrimPrefix(v, flagStar)
  295. switch v {
  296. case "double", "float", "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64",
  297. "bool", "string", "bytes":
  298. invokeTypeExpression = v
  299. break
  300. default:
  301. name = expression
  302. invokeTypeExpression = v
  303. if strings.HasPrefix(v, "map[") || strings.HasPrefix(v, "[]") {
  304. starExpression = strings.ReplaceAll(v, flagStar, flagStar+sourcePackage+".")
  305. } else {
  306. starExpression = fmt.Sprintf("*%v.%v", sourcePackage, name)
  307. invokeTypeExpression = v
  308. }
  309. }
  310. }
  311. expression = strings.TrimPrefix(starExpression, flagStar)
  312. index := strings.LastIndex(expression, flagDot)
  313. if index > 0 {
  314. pkg = expression[0:index]
  315. name = expression[index+1:]
  316. } else {
  317. pkg = sourcePackage
  318. }
  319. return Type{
  320. Expression: expression,
  321. StarExpression: starExpression,
  322. InvokeTypeExpression: invokeTypeExpression,
  323. Package: pkg,
  324. Name: name,
  325. }
  326. }
  327. func (a *astParser) parseObject(structName string, tp *ast.StructType, sourcePackage string) (*Struct, error) {
  328. if data, ok := objectM[structName]; ok {
  329. return data, nil
  330. }
  331. var st Struct
  332. st.Name = stringx.From(structName)
  333. if tp == nil {
  334. return &st, nil
  335. }
  336. fields := tp.Fields
  337. if fields == nil {
  338. objectM[structName] = &st
  339. return &st, nil
  340. }
  341. fieldList := fields.List
  342. members, err := a.parseFields(fieldList, false, sourcePackage)
  343. if err != nil {
  344. return nil, err
  345. }
  346. for _, m := range members {
  347. var field Field
  348. field.Name = m.Name
  349. field.Type = m.Type
  350. field.JsonTag = m.JsonTag
  351. field.Document = m.Document
  352. field.Comment = m.Comment
  353. st.Field = append(st.Field, &field)
  354. }
  355. objectM[structName] = &st
  356. return &st, nil
  357. }
  358. func (a *astParser) parseFields(fields []*ast.Field, onlyType bool, sourcePackage string) ([]*Field, error) {
  359. ret := make([]*Field, 0)
  360. for _, field := range fields {
  361. var item Field
  362. tag := a.parseTag(field.Tag)
  363. if tag == "" && !onlyType {
  364. continue
  365. }
  366. if tag == ignoreJsonTagExpression {
  367. continue
  368. }
  369. item.JsonTag = tag
  370. name := a.parseName(field.Names)
  371. if strings.HasPrefix(name, unknownPrefix) {
  372. continue
  373. }
  374. item.Name = stringx.From(name)
  375. typeName, err := a.parseType(field.Type)
  376. if err != nil {
  377. return nil, err
  378. }
  379. item.Type = a.getFieldType(typeName, sourcePackage)
  380. if onlyType {
  381. ret = append(ret, &item)
  382. continue
  383. }
  384. docs := a.parseCommentOrDoc(field.Doc)
  385. comments := a.parseCommentOrDoc(field.Comment)
  386. item.Document = docs
  387. item.Comment = comments
  388. isInline := name == ""
  389. if isInline {
  390. return nil, a.wrapError(field.Pos(), "unexpected inline type:%s", name)
  391. }
  392. ret = append(ret, &item)
  393. }
  394. return ret, nil
  395. }
  396. func (a *astParser) parseTag(basicLit *ast.BasicLit) string {
  397. if basicLit == nil {
  398. return ""
  399. }
  400. value := basicLit.Value
  401. splits := strings.Split(value, " ")
  402. if len(splits) == 1 {
  403. return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[0], "`", ""))
  404. } else {
  405. return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[1], "`", ""))
  406. }
  407. }
  408. // returns
  409. // resp1:type's string expression,like int、string、[]int64、map[string]User、*User
  410. // resp2:error
  411. func (a *astParser) parseType(expr ast.Expr) (string, error) {
  412. if expr == nil {
  413. return "", errorParseError
  414. }
  415. switch v := expr.(type) {
  416. case *ast.StarExpr:
  417. stringExpr, err := a.parseType(v.X)
  418. if err != nil {
  419. return "", err
  420. }
  421. e := fmt.Sprintf("*%s", stringExpr)
  422. return e, nil
  423. case *ast.Ident:
  424. return a.mustGetIndentName(v), nil
  425. case *ast.MapType:
  426. keyStringExpr, err := a.parseType(v.Key)
  427. if err != nil {
  428. return "", err
  429. }
  430. valueStringExpr, err := a.parseType(v.Value)
  431. if err != nil {
  432. return "", err
  433. }
  434. e := fmt.Sprintf("map[%s]%s", keyStringExpr, valueStringExpr)
  435. return e, nil
  436. case *ast.ArrayType:
  437. stringExpr, err := a.parseType(v.Elt)
  438. if err != nil {
  439. return "", err
  440. }
  441. e := fmt.Sprintf("[]%s", stringExpr)
  442. return e, nil
  443. case *ast.InterfaceType:
  444. return "interface{}", nil
  445. case *ast.SelectorExpr:
  446. join := make([]string, 0)
  447. xIdent, ok := v.X.(*ast.Ident)
  448. xIndentName := a.mustGetIndentName(xIdent)
  449. if ok {
  450. join = append(join, xIndentName)
  451. }
  452. sel := v.Sel
  453. join = append(join, a.mustGetIndentName(sel))
  454. return strings.Join(join, "."), nil
  455. case *ast.ChanType:
  456. return "", a.wrapError(v.Pos(), "unexpected type 'chan'")
  457. case *ast.FuncType:
  458. return "", a.wrapError(v.Pos(), "unexpected type 'func'")
  459. case *ast.StructType:
  460. return "", a.wrapError(v.Pos(), "unexpected inline struct type")
  461. default:
  462. return "", a.wrapError(v.Pos(), "unexpected type '%v'", v)
  463. }
  464. }
  465. func (a *astParser) parseName(names []*ast.Ident) string {
  466. if len(names) == 0 {
  467. return ""
  468. }
  469. name := names[0]
  470. return a.mustGetIndentName(name)
  471. }
  472. func (a *astParser) parseCommentOrDoc(cg *ast.CommentGroup) []string {
  473. if cg == nil {
  474. return nil
  475. }
  476. comments := make([]string, 0)
  477. for _, comment := range cg.List {
  478. if comment == nil {
  479. continue
  480. }
  481. text := strings.TrimSpace(comment.Text)
  482. if text == "" {
  483. continue
  484. }
  485. comments = append(comments, text)
  486. }
  487. return comments
  488. }
  489. func (a *astParser) mustGetIndentName(ident *ast.Ident) string {
  490. if ident == nil {
  491. return ""
  492. }
  493. return ident.Name
  494. }
  495. func (a *astParser) wrapError(pos token.Pos, format string, arg ...interface{}) error {
  496. file := a.fileSet.Position(pos)
  497. return fmt.Errorf("line %v: %s", file.Line, fmt.Sprintf(format, arg...))
  498. }
  499. func (f *Func) GetDoc() string {
  500. return strings.Join(f.Document, util.NL)
  501. }
  502. func (f *Func) HaveDoc() bool {
  503. return len(f.Document) > 0
  504. }
  505. func (a *PbAst) GenEnumCode() (string, error) {
  506. var element []string
  507. for _, item := range a.Enum {
  508. code, err := item.GenEnumCode()
  509. if err != nil {
  510. return "", err
  511. }
  512. element = append(element, code)
  513. }
  514. return strings.Join(element, util.NL), nil
  515. }
  516. func (a *PbAst) GenTypesCode() (string, error) {
  517. types := make([]string, 0)
  518. sts := make([]*Struct, 0)
  519. for _, item := range a.Structure {
  520. sts = append(sts, item)
  521. }
  522. sort.Slice(sts, func(i, j int) bool {
  523. return sts[i].Name.Source() < sts[j].Name.Source()
  524. })
  525. for _, s := range sts {
  526. structCode, err := s.genCode(false)
  527. if err != nil {
  528. return "", err
  529. }
  530. if structCode == "" {
  531. continue
  532. }
  533. types = append(types, structCode)
  534. }
  535. types = append(types, a.genAnyCode())
  536. for _, item := range a.Enum {
  537. typeCode, err := item.GenEnumTypeCode()
  538. if err != nil {
  539. return "", err
  540. }
  541. types = append(types, typeCode)
  542. }
  543. buffer, err := util.With("type").Parse(typeTemplate).Execute(map[string]interface{}{
  544. "types": strings.Join(types, util.NL+util.NL),
  545. })
  546. if err != nil {
  547. return "", err
  548. }
  549. return buffer.String(), nil
  550. }
  551. func (a *PbAst) genAnyCode() string {
  552. if !a.ContainsAny {
  553. return ""
  554. }
  555. return anyTypeTemplate
  556. }
  557. func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
  558. fields := make([]string, 0)
  559. for _, f := range s.Field {
  560. var comment, doc string
  561. if len(f.Comment) > 0 {
  562. comment = f.Comment[0]
  563. }
  564. doc = strings.Join(f.Document, util.NL)
  565. buffer, err := util.With(sx.Rand()).Parse(fieldTemplate).Execute(map[string]interface{}{
  566. "name": f.Name.Title(),
  567. "type": f.Type.InvokeTypeExpression,
  568. "tag": f.JsonTag,
  569. "hasDoc": len(f.Document) > 0,
  570. "doc": doc,
  571. "hasComment": len(f.Comment) > 0,
  572. "comment": comment,
  573. })
  574. if err != nil {
  575. return "", err
  576. }
  577. fields = append(fields, buffer.String())
  578. }
  579. buffer, err := util.With("struct").Parse(structTemplate).Execute(map[string]interface{}{
  580. "type": containsTypeStatement,
  581. "name": s.Name.Title(),
  582. "fields": strings.Join(fields, util.NL),
  583. })
  584. if err != nil {
  585. return "", err
  586. }
  587. return buffer.String(), nil
  588. }