hi
For an experiment, I'm trying to rewrite a method call to use a different package.
for example
package main
import "github.com/pkg/errors"
func main() {
err := errors.New("original error")
errors.Wrap(err, "wrapped error")
}
into
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("original err")
fmt.Errorf("wrapped error: +%v", err)
}
So far I've been using ast functions like this
func processFile(file *ast.File, info *types.Info) {
ast.Inspect(file, func(n ast.Node) bool {
if n, ok := n.(*ast.CallExpr); ok {
if selExpr, ok := n.Fun.(*ast.SelectorExpr); ok {
// check if the call uses pkgErrors path
obj := info.Uses[selExpr.Sel]
if obj.Pkg().Path() != pkgErrors {
return true
}
// rename caller package to 'fmt'
if x, ok := selExpr.X.(*ast.Ident); ok {
x.Name = "fmt"
}
// change method call
switch selExpr.Sel.Name {
case "Wrap":
selExpr.Sel.Name = "Printf"
case "Wrapf":
selExpr.Sel.Name = "Printf"
}
// reverse the arguments
n.Args = append(n.Args[1:], n.Args[0])
// TODO: format first argument by appending ": %w" in the end
}
}
return true // recur
})
astutil.AddImport(fset, file, "errors")
astutil.DeleteImport(fset, file, pkgErrors)
}
And using this function to test it
func TestProcessFile(t *testing.T) {
const src = `package main
import "github.com/pkg/errors"
func main() {
err := errors.New("original error")
errors.Wrap(err, "wrapped error")
}`
file, err := parser.ParseFile(fset, "input.go", src, parser.AllErrors)
if err != nil {
t.Fatal(err)
}
conf := types.Config{Importer: importer.Default()}
info := &types.Info{
Types: map[ast.Expr]types.TypeAndValue{},
Defs: map[*ast.Ident]types.Object{},
Uses: map[*ast.Ident]types.Object{},
Implicits: map[ast.Node]types.Object{},
Selections: map[*ast.SelectorExpr]*types.Selection{},
Scopes: map[ast.Node]*types.Scope{},
}
_, err = conf.Check("cmd/hello", fset, []*ast.File{file}, info)
if err != nil {
t.Fatal(err)
}
processFile(file, info)
//ast.Print(fset, file)
_ = format.Node(os.Stdout, fset, file)
}
So far it gives me the correct output. But I'm wondering if this is the correct approach. It feels like by changing the value of Name I messed up the tokens positions.
Also I've been reading the `gofmt` and `eg`. that uses reflections but I'm not sure if I should use it.
Is there a better approach to achieve the desire result? looking for some pointers to do this.
Regards