| // 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/token" |
| "os" |
| ) |
| |
| type fix struct { |
| name string |
| f func(*ast.File) bool |
| desc string |
| } |
| |
| // main runs sort.Sort(fixes) after init process is done. |
| type fixlist []fix |
| |
| func (f fixlist) Len() int { return len(f) } |
| func (f fixlist) Swap(i, j int) { f[i], f[j] = f[j], f[i] } |
| func (f fixlist) Less(i, j int) bool { return f[i].name < f[j].name } |
| |
| var fixes fixlist |
| |
| func register(f fix) { |
| fixes = append(fixes, f) |
| } |
| |
| // rewrite walks the AST x, calling visit(y) for each node y in the tree but |
| // also with a pointer to each ast.Expr, in a bottom-up traversal. |
| func rewrite(x interface{}, visit func(interface{})) { |
| switch n := x.(type) { |
| case *ast.Expr: |
| rewrite(*n, visit) |
| |
| // everything else just recurses |
| default: |
| panic(fmt.Errorf("unexpected type %T in walk", x, visit)) |
| |
| case nil: |
| |
| // These are ordered and grouped to match ../../pkg/go/ast/ast.go |
| case *ast.Field: |
| rewrite(&n.Type, visit) |
| case *ast.FieldList: |
| for _, field := range n.List { |
| rewrite(field, visit) |
| } |
| case *ast.BadExpr: |
| case *ast.Ident: |
| case *ast.Ellipsis: |
| case *ast.BasicLit: |
| case *ast.FuncLit: |
| rewrite(n.Type, visit) |
| rewrite(n.Body, visit) |
| case *ast.CompositeLit: |
| rewrite(&n.Type, visit) |
| rewrite(n.Elts, visit) |
| case *ast.ParenExpr: |
| rewrite(&n.X, visit) |
| case *ast.SelectorExpr: |
| rewrite(&n.X, visit) |
| case *ast.IndexExpr: |
| rewrite(&n.X, visit) |
| rewrite(&n.Index, visit) |
| case *ast.SliceExpr: |
| rewrite(&n.X, visit) |
| if n.Low != nil { |
| rewrite(&n.Low, visit) |
| } |
| if n.High != nil { |
| rewrite(&n.High, visit) |
| } |
| case *ast.TypeAssertExpr: |
| rewrite(&n.X, visit) |
| rewrite(&n.Type, visit) |
| case *ast.CallExpr: |
| rewrite(&n.Fun, visit) |
| rewrite(n.Args, visit) |
| case *ast.StarExpr: |
| rewrite(&n.X, visit) |
| case *ast.UnaryExpr: |
| rewrite(&n.X, visit) |
| case *ast.BinaryExpr: |
| rewrite(&n.X, visit) |
| rewrite(&n.Y, visit) |
| case *ast.KeyValueExpr: |
| rewrite(&n.Key, visit) |
| rewrite(&n.Value, visit) |
| |
| case *ast.ArrayType: |
| rewrite(&n.Len, visit) |
| rewrite(&n.Elt, visit) |
| case *ast.StructType: |
| rewrite(n.Fields, visit) |
| case *ast.FuncType: |
| rewrite(n.Params, visit) |
| if n.Results != nil { |
| rewrite(n.Results, visit) |
| } |
| case *ast.InterfaceType: |
| rewrite(n.Methods, visit) |
| case *ast.MapType: |
| rewrite(&n.Key, visit) |
| rewrite(&n.Value, visit) |
| case *ast.ChanType: |
| rewrite(&n.Value, visit) |
| |
| case *ast.BadStmt: |
| case *ast.DeclStmt: |
| rewrite(n.Decl, visit) |
| case *ast.EmptyStmt: |
| case *ast.LabeledStmt: |
| rewrite(n.Stmt, visit) |
| case *ast.ExprStmt: |
| rewrite(&n.X, visit) |
| case *ast.SendStmt: |
| rewrite(&n.Chan, visit) |
| rewrite(&n.Value, visit) |
| case *ast.IncDecStmt: |
| rewrite(&n.X, visit) |
| case *ast.AssignStmt: |
| rewrite(n.Lhs, visit) |
| if len(n.Lhs) == 2 && len(n.Rhs) == 1 { |
| rewrite(n.Rhs, visit) |
| } else { |
| rewrite(n.Rhs, visit) |
| } |
| case *ast.GoStmt: |
| rewrite(n.Call, visit) |
| case *ast.DeferStmt: |
| rewrite(n.Call, visit) |
| case *ast.ReturnStmt: |
| rewrite(n.Results, visit) |
| case *ast.BranchStmt: |
| case *ast.BlockStmt: |
| rewrite(n.List, visit) |
| case *ast.IfStmt: |
| rewrite(n.Init, visit) |
| rewrite(&n.Cond, visit) |
| rewrite(n.Body, visit) |
| rewrite(n.Else, visit) |
| case *ast.CaseClause: |
| rewrite(n.List, visit) |
| rewrite(n.Body, visit) |
| case *ast.SwitchStmt: |
| rewrite(n.Init, visit) |
| rewrite(&n.Tag, visit) |
| rewrite(n.Body, visit) |
| case *ast.TypeSwitchStmt: |
| rewrite(n.Init, visit) |
| rewrite(n.Assign, visit) |
| rewrite(n.Body, visit) |
| case *ast.CommClause: |
| rewrite(n.Comm, visit) |
| rewrite(n.Body, visit) |
| case *ast.SelectStmt: |
| rewrite(n.Body, visit) |
| case *ast.ForStmt: |
| rewrite(n.Init, visit) |
| rewrite(&n.Cond, visit) |
| rewrite(n.Post, visit) |
| rewrite(n.Body, visit) |
| case *ast.RangeStmt: |
| rewrite(&n.Key, visit) |
| rewrite(&n.Value, visit) |
| rewrite(&n.X, visit) |
| rewrite(n.Body, visit) |
| |
| case *ast.ImportSpec: |
| case *ast.ValueSpec: |
| rewrite(&n.Type, visit) |
| rewrite(n.Values, visit) |
| case *ast.TypeSpec: |
| rewrite(&n.Type, visit) |
| |
| case *ast.BadDecl: |
| case *ast.GenDecl: |
| rewrite(n.Specs, visit) |
| case *ast.FuncDecl: |
| if n.Recv != nil { |
| rewrite(n.Recv, visit) |
| } |
| rewrite(n.Type, visit) |
| if n.Body != nil { |
| rewrite(n.Body, visit) |
| } |
| |
| case *ast.File: |
| rewrite(n.Decls, visit) |
| |
| case *ast.Package: |
| for _, file := range n.Files { |
| rewrite(file, visit) |
| } |
| |
| case []ast.Decl: |
| for _, d := range n { |
| rewrite(d, visit) |
| } |
| case []ast.Expr: |
| for i := range n { |
| rewrite(&n[i], visit) |
| } |
| case []ast.Stmt: |
| for _, s := range n { |
| rewrite(s, visit) |
| } |
| case []ast.Spec: |
| for _, s := range n { |
| rewrite(s, visit) |
| } |
| } |
| visit(x) |
| } |
| |
| func imports(f *ast.File, path string) bool { |
| for _, decl := range f.Decls { |
| d, ok := decl.(*ast.GenDecl) |
| if !ok { |
| continue |
| } |
| for _, spec := range d.Specs { |
| s, ok := spec.(*ast.ImportSpec) |
| if !ok { |
| continue |
| } |
| if string(s.Path.Value) == `"`+path+`"` { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| |
| func isPkgDot(t ast.Expr, pkg, name string) bool { |
| sel, ok := t.(*ast.SelectorExpr) |
| if !ok { |
| return false |
| } |
| return isName(sel.X, pkg) && sel.Sel.String() == name |
| } |
| |
| func isPtrPkgDot(t ast.Expr, pkg, name string) bool { |
| ptr, ok := t.(*ast.StarExpr) |
| if !ok { |
| return false |
| } |
| return isPkgDot(ptr.X, pkg, name) |
| } |
| |
| func isName(n ast.Expr, name string) bool { |
| id, ok := n.(*ast.Ident) |
| if !ok { |
| return false |
| } |
| return id.String() == name |
| } |
| |
| func isCall(t ast.Expr, pkg, name string) bool { |
| call, ok := t.(*ast.CallExpr) |
| return ok && isPkgDot(call.Fun, pkg, name) |
| } |
| |
| func refersTo(n ast.Node, x *ast.Ident) bool { |
| id, ok := n.(*ast.Ident) |
| if !ok { |
| return false |
| } |
| return id.String() == x.String() |
| } |
| |
| func isBlank(n ast.Expr) bool { |
| return isName(n, "_") |
| } |
| |
| func isEmptyString(n ast.Expr) bool { |
| lit, ok := n.(*ast.BasicLit) |
| if !ok { |
| return false |
| } |
| if lit.Kind != token.STRING { |
| return false |
| } |
| s := string(lit.Value) |
| return s == `""` || s == "``" |
| } |
| |
| func warn(pos token.Pos, msg string, args ...interface{}) { |
| s := "" |
| if pos.IsValid() { |
| s = fmt.Sprintf("%s: ", fset.Position(pos).String()) |
| } |
| fmt.Fprintf(os.Stderr, "%s"+msg+"\n", append([]interface{}{s}, args...)) |
| } |