首先采用可视化工具,https://yuroyoro.github.io/goast-viewer/
目标:把errors方法改造成syserror,代码如下:
package mainimport("errors")func main(){errors.New("改造")errors.New("--代码--")errors.New(Foo("改造代码"))}func Ts(){errors.New("zzz")}func Foo(s string){}

我们知道使用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
package mainimport ("fmt""go/ast""go/parser""go/token")func main() {// src is the input for which we want to inspect the AST.src := `package pconst c = 1.0var X = f(3.14)*2 + c`// Create the AST by parsing src.fset := token.NewFileSet() // positions are relative to fsetf, err := parser.ParseFile(fset, "src.go", src, 0)if err != nil {panic(err)}// Inspect the AST and print all identifiers and literals.ast.Inspect(f, func(n ast.Node) bool {var s stringswitch x := n.(type) {case *ast.BasicLit:s = x.Valuecase *ast.Ident: // 获取属性s = x.Name // 此处获取}if s != "" {fmt.Printf("%s:\t%s\n", fset.Position(n.Pos()), s)}return true})}
分哪些步骤:
1.设置可写入文件,token.NewFileSet()
2.parse解析文件内容。
3.使用astutil工具,转换新旧路径
func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
4.使用方法Cursor变更节点
func(c *astutil.Cursor) bool {
5.断言类型
6. 导入file,最终返回file。 astutil.Apply方法类似ast.Inspect
7.替换字段
ast.go
package _8astutilimport ("go/ast""go/format""go/parser""go/token""log""os""golang.org/x/tools/go/ast/astutil")var src string = `package mainimport("errors")func main(){errors.New("xxxxx")errors.New("-----")errors.New(Foo("asdfasdf"))}func Ts(){errors.New("zzz")}func Foo(s string){}`func Rewrite() {// 设置可写入文件fset := token.NewFileSet()// 解析文件file, err := parser.ParseFile(fset, "main.go", src, parser.ParseComments)if err != nil {panic(err)}//ast.Print(fset, file)astutil.RewriteImport(fset, file, "errors", "syserrors")// 导入file,最终返回file。 .Apply方法类似ast.Inspectastutil.Apply(file, nil, func(c *astutil.Cursor) bool {n := c.Node()switch t := n.(type) {case *ast.CallExpr:if sl, ok := t.Fun.(*ast.SelectorExpr); ok {// 检查语句是否是errors.Newif sl.Sel.Name != "New" {break}// 断言是否有子属性 sl.X.(*ast.Ident)if cl, ok := sl.X.(*ast.Ident); ok {// 判断是否是errors,否则跳过if cl.Name != "errors" {break}// 替换成syserrorcl.Name = "syserror"// 拼接两个数组t.Args = append([]ast.Expr{&ast.BasicLit{Kind: token.IDENT, Value: "traceID"}}, t.Args...)}}}return true})if err := format.Node(os.Stdout, token.NewFileSet(), file); err != nil {log.Fatalln("Error:", err)}}
ast_test.go
package _8astutilimport ("testing")func TestRewrite(t *testing.T) {Rewrite()}
执行命令: go test -run “^TestParse$”
