Skip to content

Instantly share code, notes, and snippets.

@lavalamp
Created September 2, 2020 17:54
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save lavalamp/f9641969bfc5f327f6a89a70b2f4668a to your computer and use it in GitHub Desktop.
Change a "top level" name (e.g. a package reference) in a go file
package main
import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io/ioutil"
"log"
"os"
)
func main() {
from := os.Args[1]
to := os.Args[2]
file := os.Args[3]
fs := token.NewFileSet()
// Parse the file
fileAST, err := parser.ParseFile(fs, file, nil, parser.ParseComments)
if err != nil {
log.Printf("error: %v", file, err)
return
}
count, collision := modifyTopLevelRefs(fileAST, from, to)
if collision {
log.Fatalf("%q: colliding use of %q, aborting", file, to)
}
if count == 0 {
log.Printf("no uses of %q", from)
return
}
cfg := &printer.Config{
Mode: printer.TabIndent | printer.UseSpaces,
Tabwidth: 8,
}
var outputBuffer bytes.Buffer
if err := cfg.Fprint(&outputBuffer, fs, fileAST); err != nil {
log.Fatalf("couldn't format: %v", err)
}
if err := ioutil.WriteFile(file, outputBuffer.Bytes(), os.ModePerm); err != nil {
log.Fatalf("failed to write file %q: %v", file, err)
}
}
func modifyTopLevelRefs(f *ast.File, from, to string) (count int, collision bool) {
ast.Walk(visitFn(func(n ast.Node) {
sel, ok := n.(*ast.SelectorExpr)
if !ok {
return
}
id, ok := sel.X.(*ast.Ident)
if !ok {
return
}
if id.Obj != nil {
return
}
if id.Name == to {
collision = true
}
if id.Name == from {
count++
id.Name = to
}
}), f)
return
}
type visitFn func(node ast.Node)
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
fn(node)
return fn
}
@lavalamp
Copy link
Author

lavalamp commented Sep 2, 2020

Starting point was https://github.com/KSubedi/gomove and golang.org/x/tools/go/ast/astutil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment