| // 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 "go/ast" |
| |
| func init() { |
| register(strconvFix) |
| } |
| |
| var strconvFix = fix{ |
| "strconv", |
| "2011-12-01", |
| strconvFn, |
| `Convert to new strconv API. |
| |
| http://codereview.appspot.com/5434095 |
| http://codereview.appspot.com/5434069 |
| `, |
| } |
| |
| func strconvFn(f *ast.File) bool { |
| if !imports(f, "strconv") { |
| return false |
| } |
| |
| fixed := false |
| |
| walk(f, func(n interface{}) { |
| // Rename functions. |
| call, ok := n.(*ast.CallExpr) |
| if !ok || len(call.Args) < 1 { |
| return |
| } |
| sel, ok := call.Fun.(*ast.SelectorExpr) |
| if !ok || !isTopName(sel.X, "strconv") { |
| return |
| } |
| change := func(name string) { |
| fixed = true |
| sel.Sel.Name = name |
| } |
| add := func(s string) { |
| call.Args = append(call.Args, expr(s)) |
| } |
| switch sel.Sel.Name { |
| case "Atob": |
| change("ParseBool") |
| case "Atof32": |
| change("ParseFloat") |
| add("32") // bitSize |
| warn(call.Pos(), "rewrote strconv.Atof32(_) to strconv.ParseFloat(_, 32) but return value must be converted to float32") |
| case "Atof64": |
| change("ParseFloat") |
| add("64") // bitSize |
| case "AtofN": |
| change("ParseFloat") |
| case "Atoi": |
| // Atoi stayed as a convenience wrapper. |
| case "Atoi64": |
| change("ParseInt") |
| add("10") // base |
| add("64") // bitSize |
| case "Atoui": |
| change("ParseUint") |
| add("10") // base |
| add("0") // bitSize |
| warn(call.Pos(), "rewrote strconv.Atoui(_) to strconv.ParseUint(_, 10, 0) but return value must be converted to uint") |
| case "Atoui64": |
| change("ParseUint") |
| add("10") // base |
| add("64") // bitSize |
| case "Btoa": |
| change("FormatBool") |
| case "Btoi64": |
| change("ParseInt") |
| add("64") // bitSize |
| case "Btoui64": |
| change("ParseUint") |
| add("64") // bitSize |
| case "Ftoa32": |
| change("FormatFloat") |
| call.Args[0] = strconvRewrite("float32", "float64", call.Args[0]) |
| add("32") // bitSize |
| case "Ftoa64": |
| change("FormatFloat") |
| add("64") // bitSize |
| case "FtoaN": |
| change("FormatFloat") |
| case "Itoa": |
| // Itoa stayed as a convenience wrapper. |
| case "Itoa64": |
| change("FormatInt") |
| add("10") // base |
| case "Itob": |
| change("FormatInt") |
| call.Args[0] = strconvRewrite("int", "int64", call.Args[0]) |
| case "Itob64": |
| change("FormatInt") |
| case "Uitoa": |
| change("FormatUint") |
| call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) |
| add("10") // base |
| case "Uitoa64": |
| change("FormatUint") |
| add("10") // base |
| case "Uitob": |
| change("FormatUint") |
| call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) |
| case "Uitob64": |
| change("FormatUint") |
| } |
| }) |
| return fixed |
| } |
| |
| // rewrite from type t1 to type t2 |
| // If the expression x is of the form t1(_), use t2(_). Otherwise use t2(x). |
| func strconvRewrite(t1, t2 string, x ast.Expr) ast.Expr { |
| if call, ok := x.(*ast.CallExpr); ok && isTopName(call.Fun, t1) { |
| call.Fun.(*ast.Ident).Name = t2 |
| return x |
| } |
| return &ast.CallExpr{Fun: ast.NewIdent(t2), Args: []ast.Expr{x}} |
| } |