| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package main
- import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "os"
- "path"
- "reflect"
- "strconv"
- "strings"
- )
- type fix struct {
- name string
- date string // date that fix was introduced, in YYYY-MM-DD format
- f func(*ast.File) bool
- desc string
- }
- // main runs sort.Sort(byName(fixes)) before printing list of fixes.
- type byName []fix
- func (f byName) Len() int { return len(f) }
- func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
- func (f byName) Less(i, j int) bool { return f[i].name < f[j].name }
- // main runs sort.Sort(byDate(fixes)) before applying fixes.
- type byDate []fix
- func (f byDate) Len() int { return len(f) }
- func (f byDate) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
- func (f byDate) Less(i, j int) bool { return f[i].date < f[j].date }
- var fixes []fix
- func register(f fix) {
- fixes = append(fixes, f)
- }
- // walk traverses the AST x, calling visit(y) for each node y in the tree but
- // also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt,
- // in a bottom-up traversal.
- func walk(x interface{}, visit func(interface{})) {
- walkBeforeAfter(x, nop, visit)
- }
- func nop(interface{}) {}
- // walkBeforeAfter is like walk but calls before(x) before traversing
- // x's children and after(x) afterward.
- func walkBeforeAfter(x interface{}, before, after func(interface{})) {
- before(x)
- switch n := x.(type) {
- default:
- panic(fmt.Errorf("unexpected type %T in walkBeforeAfter", x))
- case nil:
- // pointers to interfaces
- case *ast.Decl:
- walkBeforeAfter(*n, before, after)
- case *ast.Expr:
- walkBeforeAfter(*n, before, after)
- case *ast.Spec:
- walkBeforeAfter(*n, before, after)
- case *ast.Stmt:
- walkBeforeAfter(*n, before, after)
- // pointers to struct pointers
- case **ast.BlockStmt:
- walkBeforeAfter(*n, before, after)
- case **ast.CallExpr:
- walkBeforeAfter(*n, before, after)
- case **ast.FieldList:
- walkBeforeAfter(*n, before, after)
- case **ast.FuncType:
- walkBeforeAfter(*n, before, after)
- case **ast.Ident:
- walkBeforeAfter(*n, before, after)
- case **ast.BasicLit:
- walkBeforeAfter(*n, before, after)
- // pointers to slices
- case *[]ast.Decl:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Expr:
- walkBeforeAfter(*n, before, after)
- case *[]*ast.File:
- walkBeforeAfter(*n, before, after)
- case *[]*ast.Ident:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Spec:
- walkBeforeAfter(*n, before, after)
- case *[]ast.Stmt:
- walkBeforeAfter(*n, before, after)
- // These are ordered and grouped to match ../../pkg/go/ast/ast.go
- case *ast.Field:
- walkBeforeAfter(&n.Names, before, after)
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Tag, before, after)
- case *ast.FieldList:
- for _, field := range n.List {
- walkBeforeAfter(field, before, after)
- }
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.Ellipsis:
- walkBeforeAfter(&n.Elt, before, after)
- case *ast.BasicLit:
- case *ast.FuncLit:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.CompositeLit:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Elts, before, after)
- case *ast.ParenExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.SelectorExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.IndexExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Index, before, after)
- case *ast.SliceExpr:
- walkBeforeAfter(&n.X, before, after)
- if n.Low != nil {
- walkBeforeAfter(&n.Low, before, after)
- }
- if n.High != nil {
- walkBeforeAfter(&n.High, before, after)
- }
- case *ast.TypeAssertExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Type, before, after)
- case *ast.CallExpr:
- walkBeforeAfter(&n.Fun, before, after)
- walkBeforeAfter(&n.Args, before, after)
- case *ast.StarExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.UnaryExpr:
- walkBeforeAfter(&n.X, before, after)
- case *ast.BinaryExpr:
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Y, before, after)
- case *ast.KeyValueExpr:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
- case *ast.ArrayType:
- walkBeforeAfter(&n.Len, before, after)
- walkBeforeAfter(&n.Elt, before, after)
- case *ast.StructType:
- walkBeforeAfter(&n.Fields, before, after)
- case *ast.FuncType:
- walkBeforeAfter(&n.Params, before, after)
- if n.Results != nil {
- walkBeforeAfter(&n.Results, before, after)
- }
- case *ast.InterfaceType:
- walkBeforeAfter(&n.Methods, before, after)
- case *ast.MapType:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
- case *ast.ChanType:
- walkBeforeAfter(&n.Value, before, after)
- case *ast.BadStmt:
- case *ast.DeclStmt:
- walkBeforeAfter(&n.Decl, before, after)
- case *ast.EmptyStmt:
- case *ast.LabeledStmt:
- walkBeforeAfter(&n.Stmt, before, after)
- case *ast.ExprStmt:
- walkBeforeAfter(&n.X, before, after)
- case *ast.SendStmt:
- walkBeforeAfter(&n.Chan, before, after)
- walkBeforeAfter(&n.Value, before, after)
- case *ast.IncDecStmt:
- walkBeforeAfter(&n.X, before, after)
- case *ast.AssignStmt:
- walkBeforeAfter(&n.Lhs, before, after)
- walkBeforeAfter(&n.Rhs, before, after)
- case *ast.GoStmt:
- walkBeforeAfter(&n.Call, before, after)
- case *ast.DeferStmt:
- walkBeforeAfter(&n.Call, before, after)
- case *ast.ReturnStmt:
- walkBeforeAfter(&n.Results, before, after)
- case *ast.BranchStmt:
- case *ast.BlockStmt:
- walkBeforeAfter(&n.List, before, after)
- case *ast.IfStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Cond, before, after)
- walkBeforeAfter(&n.Body, before, after)
- walkBeforeAfter(&n.Else, before, after)
- case *ast.CaseClause:
- walkBeforeAfter(&n.List, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.SwitchStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Tag, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.TypeSwitchStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Assign, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.CommClause:
- walkBeforeAfter(&n.Comm, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.SelectStmt:
- walkBeforeAfter(&n.Body, before, after)
- case *ast.ForStmt:
- walkBeforeAfter(&n.Init, before, after)
- walkBeforeAfter(&n.Cond, before, after)
- walkBeforeAfter(&n.Post, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.RangeStmt:
- walkBeforeAfter(&n.Key, before, after)
- walkBeforeAfter(&n.Value, before, after)
- walkBeforeAfter(&n.X, before, after)
- walkBeforeAfter(&n.Body, before, after)
- case *ast.ImportSpec:
- case *ast.ValueSpec:
- walkBeforeAfter(&n.Type, before, after)
- walkBeforeAfter(&n.Values, before, after)
- walkBeforeAfter(&n.Names, before, after)
- case *ast.TypeSpec:
- walkBeforeAfter(&n.Type, before, after)
- case *ast.BadDecl:
- case *ast.GenDecl:
- walkBeforeAfter(&n.Specs, before, after)
- case *ast.FuncDecl:
- if n.Recv != nil {
- walkBeforeAfter(&n.Recv, before, after)
- }
- walkBeforeAfter(&n.Type, before, after)
- if n.Body != nil {
- walkBeforeAfter(&n.Body, before, after)
- }
- case *ast.File:
- walkBeforeAfter(&n.Decls, before, after)
- case *ast.Package:
- walkBeforeAfter(&n.Files, before, after)
- case []*ast.File:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Decl:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Expr:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []*ast.Ident:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Stmt:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- case []ast.Spec:
- for i := range n {
- walkBeforeAfter(&n[i], before, after)
- }
- }
- after(x)
- }
- // imports returns true if f imports path.
- func imports(f *ast.File, path string) bool {
- return importSpec(f, path) != nil
- }
- // importSpec returns the import spec if f imports path,
- // or nil otherwise.
- func importSpec(f *ast.File, path string) *ast.ImportSpec {
- for _, s := range f.Imports {
- if importPath(s) == path {
- return s
- }
- }
- return nil
- }
- // importPath returns the unquoted import path of s,
- // or "" if the path is not properly quoted.
- func importPath(s *ast.ImportSpec) string {
- t, err := strconv.Unquote(s.Path.Value)
- if err == nil {
- return t
- }
- return ""
- }
- // declImports reports whether gen contains an import of path.
- func declImports(gen *ast.GenDecl, path string) bool {
- if gen.Tok != token.IMPORT {
- return false
- }
- for _, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- if importPath(impspec) == path {
- return true
- }
- }
- return false
- }
- // isPkgDot returns true if t is the expression "pkg.name"
- // where pkg is an imported identifier.
- func isPkgDot(t ast.Expr, pkg, name string) bool {
- sel, ok := t.(*ast.SelectorExpr)
- return ok && isTopName(sel.X, pkg) && sel.Sel.String() == name
- }
- // isPtrPkgDot returns true if f is the expression "*pkg.name"
- // where pkg is an imported identifier.
- func isPtrPkgDot(t ast.Expr, pkg, name string) bool {
- ptr, ok := t.(*ast.StarExpr)
- return ok && isPkgDot(ptr.X, pkg, name)
- }
- // isTopName returns true if n is a top-level unresolved identifier with the given name.
- func isTopName(n ast.Expr, name string) bool {
- id, ok := n.(*ast.Ident)
- return ok && id.Name == name && id.Obj == nil
- }
- // isName returns true if n is an identifier with the given name.
- func isName(n ast.Expr, name string) bool {
- id, ok := n.(*ast.Ident)
- return ok && id.String() == name
- }
- // isCall returns true if t is a call to pkg.name.
- func isCall(t ast.Expr, pkg, name string) bool {
- call, ok := t.(*ast.CallExpr)
- return ok && isPkgDot(call.Fun, pkg, name)
- }
- // If n is an *ast.Ident, isIdent returns it; otherwise isIdent returns nil.
- func isIdent(n interface{}) *ast.Ident {
- id, _ := n.(*ast.Ident)
- return id
- }
- // refersTo returns true if n is a reference to the same object as x.
- func refersTo(n ast.Node, x *ast.Ident) bool {
- id, ok := n.(*ast.Ident)
- // The test of id.Name == x.Name handles top-level unresolved
- // identifiers, which all have Obj == nil.
- return ok && id.Obj == x.Obj && id.Name == x.Name
- }
- // isBlank returns true if n is the blank identifier.
- func isBlank(n ast.Expr) bool {
- return isName(n, "_")
- }
- // isEmptyString returns true if n is an empty string literal.
- func isEmptyString(n ast.Expr) bool {
- lit, ok := n.(*ast.BasicLit)
- return ok && lit.Kind == token.STRING && len(lit.Value) == 2
- }
- func warn(pos token.Pos, msg string, args ...interface{}) {
- if pos.IsValid() {
- msg = "%s: " + msg
- arg1 := []interface{}{fset.Position(pos).String()}
- args = append(arg1, args...)
- }
- fmt.Fprintf(os.Stderr, msg+"\n", args...)
- }
- // countUses returns the number of uses of the identifier x in scope.
- func countUses(x *ast.Ident, scope []ast.Stmt) int {
- count := 0
- ff := func(n interface{}) {
- if n, ok := n.(ast.Node); ok && refersTo(n, x) {
- count++
- }
- }
- for _, n := range scope {
- walk(n, ff)
- }
- return count
- }
- // rewriteUses replaces all uses of the identifier x and !x in scope
- // with f(x.Pos()) and fnot(x.Pos()).
- func rewriteUses(x *ast.Ident, f, fnot func(token.Pos) ast.Expr, scope []ast.Stmt) {
- var lastF ast.Expr
- ff := func(n interface{}) {
- ptr, ok := n.(*ast.Expr)
- if !ok {
- return
- }
- nn := *ptr
- // The child node was just walked and possibly replaced.
- // If it was replaced and this is a negation, replace with fnot(p).
- not, ok := nn.(*ast.UnaryExpr)
- if ok && not.Op == token.NOT && not.X == lastF {
- *ptr = fnot(nn.Pos())
- return
- }
- if refersTo(nn, x) {
- lastF = f(nn.Pos())
- *ptr = lastF
- }
- }
- for _, n := range scope {
- walk(n, ff)
- }
- }
- // assignsTo returns true if any of the code in scope assigns to or takes the address of x.
- func assignsTo(x *ast.Ident, scope []ast.Stmt) bool {
- assigned := false
- ff := func(n interface{}) {
- if assigned {
- return
- }
- switch n := n.(type) {
- case *ast.UnaryExpr:
- // use of &x
- if n.Op == token.AND && refersTo(n.X, x) {
- assigned = true
- return
- }
- case *ast.AssignStmt:
- for _, l := range n.Lhs {
- if refersTo(l, x) {
- assigned = true
- return
- }
- }
- }
- }
- for _, n := range scope {
- if assigned {
- break
- }
- walk(n, ff)
- }
- return assigned
- }
- // newPkgDot returns an ast.Expr referring to "pkg.name" at position pos.
- func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
- return &ast.SelectorExpr{
- X: &ast.Ident{
- NamePos: pos,
- Name: pkg,
- },
- Sel: &ast.Ident{
- NamePos: pos,
- Name: name,
- },
- }
- }
- // renameTop renames all references to the top-level name old.
- // It returns true if it makes any changes.
- func renameTop(f *ast.File, old, new string) bool {
- var fixed bool
- // Rename any conflicting imports
- // (assuming package name is last element of path).
- for _, s := range f.Imports {
- if s.Name != nil {
- if s.Name.Name == old {
- s.Name.Name = new
- fixed = true
- }
- } else {
- _, thisName := path.Split(importPath(s))
- if thisName == old {
- s.Name = ast.NewIdent(new)
- fixed = true
- }
- }
- }
- // Rename any top-level declarations.
- for _, d := range f.Decls {
- switch d := d.(type) {
- case *ast.FuncDecl:
- if d.Recv == nil && d.Name.Name == old {
- d.Name.Name = new
- d.Name.Obj.Name = new
- fixed = true
- }
- case *ast.GenDecl:
- for _, s := range d.Specs {
- switch s := s.(type) {
- case *ast.TypeSpec:
- if s.Name.Name == old {
- s.Name.Name = new
- s.Name.Obj.Name = new
- fixed = true
- }
- case *ast.ValueSpec:
- for _, n := range s.Names {
- if n.Name == old {
- n.Name = new
- n.Obj.Name = new
- fixed = true
- }
- }
- }
- }
- }
- }
- // Rename top-level old to new, both unresolved names
- // (probably defined in another file) and names that resolve
- // to a declaration we renamed.
- walk(f, func(n interface{}) {
- id, ok := n.(*ast.Ident)
- if ok && isTopName(id, old) {
- id.Name = new
- fixed = true
- }
- if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new {
- id.Name = id.Obj.Name
- fixed = true
- }
- })
- return fixed
- }
- // matchLen returns the length of the longest prefix shared by x and y.
- func matchLen(x, y string) int {
- i := 0
- for i < len(x) && i < len(y) && x[i] == y[i] {
- i++
- }
- return i
- }
- // addImport adds the import path to the file f, if absent.
- func addImport(f *ast.File, ipath string) (added bool) {
- if imports(f, ipath) {
- return false
- }
- // Determine name of import.
- // Assume added imports follow convention of using last element.
- _, name := path.Split(ipath)
- // Rename any conflicting top-level references from name to name_.
- renameTop(f, name, name+"_")
- newImport := &ast.ImportSpec{
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote(ipath),
- },
- }
- // Find an import decl to add to.
- var (
- bestMatch = -1
- lastImport = -1
- impDecl *ast.GenDecl
- impIndex = -1
- )
- for i, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if ok && gen.Tok == token.IMPORT {
- lastImport = i
- // Do not add to import "C", to avoid disrupting the
- // association with its doc comment, breaking cgo.
- if declImports(gen, "C") {
- continue
- }
- // Compute longest shared prefix with imports in this block.
- for j, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- n := matchLen(importPath(impspec), ipath)
- if n > bestMatch {
- bestMatch = n
- impDecl = gen
- impIndex = j
- }
- }
- }
- }
- // If no import decl found, add one after the last import.
- if impDecl == nil {
- impDecl = &ast.GenDecl{
- Tok: token.IMPORT,
- }
- f.Decls = append(f.Decls, nil)
- copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
- f.Decls[lastImport+1] = impDecl
- }
- // Ensure the import decl has parentheses, if needed.
- if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() {
- impDecl.Lparen = impDecl.Pos()
- }
- insertAt := impIndex + 1
- if insertAt == 0 {
- insertAt = len(impDecl.Specs)
- }
- impDecl.Specs = append(impDecl.Specs, nil)
- copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
- impDecl.Specs[insertAt] = newImport
- if insertAt > 0 {
- // Assign same position as the previous import,
- // so that the sorter sees it as being in the same block.
- prev := impDecl.Specs[insertAt-1]
- newImport.Path.ValuePos = prev.Pos()
- newImport.EndPos = prev.Pos()
- }
- f.Imports = append(f.Imports, newImport)
- return true
- }
- // deleteImport deletes the import path from the file f, if present.
- func deleteImport(f *ast.File, path string) (deleted bool) {
- oldImport := importSpec(f, path)
- // Find the import node that imports path, if any.
- for i, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if !ok || gen.Tok != token.IMPORT {
- continue
- }
- for j, spec := range gen.Specs {
- impspec := spec.(*ast.ImportSpec)
- if oldImport != impspec {
- continue
- }
- // We found an import spec that imports path.
- // Delete it.
- deleted = true
- copy(gen.Specs[j:], gen.Specs[j+1:])
- gen.Specs = gen.Specs[:len(gen.Specs)-1]
- // If this was the last import spec in this decl,
- // delete the decl, too.
- if len(gen.Specs) == 0 {
- copy(f.Decls[i:], f.Decls[i+1:])
- f.Decls = f.Decls[:len(f.Decls)-1]
- } else if len(gen.Specs) == 1 {
- gen.Lparen = token.NoPos // drop parens
- }
- if j > 0 {
- // We deleted an entry but now there will be
- // a blank line-sized hole where the import was.
- // Close the hole by making the previous
- // import appear to "end" where this one did.
- gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End()
- }
- break
- }
- }
- // Delete it from f.Imports.
- for i, imp := range f.Imports {
- if imp == oldImport {
- copy(f.Imports[i:], f.Imports[i+1:])
- f.Imports = f.Imports[:len(f.Imports)-1]
- break
- }
- }
- return
- }
- // rewriteImport rewrites any import of path oldPath to path newPath.
- func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) {
- for _, imp := range f.Imports {
- if importPath(imp) == oldPath {
- rewrote = true
- // record old End, because the default is to compute
- // it using the length of imp.Path.Value.
- imp.EndPos = imp.End()
- imp.Path.Value = strconv.Quote(newPath)
- }
- }
- return
- }
- func usesImport(f *ast.File, path string) (used bool) {
- spec := importSpec(f, path)
- if spec == nil {
- return
- }
- name := spec.Name.String()
- switch name {
- case "<nil>":
- // If the package name is not explicitly specified,
- // make an educated guess. This is not guaranteed to be correct.
- lastSlash := strings.LastIndex(path, "/")
- if lastSlash == -1 {
- name = path
- } else {
- name = path[lastSlash+1:]
- }
- case "_", ".":
- // Not sure if this import is used - err on the side of caution.
- return true
- }
- walk(f, func(n interface{}) {
- sel, ok := n.(*ast.SelectorExpr)
- if ok && isTopName(sel.X, name) {
- used = true
- }
- })
- return
- }
- func expr(s string) ast.Expr {
- x, err := parser.ParseExpr(s)
- if err != nil {
- panic("parsing " + s + ": " + err.Error())
- }
- // Remove position information to avoid spurious newlines.
- killPos(reflect.ValueOf(x))
- return x
- }
- var posType = reflect.TypeOf(token.Pos(0))
- func killPos(v reflect.Value) {
- switch v.Kind() {
- case reflect.Ptr, reflect.Interface:
- if !v.IsNil() {
- killPos(v.Elem())
- }
- case reflect.Slice:
- n := v.Len()
- for i := 0; i < n; i++ {
- killPos(v.Index(i))
- }
- case reflect.Struct:
- n := v.NumField()
- for i := 0; i < n; i++ {
- f := v.Field(i)
- if f.Type() == posType {
- f.SetInt(0)
- continue
- }
- killPos(f)
- }
- }
- }
- // A Rename describes a single renaming.
- type rename struct {
- OldImport string // only apply rename if this import is present
- NewImport string // add this import during rewrite
- Old string // old name: p.T or *p.T
- New string // new name: p.T or *p.T
- }
- func renameFix(tab []rename) func(*ast.File) bool {
- return func(f *ast.File) bool {
- return renameFixTab(f, tab)
- }
- }
- func parseName(s string) (ptr bool, pkg, nam string) {
- i := strings.Index(s, ".")
- if i < 0 {
- panic("parseName: invalid name " + s)
- }
- if strings.HasPrefix(s, "*") {
- ptr = true
- s = s[1:]
- i--
- }
- pkg = s[:i]
- nam = s[i+1:]
- return
- }
- func renameFixTab(f *ast.File, tab []rename) bool {
- fixed := false
- added := map[string]bool{}
- check := map[string]bool{}
- for _, t := range tab {
- if !imports(f, t.OldImport) {
- continue
- }
- optr, opkg, onam := parseName(t.Old)
- walk(f, func(n interface{}) {
- np, ok := n.(*ast.Expr)
- if !ok {
- return
- }
- x := *np
- if optr {
- p, ok := x.(*ast.StarExpr)
- if !ok {
- return
- }
- x = p.X
- }
- if !isPkgDot(x, opkg, onam) {
- return
- }
- if t.NewImport != "" && !added[t.NewImport] {
- addImport(f, t.NewImport)
- added[t.NewImport] = true
- }
- *np = expr(t.New)
- check[t.OldImport] = true
- fixed = true
- })
- }
- for ipath := range check {
- if !usesImport(f, ipath) {
- deleteImport(f, ipath)
- }
- }
- return fixed
- }
|