首先采用可视化工具,https://yuroyoro.github.io/goast-viewer/
    目标:把errors方法改造成syserror,代码如下:

    1. package main
    2. import(
    3. "errors"
    4. )
    5. func main(){
    6. errors.New("改造")
    7. errors.New("--代码--")
    8. errors.New(Foo("改造代码"))
    9. }
    10. func Ts(){
    11. errors.New("zzz")
    12. }
    13. func Foo(s string){}

    image.png
    我们知道使用go AST内置库就可以实现我们的目标:
    以下引入”http://golang.org/x/tools/go/ast/astutil“工具进行转换。
    astutil package - golang.org/x/tools/go/ast/astutil - Go Packages
    借助官网的例子:ast package - go/ast - Go Packages

    1. package main
    2. import (
    3. "fmt"
    4. "go/ast"
    5. "go/parser"
    6. "go/token"
    7. )
    8. func main() {
    9. // src is the input for which we want to inspect the AST.
    10. src := `
    11. package p
    12. const c = 1.0
    13. var X = f(3.14)*2 + c
    14. `
    15. // Create the AST by parsing src.
    16. fset := token.NewFileSet() // positions are relative to fset
    17. f, err := parser.ParseFile(fset, "src.go", src, 0)
    18. if err != nil {
    19. panic(err)
    20. }
    21. // Inspect the AST and print all identifiers and literals.
    22. ast.Inspect(f, func(n ast.Node) bool {
    23. var s string
    24. switch x := n.(type) {
    25. case *ast.BasicLit:
    26. s = x.Value
    27. case *ast.Ident: // 获取属性
    28. s = x.Name // 此处获取
    29. }
    30. if s != "" {
    31. fmt.Printf("%s:\t%s\n", fset.Position(n.Pos()), s)
    32. }
    33. return true
    34. })
    35. }

    分哪些步骤:
    1.设置可写入文件,token.NewFileSet()
    2.parse解析文件内容。
    3.使用astutil工具,转换新旧路径

    1. func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {

    4.使用方法Cursor变更节点

    1. func(c *astutil.Cursor) bool {

    5.断言类型
    6. 导入file,最终返回file。 astutil.Apply方法类似ast.Inspect
    7.替换字段
    ast.go

    1. package _8astutil
    2. import (
    3. "go/ast"
    4. "go/format"
    5. "go/parser"
    6. "go/token"
    7. "log"
    8. "os"
    9. "golang.org/x/tools/go/ast/astutil"
    10. )
    11. var src string = `package main
    12. import(
    13. "errors"
    14. )
    15. func main(){
    16. errors.New("xxxxx")
    17. errors.New("-----")
    18. errors.New(Foo("asdfasdf"))
    19. }
    20. func Ts(){
    21. errors.New("zzz")
    22. }
    23. func Foo(s string){}
    24. `
    25. func Rewrite() {
    26. // 设置可写入文件
    27. fset := token.NewFileSet()
    28. // 解析文件
    29. file, err := parser.ParseFile(fset, "main.go", src, parser.ParseComments)
    30. if err != nil {
    31. panic(err)
    32. }
    33. //ast.Print(fset, file)
    34. astutil.RewriteImport(fset, file, "errors", "syserrors")
    35. // 导入file,最终返回file。 .Apply方法类似ast.Inspect
    36. astutil.Apply(file, nil, func(c *astutil.Cursor) bool {
    37. n := c.Node()
    38. switch t := n.(type) {
    39. case *ast.CallExpr:
    40. if sl, ok := t.Fun.(*ast.SelectorExpr); ok {
    41. // 检查语句是否是errors.New
    42. if sl.Sel.Name != "New" {
    43. break
    44. }
    45. // 断言是否有子属性 sl.X.(*ast.Ident)
    46. if cl, ok := sl.X.(*ast.Ident); ok {
    47. // 判断是否是errors,否则跳过
    48. if cl.Name != "errors" {
    49. break
    50. }
    51. // 替换成syserror
    52. cl.Name = "syserror"
    53. // 拼接两个数组
    54. t.Args = append([]ast.Expr{&ast.BasicLit{Kind: token.IDENT, Value: "traceID"}}, t.Args...)
    55. }
    56. }
    57. }
    58. return true
    59. })
    60. if err := format.Node(os.Stdout, token.NewFileSet(), file); err != nil {
    61. log.Fatalln("Error:", err)
    62. }
    63. }

    ast_test.go

    1. package _8astutil
    2. import (
    3. "testing"
    4. )
    5. func TestRewrite(t *testing.T) {
    6. Rewrite()
    7. }

    执行命令: go test -run “^TestParse$”