For an experiment, I'm trying to rewrite a method call to use a different 

for example 

package main

import "github.com/pkg/errors"

func main() {
    err := errors.New("original error")
    errors.Wrap(err, "wrapped error")


package main

import (

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 

        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 {

    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 {

    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.


You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 

Reply via email to