goparser.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. package goparser
  2. import (
  3. "fmt"
  4. "go/ast"
  5. "go/parser"
  6. "go/token"
  7. "log"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "regexp"
  12. "runtime"
  13. "strings"
  14. "go-common/app/tool/warden/types"
  15. )
  16. var protoFileRegexp *regexp.Regexp
  17. const (
  18. optionsPrefix = "+wd:"
  19. )
  20. func init() {
  21. protoFileRegexp = regexp.MustCompile(`//\s+source:\s+(.*\.proto)`)
  22. }
  23. // GoPackage get go package name from file or directory path
  24. func GoPackage(dpath string) (string, error) {
  25. if strings.HasSuffix(dpath, ".go") {
  26. dpath = filepath.Dir(dpath)
  27. }
  28. absDir, err := filepath.Abs(dpath)
  29. if err != nil {
  30. return "", err
  31. }
  32. goPaths := os.Getenv("GOPATH")
  33. if goPaths == "" {
  34. return "", fmt.Errorf("GOPATH not set")
  35. }
  36. for _, goPath := range strings.Split(goPaths, ":") {
  37. srcPath := path.Join(goPath, "src")
  38. if !strings.HasPrefix(absDir, srcPath) {
  39. continue
  40. }
  41. return strings.Trim(absDir[len(srcPath):], "/"), nil
  42. }
  43. return "", fmt.Errorf("give package not under $GOPATH")
  44. }
  45. // Parse service spec with gived path and receiver name
  46. func Parse(name, dpath, recvName, workDir string) (*types.ServiceSpec, error) {
  47. if workDir == "" {
  48. workDir, _ = os.Getwd()
  49. }
  50. ps := &parseState{
  51. name: strings.Title(name),
  52. dpath: dpath,
  53. recvName: recvName,
  54. workDir: workDir,
  55. }
  56. return ps.parse()
  57. }
  58. type parseState struct {
  59. dpath string
  60. recvName string
  61. name string
  62. workDir string
  63. typedb map[string]types.Typer
  64. importPath string
  65. packageName string
  66. methods []*types.Method
  67. }
  68. func (p *parseState) parse() (spec *types.ServiceSpec, err error) {
  69. p.typedb = make(map[string]types.Typer)
  70. if p.importPath, err = GoPackage(p.dpath); err != nil {
  71. return
  72. }
  73. if err := p.searchMethods(); err != nil {
  74. return nil, err
  75. }
  76. return &types.ServiceSpec{
  77. ImportPath: p.importPath,
  78. Name: p.name,
  79. Package: p.packageName,
  80. Receiver: p.recvName,
  81. Methods: p.methods,
  82. }, nil
  83. }
  84. func (p *parseState) searchMethods() error {
  85. fset := token.NewFileSet()
  86. pkgs, err := parser.ParseDir(fset, p.dpath, nil, parser.ParseComments)
  87. if err != nil {
  88. return err
  89. }
  90. if len(pkgs) == 0 {
  91. return fmt.Errorf("no package found on %s", p.dpath)
  92. }
  93. if len(pkgs) > 1 {
  94. return fmt.Errorf("multiple package found on %s", p.dpath)
  95. }
  96. for pkgName, pkg := range pkgs {
  97. //log.Printf("search method in package %s", pkgName)
  98. p.packageName = pkgName
  99. for fn, f := range pkg.Files {
  100. //log.Printf("search method in file %s", fn)
  101. if err = p.searchMethodsInFile(pkg, f); err != nil {
  102. log.Printf("search method in %s err %s", fn, err)
  103. }
  104. }
  105. }
  106. return nil
  107. }
  108. func (p *parseState) searchMethodsInFile(pkg *ast.Package, f *ast.File) error {
  109. for _, decl := range f.Decls {
  110. funcDecl, ok := decl.(*ast.FuncDecl)
  111. if !ok || !funcDecl.Name.IsExported() || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
  112. continue
  113. }
  114. var recvIdent *ast.Ident
  115. recvField := funcDecl.Recv.List[0]
  116. switch rt := recvField.Type.(type) {
  117. case *ast.Ident:
  118. recvIdent = rt
  119. case *ast.StarExpr:
  120. recvIdent = rt.X.(*ast.Ident)
  121. }
  122. if recvIdent == nil {
  123. return fmt.Errorf("unknown recv %v", recvField)
  124. }
  125. if recvIdent.Name != p.recvName {
  126. continue
  127. }
  128. log.Printf("find method %s", funcDecl.Name.Name)
  129. if err := p.parseFuncDecl(pkg, f, funcDecl); err != nil {
  130. return err
  131. }
  132. }
  133. return nil
  134. }
  135. func (p *parseState) parseFuncDecl(pkg *ast.Package, f *ast.File, funcDecl *ast.FuncDecl) error {
  136. //log.Printf("parse method %s", funcDecl.Name.Name)
  137. comments, options := parseComments(funcDecl)
  138. for _, option := range options {
  139. if option == "ignore" {
  140. log.Printf("ignore method %s", funcDecl.Name.Name)
  141. return nil
  142. }
  143. }
  144. ps := typeState{
  145. File: f,
  146. ImportPath: p.importPath,
  147. Pkg: pkg,
  148. WorkDir: p.workDir,
  149. typedb: p.typedb,
  150. PkgDir: p.dpath,
  151. }
  152. parameters, err := ps.parseFieldList(funcDecl.Type.Params, false)
  153. if err != nil {
  154. return err
  155. }
  156. results, err := ps.parseFieldList(funcDecl.Type.Results, false)
  157. if err != nil {
  158. return err
  159. }
  160. method := &types.Method{
  161. Name: funcDecl.Name.Name,
  162. Comments: comments,
  163. Options: options,
  164. Parameters: parameters,
  165. Results: results,
  166. }
  167. p.methods = append(p.methods, method)
  168. return nil
  169. }
  170. type typeState struct {
  171. typedb map[string]types.Typer
  172. ImportPath string
  173. Pkg *ast.Package
  174. File *ast.File
  175. WorkDir string
  176. PkgDir string
  177. }
  178. func (t *typeState) parseType(expr ast.Expr, ident string) (types.Typer, error) {
  179. oldFile := t.File
  180. defer func() {
  181. t.File = oldFile
  182. }()
  183. switch exp := expr.(type) {
  184. case *ast.Ident:
  185. if isBuildIn(exp.Name) {
  186. return &types.BasicType{Name: exp.Name}, nil
  187. }
  188. tid := fmt.Sprintf("%s-%s-%s", t.ImportPath, t.Pkg.Name, exp.Name)
  189. if ty, ok := t.typedb[tid]; ok {
  190. return ty, nil
  191. }
  192. ty, err := t.searchType(exp)
  193. if err != nil {
  194. return nil, err
  195. }
  196. t.typedb[tid] = ty
  197. return ty, nil
  198. case *ast.StarExpr:
  199. t, err := t.parseType(exp.X, ident)
  200. if err != nil {
  201. return nil, err
  202. }
  203. return t.SetReference(), nil
  204. case *ast.SelectorExpr:
  205. return t.parseSel(exp)
  206. case *ast.ArrayType:
  207. et, err := t.parseType(exp.Elt, ident)
  208. if err != nil {
  209. return nil, err
  210. }
  211. return &types.ArrayType{EltType: et}, nil
  212. case *ast.MapType:
  213. kt, err := t.parseType(exp.Key, ident)
  214. if err != nil {
  215. return nil, err
  216. }
  217. vt, err := t.parseType(exp.Value, ident)
  218. if err != nil {
  219. return nil, err
  220. }
  221. return &types.MapType{KeyType: kt, ValueType: vt}, nil
  222. case *ast.InterfaceType:
  223. return &types.InterfaceType{
  224. ImportPath: t.ImportPath,
  225. Package: t.Pkg.Name,
  226. IdentName: ident,
  227. }, nil
  228. case *ast.StructType:
  229. fields, err := t.parseFieldList(exp.Fields, true)
  230. return &types.StructType{
  231. IdentName: ident,
  232. ImportPath: t.ImportPath,
  233. Package: t.Pkg.Name,
  234. Fields: fields,
  235. ProtoFile: findProtoFile(t.PkgDir, t.File),
  236. }, err
  237. }
  238. return nil, fmt.Errorf("unexpect expr %v", expr)
  239. }
  240. func (t *typeState) searchType(ident *ast.Ident) (types.Typer, error) {
  241. //log.Printf("search type %s", ident.Name)
  242. for fn, f := range t.Pkg.Files {
  243. //log.Printf("search in %s", fn)
  244. for _, decl := range f.Decls {
  245. if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
  246. for _, spec := range genDecl.Specs {
  247. typeSpec, ok := spec.(*ast.TypeSpec)
  248. if !ok {
  249. return nil, fmt.Errorf("expect typeSpec get %v in file %s", spec, fn)
  250. }
  251. if typeSpec.Name.Name == ident.Name {
  252. //log.Printf("found in %s", fn)
  253. t.File = f
  254. return t.parseType(typeSpec.Type, ident.Name)
  255. }
  256. }
  257. }
  258. }
  259. }
  260. return nil, fmt.Errorf("type %s not found in package %s", ident.Name, t.Pkg.Name)
  261. }
  262. func lockType(pkg *ast.Package, ident *ast.Ident) (*ast.File, error) {
  263. //log.Printf("lock type %s", ident.Name)
  264. for fn, f := range pkg.Files {
  265. //log.Printf("search in %s", fn)
  266. for _, decl := range f.Decls {
  267. if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
  268. for _, spec := range genDecl.Specs {
  269. typeSpec, ok := spec.(*ast.TypeSpec)
  270. if !ok {
  271. return nil, fmt.Errorf("expect typeSpec get %v in file %s fn", spec, fn)
  272. }
  273. if typeSpec.Name.Name == ident.Name {
  274. return f, nil
  275. }
  276. }
  277. }
  278. }
  279. }
  280. return nil, fmt.Errorf("type %s not found in package %s", ident.Name, pkg.Name)
  281. }
  282. func (t *typeState) parseFieldList(fl *ast.FieldList, filterExported bool) ([]*types.Field, error) {
  283. fields := make([]*types.Field, 0, fl.NumFields())
  284. if fl == nil {
  285. return fields, nil
  286. }
  287. for _, af := range fl.List {
  288. ty, err := t.parseType(af.Type, "")
  289. if err != nil {
  290. return nil, err
  291. }
  292. if af.Names == nil {
  293. fields = append(fields, &types.Field{Type: ty})
  294. } else {
  295. for _, name := range af.Names {
  296. if filterExported && !name.IsExported() {
  297. continue
  298. }
  299. fields = append(fields, &types.Field{Type: ty, Name: name.Name})
  300. }
  301. }
  302. }
  303. return fields, nil
  304. }
  305. func (t *typeState) parseSel(sel *ast.SelectorExpr) (types.Typer, error) {
  306. //log.Printf("parse sel %v.%v", sel.X, sel.Sel)
  307. x, ok := sel.X.(*ast.Ident)
  308. if !ok {
  309. return nil, fmt.Errorf("unsupport sel.X type %v", sel.X)
  310. }
  311. var pkg *ast.Package
  312. var pkgPath string
  313. var err error
  314. var importPath string
  315. var found bool
  316. var pkgs map[string]*ast.Package
  317. for _, spec := range t.File.Imports {
  318. importPath = strings.Trim(spec.Path.Value, "\"")
  319. if spec.Name != nil && spec.Name.Name == x.Name {
  320. pkgPath, err = importPackage(t.WorkDir, importPath)
  321. if err != nil {
  322. return nil, err
  323. }
  324. pkgs, err = parser.ParseDir(token.NewFileSet(), pkgPath, nil, parser.ParseComments)
  325. if err != nil {
  326. return nil, err
  327. }
  328. pkg, err = filterPkgs(pkgs)
  329. if err != nil {
  330. return nil, err
  331. }
  332. found = true
  333. break
  334. }
  335. pkgPath, err = importPackage(t.WorkDir, importPath)
  336. if err != nil {
  337. return nil, err
  338. }
  339. pkgs, err = parser.ParseDir(token.NewFileSet(), pkgPath, nil, parser.ParseComments)
  340. if err != nil {
  341. return nil, err
  342. }
  343. if pkg, ok = pkgs[x.Name]; ok {
  344. found = true
  345. break
  346. }
  347. }
  348. if !found {
  349. return nil, fmt.Errorf("can't found type %s.%s", x.Name, sel.Sel.Name)
  350. }
  351. file, err := lockType(pkg, sel.Sel)
  352. if err != nil {
  353. return nil, err
  354. }
  355. ts := &typeState{
  356. File: file,
  357. Pkg: pkg,
  358. ImportPath: importPath,
  359. WorkDir: t.WorkDir,
  360. typedb: t.typedb,
  361. PkgDir: pkgPath,
  362. }
  363. return ts.searchType(sel.Sel)
  364. }
  365. func filterPkgs(pkgs map[string]*ast.Package) (*ast.Package, error) {
  366. for pname, pkg := range pkgs {
  367. if strings.HasSuffix(pname, "_test") {
  368. continue
  369. }
  370. return pkg, nil
  371. }
  372. return nil, fmt.Errorf("no package found")
  373. }
  374. func importPackage(workDir, importPath string) (string, error) {
  375. //log.Printf("import package %s", importPath)
  376. searchPaths := make([]string, 0, 3)
  377. searchPaths = append(searchPaths, path.Join(runtime.GOROOT(), "src"))
  378. if vendorDir, ok := searchVendor(workDir); ok {
  379. searchPaths = append(searchPaths, vendorDir)
  380. }
  381. for _, goPath := range strings.Split(os.Getenv("GOPATH"), ":") {
  382. searchPaths = append(searchPaths, path.Join(goPath, "src"))
  383. }
  384. var pkgPath string
  385. var found bool
  386. for _, basePath := range searchPaths {
  387. pkgPath = path.Join(basePath, importPath)
  388. if stat, err := os.Stat(pkgPath); err == nil && stat.IsDir() {
  389. found = true
  390. break
  391. }
  392. }
  393. if !found {
  394. return "", fmt.Errorf("can't import package %s", importPath)
  395. }
  396. return pkgPath, nil
  397. }
  398. func searchVendor(workDir string) (vendorDir string, ok bool) {
  399. var err error
  400. if workDir, err = filepath.Abs(workDir); err != nil {
  401. return "", false
  402. }
  403. goPath := os.Getenv("GOPATH")
  404. for {
  405. if !strings.HasPrefix(workDir, goPath) {
  406. break
  407. }
  408. vendorDir := path.Join(workDir, "vendor")
  409. if stat, err := os.Stat(vendorDir); err == nil && stat.IsDir() {
  410. return vendorDir, true
  411. }
  412. workDir = filepath.Dir(workDir)
  413. }
  414. return
  415. }
  416. func parseComments(funcDecl *ast.FuncDecl) (comments []string, options []string) {
  417. if funcDecl.Doc == nil {
  418. return
  419. }
  420. for _, comment := range funcDecl.Doc.List {
  421. text := strings.TrimLeft(comment.Text, "/ ")
  422. if strings.HasPrefix(text, optionsPrefix) {
  423. options = append(options, text[len(optionsPrefix):])
  424. } else {
  425. comments = append(comments, text)
  426. }
  427. }
  428. return
  429. }
  430. func isBuildIn(t string) bool {
  431. switch t {
  432. case "bool", "byte", "complex128", "complex64", "error", "float32",
  433. "float64", "int", "int16", "int32", "int64", "int8",
  434. "rune", "string", "uint", "uint16", "uint32", "uint64", "uint8", "uintptr":
  435. return true
  436. }
  437. return false
  438. }
  439. func findProtoFile(pkgDir string, f *ast.File) string {
  440. if f.Comments == nil {
  441. return ""
  442. }
  443. for _, comment := range f.Comments {
  444. if comment.List == nil {
  445. continue
  446. }
  447. for _, line := range comment.List {
  448. if protoFile := extractProtoFile(line.Text); protoFile != "" {
  449. fixPath := path.Join(pkgDir, protoFile)
  450. if s, err := os.Stat(fixPath); err == nil && !s.IsDir() {
  451. return fixPath
  452. }
  453. return protoFile
  454. }
  455. }
  456. }
  457. return ""
  458. }
  459. func extractProtoFile(line string) string {
  460. matchs := protoFileRegexp.FindStringSubmatch(line)
  461. if len(matchs) > 1 {
  462. return matchs[1]
  463. }
  464. return ""
  465. }