mirror of
https://github.com/golang/go
synced 2024-11-24 21:10:04 -07:00
gofix: add time+fileinfo fix
R=adg, rogpeppe, r, cw CC=golang-dev https://golang.org/cl/5450050
This commit is contained in:
parent
a6729b3085
commit
c52b7db470
@ -33,6 +33,7 @@ GOFILES=\
|
||||
sortslice.go\
|
||||
stringssplit.go\
|
||||
template.go\
|
||||
timefileinfo.go\
|
||||
typecheck.go\
|
||||
url.go\
|
||||
|
||||
|
298
src/cmd/gofix/timefileinfo.go
Normal file
298
src/cmd/gofix/timefileinfo.go
Normal file
@ -0,0 +1,298 @@
|
||||
// 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"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register(timefileinfoFix)
|
||||
}
|
||||
|
||||
var timefileinfoFix = fix{
|
||||
"time+fileinfo",
|
||||
"2011-11-29",
|
||||
timefileinfo,
|
||||
`Rewrite for new time and os.FileInfo APIs.
|
||||
|
||||
This fix applies some of the more mechanical changes,
|
||||
but most code will still need manual cleanup.
|
||||
|
||||
http://codereview.appspot.com/5392041
|
||||
http://codereview.appspot.com/5416060
|
||||
`,
|
||||
}
|
||||
|
||||
var timefileinfoTypeConfig = &TypeConfig{
|
||||
Type: map[string]*Type{
|
||||
"os.File": &Type{
|
||||
Method: map[string]string{
|
||||
"Readdir": "func() []*os.FileInfo",
|
||||
"Stat": "func() (*os.FileInfo, error)",
|
||||
},
|
||||
},
|
||||
"time.Time": &Type{
|
||||
Method: map[string]string{
|
||||
"Seconds": "time.raw",
|
||||
"Nanoseconds": "time.raw",
|
||||
},
|
||||
},
|
||||
},
|
||||
Func: map[string]string{
|
||||
"ioutil.ReadDir": "([]*os.FileInfo, error)",
|
||||
"os.Stat": "(*os.FileInfo, error)",
|
||||
"os.Lstat": "(*os.FileInfo, error)",
|
||||
"time.LocalTime": "*time.Time",
|
||||
"time.UTC": "*time.Time",
|
||||
"time.SecondsToLocalTime": "*time.Time",
|
||||
"time.SecondsToUTC": "*time.Time",
|
||||
"time.NanosecondsToLocalTime": "*time.Time",
|
||||
"time.NanosecondsToUTC": "*time.Time",
|
||||
"time.Parse": "(*time.Time, error)",
|
||||
"time.Nanoseconds": "time.raw",
|
||||
"time.Seconds": "time.raw",
|
||||
},
|
||||
}
|
||||
|
||||
// timefileinfoIsOld reports whether f has evidence of being
|
||||
// "old code", from before the API changes. Evidence means:
|
||||
//
|
||||
// a mention of *os.FileInfo (the pointer)
|
||||
// a mention of *time.Time (the pointer)
|
||||
// a mention of old functions from package time
|
||||
// an attempt to call time.UTC
|
||||
//
|
||||
func timefileinfoIsOld(f *ast.File, typeof map[interface{}]string) bool {
|
||||
old := false
|
||||
|
||||
// called records the expressions that appear as
|
||||
// the function part of a function call, so that
|
||||
// we can distinguish a ref to the possibly new time.UTC
|
||||
// from the definitely old time.UTC() function call.
|
||||
called := make(map[interface{}]bool)
|
||||
|
||||
before := func(n interface{}) {
|
||||
if old {
|
||||
return
|
||||
}
|
||||
if star, ok := n.(*ast.StarExpr); ok {
|
||||
if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") {
|
||||
old = true
|
||||
return
|
||||
}
|
||||
}
|
||||
if sel, ok := n.(*ast.SelectorExpr); ok {
|
||||
if isTopName(sel.X, "time") {
|
||||
if timefileinfoOldTimeFunc[sel.Sel.Name] {
|
||||
old = true
|
||||
return
|
||||
}
|
||||
}
|
||||
if typeof[sel.X] == "os.FileInfo" || typeof[sel.X] == "*os.FileInfo" {
|
||||
switch sel.Sel.Name {
|
||||
case "Mtime_ns", "IsDirectory", "IsRegular":
|
||||
old = true
|
||||
return
|
||||
case "Name", "Mode", "Size":
|
||||
if !called[sel] {
|
||||
old = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
call, ok := n.(*ast.CallExpr)
|
||||
if ok && isPkgDot(call.Fun, "time", "UTC") {
|
||||
old = true
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
called[call.Fun] = true
|
||||
}
|
||||
}
|
||||
walkBeforeAfter(f, before, nop)
|
||||
return old
|
||||
}
|
||||
|
||||
var timefileinfoOldTimeFunc = map[string]bool{
|
||||
"LocalTime": true,
|
||||
"SecondsToLocalTime": true,
|
||||
"SecondsToUTC": true,
|
||||
"NanosecondsToLocalTime": true,
|
||||
"NanosecondsToUTC": true,
|
||||
"Seconds": true,
|
||||
"Nanoseconds": true,
|
||||
}
|
||||
|
||||
var isTimeNow = map[string]bool{
|
||||
"LocalTime": true,
|
||||
"UTC": true,
|
||||
"Seconds": true,
|
||||
"Nanoseconds": true,
|
||||
}
|
||||
|
||||
func timefileinfo(f *ast.File) bool {
|
||||
if !imports(f, "os") && !imports(f, "time") && !imports(f, "io/ioutil") {
|
||||
return false
|
||||
}
|
||||
|
||||
typeof, _ := typecheck(timefileinfoTypeConfig, f)
|
||||
|
||||
if !timefileinfoIsOld(f, typeof) {
|
||||
return false
|
||||
}
|
||||
|
||||
fixed := false
|
||||
walk(f, func(n interface{}) {
|
||||
p, ok := n.(*ast.Expr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
nn := *p
|
||||
|
||||
// Rewrite *os.FileInfo and *time.Time to drop the pointer.
|
||||
if star, ok := nn.(*ast.StarExpr); ok {
|
||||
if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") {
|
||||
fixed = true
|
||||
*p = star.X
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite old time API calls to new calls.
|
||||
// The code will still not compile after this edit,
|
||||
// but the compiler will catch that, and the replacement
|
||||
// code will be the correct functions to use in the new API.
|
||||
if sel, ok := nn.(*ast.SelectorExpr); ok && isTopName(sel.X, "time") {
|
||||
fn := sel.Sel.Name
|
||||
if fn == "LocalTime" || fn == "Seconds" || fn == "Nanoseconds" {
|
||||
fixed = true
|
||||
sel.Sel.Name = "Now"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if call, ok := nn.(*ast.CallExpr); ok {
|
||||
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
|
||||
// Rewrite time.UTC but only when called (there's a new time.UTC var now).
|
||||
if isPkgDot(sel, "time", "UTC") {
|
||||
fixed = true
|
||||
sel.Sel.Name = "Now"
|
||||
// rewrite time.Now() into time.Now().UTC()
|
||||
*p = &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: call,
|
||||
Sel: ast.NewIdent("UTC"),
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Rewrite conversions.
|
||||
if ok && isTopName(sel.X, "time") && len(call.Args) == 1 {
|
||||
fn := sel.Sel.Name
|
||||
switch fn {
|
||||
case "SecondsToLocalTime", "SecondsToUTC",
|
||||
"NanosecondsToLocalTime", "NanosecondsToUTC":
|
||||
fixed = true
|
||||
sel.Sel.Name = "Unix"
|
||||
call.Args = append(call.Args, nil)
|
||||
if strings.HasPrefix(fn, "Seconds") {
|
||||
// Unix(sec, 0)
|
||||
call.Args[1] = ast.NewIdent("0")
|
||||
} else {
|
||||
// Unix(0, nsec)
|
||||
call.Args[1] = call.Args[0]
|
||||
call.Args[0] = ast.NewIdent("0")
|
||||
}
|
||||
if strings.HasSuffix(fn, "ToUTC") {
|
||||
// rewrite call into call.UTC()
|
||||
*p = &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: call,
|
||||
Sel: ast.NewIdent("UTC"),
|
||||
},
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite method calls.
|
||||
switch typeof[sel.X] {
|
||||
case "*time.Time", "time.Time":
|
||||
switch sel.Sel.Name {
|
||||
case "Seconds":
|
||||
fixed = true
|
||||
sel.Sel.Name = "Unix"
|
||||
return
|
||||
case "Nanoseconds":
|
||||
fixed = true
|
||||
sel.Sel.Name = "UnixNano"
|
||||
return
|
||||
}
|
||||
|
||||
case "*os.FileInfo", "os.FileInfo":
|
||||
switch sel.Sel.Name {
|
||||
case "IsDirectory":
|
||||
fixed = true
|
||||
sel.Sel.Name = "IsDir"
|
||||
return
|
||||
case "IsRegular":
|
||||
fixed = true
|
||||
sel.Sel.Name = "IsDir"
|
||||
*p = &ast.UnaryExpr{
|
||||
Op: token.NOT,
|
||||
X: call,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite subtraction of two times.
|
||||
// Cannot handle +=/-=.
|
||||
if bin, ok := nn.(*ast.BinaryExpr); ok &&
|
||||
bin.Op == token.SUB &&
|
||||
(typeof[bin.X] == "time.raw" || typeof[bin.Y] == "time.raw") {
|
||||
fixed = true
|
||||
*p = &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: bin.X,
|
||||
Sel: ast.NewIdent("Sub"),
|
||||
},
|
||||
Args: []ast.Expr{bin.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite field references for os.FileInfo.
|
||||
if sel, ok := nn.(*ast.SelectorExpr); ok {
|
||||
if typ := typeof[sel.X]; typ == "*os.FileInfo" || typ == "os.FileInfo" {
|
||||
addCall := false
|
||||
switch sel.Sel.Name {
|
||||
case "Name", "Size", "Mode":
|
||||
fixed = true
|
||||
addCall = true
|
||||
case "Mtime_ns":
|
||||
fixed = true
|
||||
sel.Sel.Name = "ModTime"
|
||||
addCall = true
|
||||
}
|
||||
if addCall {
|
||||
*p = &ast.CallExpr{
|
||||
Fun: sel,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
161
src/cmd/gofix/timefileinfo_test.go
Normal file
161
src/cmd/gofix/timefileinfo_test.go
Normal file
@ -0,0 +1,161 @@
|
||||
// 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
|
||||
|
||||
func init() {
|
||||
addTestCases(timefileinfoTests, timefileinfo)
|
||||
}
|
||||
|
||||
var timefileinfoTests = []testCase{
|
||||
{
|
||||
Name: "timefileinfo.0",
|
||||
In: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
st, _ := os.Stat("/etc/passwd")
|
||||
_ = st.Name
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
st, _ := os.Stat("/etc/passwd")
|
||||
_ = st.Name()
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "timefileinfo.1",
|
||||
In: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
st, _ := os.Stat("/etc/passwd")
|
||||
_ = st.Size
|
||||
_ = st.Mode
|
||||
_ = st.Mtime_ns
|
||||
_ = st.IsDirectory()
|
||||
_ = st.IsRegular()
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
st, _ := os.Stat("/etc/passwd")
|
||||
_ = st.Size()
|
||||
_ = st.Mode()
|
||||
_ = st.ModTime()
|
||||
_ = st.IsDir()
|
||||
_ = !st.IsDir()
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "timefileinfo.2",
|
||||
In: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func f(st *os.FileInfo) {
|
||||
_ = st.Name
|
||||
_ = st.Size
|
||||
_ = st.Mode
|
||||
_ = st.Mtime_ns
|
||||
_ = st.IsDirectory()
|
||||
_ = st.IsRegular()
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import "os"
|
||||
|
||||
func f(st os.FileInfo) {
|
||||
_ = st.Name()
|
||||
_ = st.Size()
|
||||
_ = st.Mode()
|
||||
_ = st.ModTime()
|
||||
_ = st.IsDir()
|
||||
_ = !st.IsDir()
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "timefileinfo.3",
|
||||
In: `package main
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
_ = time.Seconds()
|
||||
_ = time.Nanoseconds()
|
||||
_ = time.LocalTime()
|
||||
_ = time.UTC()
|
||||
_ = time.SecondsToLocalTime(sec)
|
||||
_ = time.SecondsToUTC(sec)
|
||||
_ = time.NanosecondsToLocalTime(nsec)
|
||||
_ = time.NanosecondsToUTC(nsec)
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
_ = time.Now()
|
||||
_ = time.Now()
|
||||
_ = time.Now()
|
||||
_ = time.Now().UTC()
|
||||
_ = time.Unix(sec, 0)
|
||||
_ = time.Unix(sec, 0).UTC()
|
||||
_ = time.Unix(0, nsec)
|
||||
_ = time.Unix(0, nsec).UTC()
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "timefileinfo.4",
|
||||
In: `package main
|
||||
|
||||
import "time"
|
||||
|
||||
func f(*time.Time)
|
||||
|
||||
func main() {
|
||||
t := time.LocalTime()
|
||||
_ = t.Seconds()
|
||||
_ = t.Nanoseconds()
|
||||
|
||||
t1 := time.Nanoseconds()
|
||||
f(nil)
|
||||
t2 := time.Nanoseconds()
|
||||
dt := t2 - t1
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import "time"
|
||||
|
||||
func f(time.Time)
|
||||
|
||||
func main() {
|
||||
t := time.Now()
|
||||
_ = t.Unix()
|
||||
_ = t.UnixNano()
|
||||
|
||||
t1 := time.Now()
|
||||
f(nil)
|
||||
t2 := time.Now()
|
||||
dt := t2.Sub(t1)
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue
Block a user