mirror of
https://github.com/golang/go
synced 2024-11-22 03:24:41 -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\
|
sortslice.go\
|
||||||
stringssplit.go\
|
stringssplit.go\
|
||||||
template.go\
|
template.go\
|
||||||
|
timefileinfo.go\
|
||||||
typecheck.go\
|
typecheck.go\
|
||||||
url.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