首先采用可视化工具,https://yuroyoro.github.io/goast-viewer/
目标:把errors方法改造成syserror,代码如下:
package main
import(
"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 main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
// src is the input for which we want to inspect the AST.
src := `
package p
const c = 1.0
var X = f(3.14)*2 + c
`
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
f, 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 string
switch x := n.(type) {
case *ast.BasicLit:
s = x.Value
case *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 _8astutil
import (
"go/ast"
"go/format"
"go/parser"
"go/token"
"log"
"os"
"golang.org/x/tools/go/ast/astutil"
)
var src string = `package main
import(
"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.Inspect
astutil.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.New
if sl.Sel.Name != "New" {
break
}
// 断言是否有子属性 sl.X.(*ast.Ident)
if cl, ok := sl.X.(*ast.Ident); ok {
// 判断是否是errors,否则跳过
if cl.Name != "errors" {
break
}
// 替换成syserror
cl.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 _8astutil
import (
"testing"
)
func TestRewrite(t *testing.T) {
Rewrite()
}
执行命令: go test -run “^TestParse$”