Commit df93283d authored by Russ Cox's avatar Russ Cox

cmd/fix: delete pre-Go 1 fixes

Assume people who were going to update to Go 1 have done so.
Those with pre-Go 1 trees remaining will need to update first
to Go 1.0 (using its 'go fix') and then to Go 1.1.

Cuts the cmd/fix test time by 99% (3 seconds to 0.03 seconds).

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/7402046
parent 92cbf82f
This diff is collapsed.
// 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(errorTests, errorFn)
}
var errorTests = []testCase{
{
Name: "error.0",
In: `package main
func error() {}
var error int
`,
Out: `package main
func error() {}
var error int
`,
},
{
Name: "error.1",
In: `package main
import "os"
func f() os.Error {
return os.EOF
}
func error() {}
var error int
func g() {
error := 1
_ = error
}
func h(os.Error) {}
func i(...os.Error) {}
`,
Out: `package main
import "io"
func f() error {
return io.EOF
}
func error_() {}
var error_ int
func g() {
error := 1
_ = error
}
func h(error) {}
func i(...error) {}
`,
},
{
Name: "error.2",
In: `package main
import "os"
func f() os.Error {
return os.EOF
}
func g() string {
// these all convert because f is known
if err := f(); err != nil {
return err.String()
}
if err1 := f(); err1 != nil {
return err1.String()
}
if e := f(); e != nil {
return e.String()
}
if x := f(); x != nil {
return x.String()
}
// only the error names (err, err1, e) convert; u is not known
if err := u(); err != nil {
return err.String()
}
if err1 := u(); err1 != nil {
return err1.String()
}
if e := u(); e != nil {
return e.String()
}
if x := u(); x != nil {
return x.String()
}
return ""
}
type T int
func (t T) String() string { return "t" }
type PT int
func (p *PT) String() string { return "pt" }
type MyError int
func (t MyError) String() string { return "myerror" }
type PMyError int
func (p *PMyError) String() string { return "pmyerror" }
func error() {}
var error int
`,
Out: `package main
import "io"
func f() error {
return io.EOF
}
func g() string {
// these all convert because f is known
if err := f(); err != nil {
return err.Error()
}
if err1 := f(); err1 != nil {
return err1.Error()
}
if e := f(); e != nil {
return e.Error()
}
if x := f(); x != nil {
return x.Error()
}
// only the error names (err, err1, e) convert; u is not known
if err := u(); err != nil {
return err.Error()
}
if err1 := u(); err1 != nil {
return err1.Error()
}
if e := u(); e != nil {
return e.Error()
}
if x := u(); x != nil {
return x.String()
}
return ""
}
type T int
func (t T) String() string { return "t" }
type PT int
func (p *PT) String() string { return "pt" }
type MyError int
func (t MyError) Error() string { return "myerror" }
type PMyError int
func (p *PMyError) Error() string { return "pmyerror" }
func error_() {}
var error_ int
`,
},
{
Name: "error.3",
In: `package main
import "os"
func f() os.Error {
return os.EOF
}
type PathError struct {
Name string
Error os.Error
}
func (p *PathError) String() string {
return p.Name + ": " + p.Error.String()
}
func (p *PathError) Error1() string {
p = &PathError{Error: nil}
return fmt.Sprint(p.Name, ": ", p.Error)
}
`,
Out: `package main
import "io"
func f() error {
return io.EOF
}
type PathError struct {
Name string
Err error
}
func (p *PathError) Error() string {
return p.Name + ": " + p.Err.Error()
}
func (p *PathError) Error1() string {
p = &PathError{Err: nil}
return fmt.Sprint(p.Name, ": ", p.Err)
}
`,
},
}
// 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(filepathFix)
}
var filepathFix = fix{
"filepath",
"2011-06-26",
filepathFunc,
`Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator).
http://codereview.appspot.com/4527090
`,
}
func filepathFunc(f *ast.File) (fixed bool) {
if !imports(f, "path/filepath") {
return
}
walk(f, func(n interface{}) {
e, ok := n.(*ast.Expr)
if !ok {
return
}
var ident string
switch {
case isPkgDot(*e, "filepath", "SeparatorString"):
ident = "filepath.Separator"
case isPkgDot(*e, "filepath", "ListSeparatorString"):
ident = "filepath.ListSeparator"
default:
return
}
// string(filepath.[List]Separator)
*e = &ast.CallExpr{
Fun: ast.NewIdent("string"),
Args: []ast.Expr{ast.NewIdent(ident)},
}
fixed = true
})
return
}
// 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(filepathTests, filepathFunc)
}
var filepathTests = []testCase{
{
Name: "filepath.0",
In: `package main
import (
"path/filepath"
)
var _ = filepath.SeparatorString
var _ = filepath.ListSeparatorString
`,
Out: `package main
import (
"path/filepath"
)
var _ = string(filepath.Separator)
var _ = string(filepath.ListSeparator)
`,
},
}
// 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"
"strings"
)
func init() {
register(go1pkgrenameFix)
}
var go1pkgrenameFix = fix{
"go1rename",
"2011-11-08",
go1pkgrename,
`Rewrite imports for packages moved during transition to Go 1.
http://codereview.appspot.com/5316078
`,
}
var go1PackageRenames = []struct{ old, new string }{
{"asn1", "encoding/asn1"},
{"big", "math/big"},
{"cmath", "math/cmplx"},
{"csv", "encoding/csv"},
{"exec", "os/exec"},
{"exp/template/html", "html/template"},
{"gob", "encoding/gob"},
{"http", "net/http"},
{"http/cgi", "net/http/cgi"},
{"http/fcgi", "net/http/fcgi"},
{"http/httptest", "net/http/httptest"},
{"http/pprof", "net/http/pprof"},
{"json", "encoding/json"},
{"mail", "net/mail"},
{"rpc", "net/rpc"},
{"rpc/jsonrpc", "net/rpc/jsonrpc"},
{"scanner", "text/scanner"},
{"smtp", "net/smtp"},
{"syslog", "log/syslog"},
{"tabwriter", "text/tabwriter"},
{"template", "text/template"},
{"template/parse", "text/template/parse"},
{"rand", "math/rand"},
{"url", "net/url"},
{"utf16", "unicode/utf16"},
{"utf8", "unicode/utf8"},
{"xml", "encoding/xml"},
// go.crypto sub-repository
{"crypto/bcrypt", "code.google.com/p/go.crypto/bcrypt"},
{"crypto/blowfish", "code.google.com/p/go.crypto/blowfish"},
{"crypto/cast5", "code.google.com/p/go.crypto/cast5"},
{"crypto/md4", "code.google.com/p/go.crypto/md4"},
{"crypto/ocsp", "code.google.com/p/go.crypto/ocsp"},
{"crypto/openpgp", "code.google.com/p/go.crypto/openpgp"},
{"crypto/openpgp/armor", "code.google.com/p/go.crypto/openpgp/armor"},
{"crypto/openpgp/elgamal", "code.google.com/p/go.crypto/openpgp/elgamal"},
{"crypto/openpgp/errors", "code.google.com/p/go.crypto/openpgp/errors"},
{"crypto/openpgp/packet", "code.google.com/p/go.crypto/openpgp/packet"},
{"crypto/openpgp/s2k", "code.google.com/p/go.crypto/openpgp/s2k"},
{"crypto/ripemd160", "code.google.com/p/go.crypto/ripemd160"},
{"crypto/twofish", "code.google.com/p/go.crypto/twofish"},
{"crypto/xtea", "code.google.com/p/go.crypto/xtea"},
{"exp/ssh", "code.google.com/p/go.crypto/ssh"},
// go.image sub-repository
{"image/bmp", "code.google.com/p/go.image/bmp"},
{"image/tiff", "code.google.com/p/go.image/tiff"},
// go.net sub-repository
{"net/dict", "code.google.com/p/go.net/dict"},
{"net/websocket", "code.google.com/p/go.net/websocket"},
{"exp/spdy", "code.google.com/p/go.net/spdy"},
{"http/spdy", "code.google.com/p/go.net/spdy"},
// go.codereview sub-repository
{"encoding/git85", "code.google.com/p/go.codereview/git85"},
{"patch", "code.google.com/p/go.codereview/patch"},
// exp
{"ebnf", "exp/ebnf"},
{"go/types", "exp/types"},
// deleted
{"container/vector", ""},
{"exp/datafmt", ""},
{"go/typechecker", ""},
{"old/netchan", ""},
{"old/regexp", ""},
{"old/template", ""},
{"try", ""},
}
var go1PackageNameRenames = []struct{ newPath, old, new string }{
{"html/template", "html", "template"},
{"math/cmplx", "cmath", "cmplx"},
}
func go1pkgrename(f *ast.File) bool {
fixed := false
// First update the imports.
for _, rename := range go1PackageRenames {
spec := importSpec(f, rename.old)
if spec == nil {
continue
}
if rename.new == "" {
warn(spec.Pos(), "package %q has been deleted in Go 1", rename.old)
continue
}
if rewriteImport(f, rename.old, rename.new) {
fixed = true
}
if strings.HasPrefix(rename.new, "exp/") {
warn(spec.Pos(), "package %q is not part of Go 1", rename.new)
}
}
if !fixed {
return false
}
// Now update the package names used by importers.
for _, rename := range go1PackageNameRenames {
// These are rare packages, so do the import test before walking.
if imports(f, rename.newPath) {
walk(f, func(n interface{}) {
if sel, ok := n.(*ast.SelectorExpr); ok {
if isTopName(sel.X, rename.old) {
// We know Sel.X is an Ident.
sel.X.(*ast.Ident).Name = rename.new
return
}
}
})
}
}
return fixed
}
// 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(go1pkgrenameTests, go1pkgrename)
}
var go1pkgrenameTests = []testCase{
{
Name: "go1rename.0",
In: `package main
import (
"asn1"
"big"
"cmath"
"csv"
"exec"
"exp/template/html"
"gob"
"http"
"http/cgi"
"http/fcgi"
"http/httptest"
"http/pprof"
"json"
"mail"
"rand"
"rpc"
"rpc/jsonrpc"
"scanner"
"smtp"
"syslog"
"tabwriter"
"template"
"template/parse"
"url"
"utf16"
"utf8"
"xml"
"crypto/bcrypt"
)
`,
Out: `package main
import (
"encoding/asn1"
"encoding/csv"
"encoding/gob"
"encoding/json"
"encoding/xml"
"html/template"
"log/syslog"
"math/big"
"math/cmplx"
"math/rand"
"net/http"
"net/http/cgi"
"net/http/fcgi"
"net/http/httptest"
"net/http/pprof"
"net/mail"
"net/rpc"
"net/rpc/jsonrpc"
"net/smtp"
"net/url"
"os/exec"
"text/scanner"
"text/tabwriter"
"text/template"
"text/template/parse"
"unicode/utf16"
"unicode/utf8"
"code.google.com/p/go.crypto/bcrypt"
)
`,
},
{
Name: "go1rename.1",
In: `package main
import "cmath"
import poot "exp/template/html"
import (
"ebnf"
"old/regexp"
)
var _ = cmath.Sin
var _ = poot.Poot
`,
Out: `package main
import "math/cmplx"
import poot "html/template"
import (
"exp/ebnf"
"old/regexp"
)
var _ = cmplx.Sin
var _ = poot.Poot
`,
},
{
Name: "go1rename.2",
In: `package foo
import (
"fmt"
"http"
"url"
"google/secret/project/go"
)
func main() {}
`,
Out: `package foo
import (
"fmt"
"net/http"
"net/url"
"google/secret/project/go"
)
func main() {}
`,
},
}
// Copyright 2012 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() {
register(go1renameFix)
}
var go1renameFix = fix{
"go1rename",
"2012-02-12",
renameFix(go1renameReplace),
`Rewrite package-level names that have been renamed in Go 1.
http://codereview.appspot.com/5625045/
http://codereview.appspot.com/5672072/
`,
}
var go1renameReplace = []rename{
{
OldImport: "crypto/aes",
NewImport: "crypto/cipher",
Old: "*aes.Cipher",
New: "cipher.Block",
},
{
OldImport: "crypto/des",
NewImport: "crypto/cipher",
Old: "*des.Cipher",
New: "cipher.Block",
},
{
OldImport: "crypto/des",
NewImport: "crypto/cipher",
Old: "*des.TripleDESCipher",
New: "cipher.Block",
},
{
OldImport: "encoding/json",
NewImport: "",
Old: "json.MarshalForHTML",
New: "json.Marshal",
},
{
OldImport: "net/url",
NewImport: "",
Old: "url.ParseWithReference",
New: "url.Parse",
},
{
OldImport: "net/url",
NewImport: "",
Old: "url.ParseRequest",
New: "url.ParseRequestURI",
},
{
OldImport: "os",
NewImport: "syscall",
Old: "os.Exec",
New: "syscall.Exec",
},
{
OldImport: "runtime",
NewImport: "",
Old: "runtime.Cgocalls",
New: "runtime.NumCgoCall",
},
{
OldImport: "runtime",
NewImport: "",
Old: "runtime.Goroutines",
New: "runtime.NumGoroutine",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ErrPersistEOF",
New: "httputil.ErrPersistEOF",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ErrPipeline",
New: "httputil.ErrPipeline",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ErrClosed",
New: "httputil.ErrClosed",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ServerConn",
New: "httputil.ServerConn",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ClientConn",
New: "httputil.ClientConn",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewChunkedReader",
New: "httputil.NewChunkedReader",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewChunkedWriter",
New: "httputil.NewChunkedWriter",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.ReverseProxy",
New: "httputil.ReverseProxy",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewSingleHostReverseProxy",
New: "httputil.NewSingleHostReverseProxy",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.DumpRequest",
New: "httputil.DumpRequest",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.DumpRequestOut",
New: "httputil.DumpRequestOut",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.DumpResponse",
New: "httputil.DumpResponse",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewClientConn",
New: "httputil.NewClientConn",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewServerConn",
New: "httputil.NewServerConn",
},
{
OldImport: "net/http",
NewImport: "net/http/httputil",
Old: "http.NewProxyClientConn",
New: "httputil.NewProxyClientConn",
},
}
// Copyright 2012 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(go1renameTests, go1renameFix.f)
}
var go1renameTests = []testCase{
{
Name: "go1rename.0",
In: `package main
import (
"crypto/aes"
"crypto/des"
"encoding/json"
"net/http"
"net/url"
"os"
"runtime"
)
var (
_ *aes.Cipher
_ *des.Cipher
_ *des.TripleDESCipher
_ = json.MarshalForHTML
_ = aes.New()
_ = url.Parse
_ = url.ParseWithReference
_ = url.ParseRequest
_ = os.Exec
_ = runtime.Cgocalls
_ = runtime.Goroutines
_ = http.ErrPersistEOF
_ = http.ErrPipeline
_ = http.ErrClosed
_ = http.NewSingleHostReverseProxy
_ = http.NewChunkedReader
_ = http.NewChunkedWriter
_ *http.ReverseProxy
_ *http.ClientConn
_ *http.ServerConn
)
`,
Out: `package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/json"
"net/http/httputil"
"net/url"
"runtime"
"syscall"
)
var (
_ cipher.Block
_ cipher.Block
_ cipher.Block
_ = json.Marshal
_ = aes.New()
_ = url.Parse
_ = url.Parse
_ = url.ParseRequestURI
_ = syscall.Exec
_ = runtime.NumCgoCall
_ = runtime.NumGoroutine
_ = httputil.ErrPersistEOF
_ = httputil.ErrPipeline
_ = httputil.ErrClosed
_ = httputil.NewSingleHostReverseProxy
_ = httputil.NewChunkedReader
_ = httputil.NewChunkedWriter
_ *httputil.ReverseProxy
_ *httputil.ClientConn
_ *httputil.ServerConn
)
`,
},
{
Name: "httputil.0",
In: `package main
import "net/http"
func f() {
http.DumpRequest(nil, false)
http.DumpRequestOut(nil, false)
http.DumpResponse(nil, false)
http.NewChunkedReader(nil)
http.NewChunkedWriter(nil)
http.NewClientConn(nil, nil)
http.NewProxyClientConn(nil, nil)
http.NewServerConn(nil, nil)
http.NewSingleHostReverseProxy(nil)
}
`,
Out: `package main
import "net/http/httputil"
func f() {
httputil.DumpRequest(nil, false)
httputil.DumpRequestOut(nil, false)
httputil.DumpResponse(nil, false)
httputil.NewChunkedReader(nil)
httputil.NewChunkedWriter(nil)
httputil.NewClientConn(nil, nil)
httputil.NewProxyClientConn(nil, nil)
httputil.NewServerConn(nil, nil)
httputil.NewSingleHostReverseProxy(nil)
}
`,
},
{
Name: "httputil.1",
In: `package main
import "net/http"
func f() {
http.DumpRequest(nil, false)
http.DumpRequestOut(nil, false)
http.DumpResponse(nil, false)
http.NewChunkedReader(nil)
http.NewChunkedWriter(nil)
http.NewClientConn(nil, nil)
http.NewProxyClientConn(nil, nil)
http.NewServerConn(nil, nil)
http.NewSingleHostReverseProxy(nil)
}
`,
Out: `package main
import "net/http/httputil"
func f() {
httputil.DumpRequest(nil, false)
httputil.DumpRequestOut(nil, false)
httputil.DumpResponse(nil, false)
httputil.NewChunkedReader(nil)
httputil.NewChunkedWriter(nil)
httputil.NewClientConn(nil, nil)
httputil.NewProxyClientConn(nil, nil)
httputil.NewServerConn(nil, nil)
httputil.NewSingleHostReverseProxy(nil)
}
`,
},
{
Name: "httputil.2",
In: `package main
import "net/http"
func f() {
http.DumpRequest(nil, false)
http.DumpRequestOut(nil, false)
http.DumpResponse(nil, false)
http.NewChunkedReader(nil)
http.NewChunkedWriter(nil)
http.NewClientConn(nil, nil)
http.NewProxyClientConn(nil, nil)
http.NewServerConn(nil, nil)
http.NewSingleHostReverseProxy(nil)
http.Get("")
}
`,
Out: `package main
import (
"net/http"
"net/http/httputil"
)
func f() {
httputil.DumpRequest(nil, false)
httputil.DumpRequestOut(nil, false)
httputil.DumpResponse(nil, false)
httputil.NewChunkedReader(nil)
httputil.NewChunkedWriter(nil)
httputil.NewClientConn(nil, nil)
httputil.NewProxyClientConn(nil, nil)
httputil.NewServerConn(nil, nil)
httputil.NewSingleHostReverseProxy(nil)
http.Get("")
}
`,
},
}
// 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"
"regexp"
)
func init() {
register(googlecodeFix)
}
var googlecodeFix = fix{
"googlecode",
"2011-11-21",
googlecode,
`Rewrite Google Code imports from the deprecated form
"foo.googlecode.com/vcs/path" to "code.google.com/p/foo/path".
`,
}
var googlecodeRe = regexp.MustCompile(`^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$`)
func googlecode(f *ast.File) bool {
fixed := false
for _, s := range f.Imports {
old := importPath(s)
if m := googlecodeRe.FindStringSubmatch(old); m != nil {
new := "code.google.com/p/" + m[1] + m[3]
if rewriteImport(f, old, new) {
fixed = true
}
}
}
return fixed
}
// 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(googlecodeTests, googlecode)
}
var googlecodeTests = []testCase{
{
Name: "googlecode.0",
In: `package main
import (
"foo.googlecode.com/hg/bar"
"go-qux-23.googlecode.com/svn"
"zap.googlecode.com/git/some/path"
)
`,
Out: `package main
import (
"code.google.com/p/foo/bar"
"code.google.com/p/go-qux-23"
"code.google.com/p/zap/some/path"
)
`,
},
}
// 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(hashSumFix)
}
var hashSumFix = fix{
"hashsum",
"2011-11-30",
hashSumFn,
`Pass a nil argument to calls to hash.Sum
This fix rewrites code so that it passes a nil argument to hash.Sum.
The additional argument will allow callers to avoid an
allocation in the future.
http://codereview.appspot.com/5448065
`,
}
// Type-checking configuration: tell the type-checker this basic
// information about types, functions, and variables in external packages.
var hashSumTypeConfig = &TypeConfig{
Var: map[string]string{
"crypto.MD4": "crypto.Hash",
"crypto.MD5": "crypto.Hash",
"crypto.SHA1": "crypto.Hash",
"crypto.SHA224": "crypto.Hash",
"crypto.SHA256": "crypto.Hash",
"crypto.SHA384": "crypto.Hash",
"crypto.SHA512": "crypto.Hash",
"crypto.MD5SHA1": "crypto.Hash",
"crypto.RIPEMD160": "crypto.Hash",
},
Func: map[string]string{
"adler32.New": "hash.Hash",
"crc32.New": "hash.Hash",
"crc32.NewIEEE": "hash.Hash",
"crc64.New": "hash.Hash",
"fnv.New32a": "hash.Hash",
"fnv.New32": "hash.Hash",
"fnv.New64a": "hash.Hash",
"fnv.New64": "hash.Hash",
"hmac.New": "hash.Hash",
"hmac.NewMD5": "hash.Hash",
"hmac.NewSHA1": "hash.Hash",
"hmac.NewSHA256": "hash.Hash",
"md4.New": "hash.Hash",
"md5.New": "hash.Hash",
"ripemd160.New": "hash.Hash",
"sha1.New224": "hash.Hash",
"sha1.New": "hash.Hash",
"sha256.New224": "hash.Hash",
"sha256.New": "hash.Hash",
"sha512.New384": "hash.Hash",
"sha512.New": "hash.Hash",
},
Type: map[string]*Type{
"crypto.Hash": {
Method: map[string]string{
"New": "func() hash.Hash",
},
},
},
}
func hashSumFn(f *ast.File) bool {
typeof, _ := typecheck(hashSumTypeConfig, f)
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if ok && len(call.Args) == 0 {
sel, ok := call.Fun.(*ast.SelectorExpr)
if ok && sel.Sel.Name == "Sum" && typeof[sel.X] == "hash.Hash" {
call.Args = append(call.Args, ast.NewIdent("nil"))
fixed = true
}
}
})
return fixed
}
// 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(hashSumTests, hashSumFn)
}
var hashSumTests = []testCase{
{
Name: "hashsum.0",
In: `package main
import "crypto/sha256"
func f() []byte {
h := sha256.New()
return h.Sum()
}
`,
Out: `package main
import "crypto/sha256"
func f() []byte {
h := sha256.New()
return h.Sum(nil)
}
`,
},
{
Name: "hashsum.1",
In: `package main
func f(h hash.Hash) []byte {
return h.Sum()
}
`,
Out: `package main
func f(h hash.Hash) []byte {
return h.Sum(nil)
}
`,
},
{
Name: "hashsum.0",
In: `package main
import "crypto/sha256"
func f() []byte {
h := sha256.New()
h.Write([]byte("foo"))
digest := h.Sum()
}
`,
Out: `package main
import "crypto/sha256"
func f() []byte {
h := sha256.New()
h.Write([]byte("foo"))
digest := h.Sum(nil)
}
`,
},
{
Name: "hashsum.0",
In: `package main
import _ "crypto/sha256"
import "crypto"
func f() []byte {
hashType := crypto.SHA256
h := hashType.New()
digest := h.Sum()
}
`,
Out: `package main
import _ "crypto/sha256"
import "crypto"
func f() []byte {
hashType := crypto.SHA256
h := hashType.New()
digest := h.Sum(nil)
}
`,
},
}
// 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(hmacNewFix)
}
var hmacNewFix = fix{
"hmacnew",
"2012-01-19",
hmacnew,
`Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256.
This fix rewrites code using hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256 to
use hmac.New:
hmac.NewMD5(key) -> hmac.New(md5.New, key)
hmac.NewSHA1(key) -> hmac.New(sha1.New, key)
hmac.NewSHA256(key) -> hmac.New(sha256.New, key)
`,
}
func hmacnew(f *ast.File) (fixed bool) {
if !imports(f, "crypto/hmac") {
return
}
walk(f, func(n interface{}) {
ce, ok := n.(*ast.CallExpr)
if !ok {
return
}
var pkg string
switch {
case isPkgDot(ce.Fun, "hmac", "NewMD5"):
pkg = "md5"
case isPkgDot(ce.Fun, "hmac", "NewSHA1"):
pkg = "sha1"
case isPkgDot(ce.Fun, "hmac", "NewSHA256"):
pkg = "sha256"
default:
return
}
addImport(f, "crypto/"+pkg)
ce.Fun = ast.NewIdent("hmac.New")
ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...)
fixed = true
})
return
}
// 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(hmacNewTests, hmacnew)
}
var hmacNewTests = []testCase{
{
Name: "hmacnew.0",
In: `package main
import "crypto/hmac"
var f = hmac.NewSHA1([]byte("some key"))
`,
Out: `package main
import (
"crypto/hmac"
"crypto/sha1"
)
var f = hmac.New(sha1.New, []byte("some key"))
`,
},
{
Name: "hmacnew.1",
In: `package main
import "crypto/hmac"
var key = make([]byte, 8)
var f = hmac.NewSHA1(key)
`,
Out: `package main
import (
"crypto/hmac"
"crypto/sha1"
)
var key = make([]byte, 8)
var f = hmac.New(sha1.New, key)
`,
},
{
Name: "hmacnew.2",
In: `package main
import "crypto/hmac"
var f = hmac.NewMD5([]byte("some key"))
`,
Out: `package main
import (
"crypto/hmac"
"crypto/md5"
)
var f = hmac.New(md5.New, []byte("some key"))
`,
},
{
Name: "hmacnew.3",
In: `package main
import "crypto/hmac"
var f = hmac.NewSHA256([]byte("some key"))
`,
Out: `package main
import (
"crypto/hmac"
"crypto/sha256"
)
var f = hmac.New(sha256.New, []byte("some key"))
`,
},
{
Name: "hmacnew.4",
In: `package main
import (
"crypto/hmac"
"crypto/sha1"
)
var f = hmac.New(sha1.New, []byte("some key"))
`,
Out: `package main
import (
"crypto/hmac"
"crypto/sha1"
)
var f = hmac.New(sha1.New, []byte("some key"))
`,
},
}
// 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(htmlerrFix)
}
var htmlerrFix = fix{
"htmlerr",
"2011-11-04",
htmlerr,
`Rename html's Tokenizer.Error method to Err.
http://codereview.appspot.com/5327064/
`,
}
var htmlerrTypeConfig = &TypeConfig{
Func: map[string]string{
"html.NewTokenizer": "html.Tokenizer",
},
}
func htmlerr(f *ast.File) bool {
if !imports(f, "html") {
return false
}
typeof, _ := typecheck(htmlerrTypeConfig, f)
fixed := false
walk(f, func(n interface{}) {
s, ok := n.(*ast.SelectorExpr)
if ok && typeof[s.X] == "html.Tokenizer" && s.Sel.Name == "Error" {
s.Sel.Name = "Err"
fixed = true
}
})
return fixed
}
// 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(htmlerrTests, htmlerr)
}
var htmlerrTests = []testCase{
{
Name: "htmlerr.0",
In: `package main
import (
"html"
)
func f() {
e := errors.New("")
t := html.NewTokenizer(r)
_, _ = e.Error(), t.Error()
}
`,
Out: `package main
import (
"html"
)
func f() {
e := errors.New("")
t := html.NewTokenizer(r)
_, _ = e.Error(), t.Err()
}
`,
},
}
// 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(httpFinalURLFix)
}
var httpFinalURLFix = fix{
"httpfinalurl",
"2011-05-13",
httpfinalurl,
`Adapt http Get calls to not have a finalURL result parameter.
http://codereview.appspot.com/4535056/
`,
}
func httpfinalurl(f *ast.File) bool {
if !imports(f, "http") {
return false
}
fixed := false
walk(f, func(n interface{}) {
// Fix up calls to http.Get.
//
// If they have blank identifiers, remove them:
// resp, _, err := http.Get(url)
// -> resp, err := http.Get(url)
//
// But if they're using the finalURL parameter, warn:
// resp, finalURL, err := http.Get(url)
as, ok := n.(*ast.AssignStmt)
if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 {
return
}
if !isCall(as.Rhs[0], "http", "Get") {
return
}
if isBlank(as.Lhs[1]) {
as.Lhs = []ast.Expr{as.Lhs[0], as.Lhs[2]}
fixed = true
} else {
warn(as.Pos(), "call to http.Get records final URL")
}
})
return fixed
}
// 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(httpfinalurlTests, httpfinalurl)
}
var httpfinalurlTests = []testCase{
{
Name: "finalurl.0",
In: `package main
import (
"http"
)
func f() {
resp, _, err := http.Get("http://www.google.com/")
_, _ = resp, err
}
`,
Out: `package main
import (
"http"
)
func f() {
resp, err := http.Get("http://www.google.com/")
_, _ = resp, err
}
`,
},
}
// 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"
)
func init() {
register(httpFileSystemFix)
}
var httpFileSystemFix = fix{
"httpfs",
"2011-06-27",
httpfs,
`Adapt http FileServer to take a FileSystem.
http://codereview.appspot.com/4629047 http FileSystem interface
`,
}
func httpfs(f *ast.File) bool {
if !imports(f, "http") {
return false
}
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if !ok || !isPkgDot(call.Fun, "http", "FileServer") {
return
}
if len(call.Args) != 2 {
return
}
dir, prefix := call.Args[0], call.Args[1]
call.Args = []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent("http"),
Sel: ast.NewIdent("Dir"),
},
Args: []ast.Expr{dir},
}}
wrapInStripHandler := true
if prefixLit, ok := prefix.(*ast.BasicLit); ok {
if prefixLit.Kind == token.STRING && (prefixLit.Value == `"/"` || prefixLit.Value == `""`) {
wrapInStripHandler = false
}
}
if wrapInStripHandler {
call.Fun.(*ast.SelectorExpr).Sel = ast.NewIdent("StripPrefix")
call.Args = []ast.Expr{
prefix,
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent("http"),
Sel: ast.NewIdent("FileServer"),
},
Args: call.Args,
},
}
}
fixed = true
})
return fixed
}
// 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(httpFileSystemTests, httpfs)
}
var httpFileSystemTests = []testCase{
{
Name: "httpfs.0",
In: `package httpfs
import (
"http"
)
func f() {
_ = http.FileServer("/var/www/foo", "/")
_ = http.FileServer("/var/www/foo", "")
_ = http.FileServer("/var/www/foo/bar", "/bar")
s := "/foo"
_ = http.FileServer(s, "/")
prefix := "/p"
_ = http.FileServer(s, prefix)
}
`,
Out: `package httpfs
import (
"http"
)
func f() {
_ = http.FileServer(http.Dir("/var/www/foo"))
_ = http.FileServer(http.Dir("/var/www/foo"))
_ = http.StripPrefix("/bar", http.FileServer(http.Dir("/var/www/foo/bar")))
s := "/foo"
_ = http.FileServer(http.Dir(s))
prefix := "/p"
_ = http.StripPrefix(prefix, http.FileServer(http.Dir(s)))
}
`,
},
}
// 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(httpHeadersFix)
}
var httpHeadersFix = fix{
"httpheaders",
"2011-06-16",
httpheaders,
`Rename http Referer, UserAgent, Cookie, SetCookie, which are now methods.
http://codereview.appspot.com/4620049/
`,
}
func httpheaders(f *ast.File) bool {
if !imports(f, "http") {
return false
}
called := make(map[ast.Node]bool)
walk(f, func(ni interface{}) {
switch n := ni.(type) {
case *ast.CallExpr:
called[n.Fun] = true
}
})
fixed := false
typeof, _ := typecheck(headerTypeConfig, f)
walk(f, func(ni interface{}) {
switch n := ni.(type) {
case *ast.SelectorExpr:
if called[n] {
break
}
if t := typeof[n.X]; t != "*http.Request" && t != "*http.Response" {
break
}
switch n.Sel.Name {
case "Referer", "UserAgent":
n.Sel.Name += "()"
fixed = true
case "Cookie":
n.Sel.Name = "Cookies()"
fixed = true
}
}
})
return fixed
}
var headerTypeConfig = &TypeConfig{
Type: map[string]*Type{
"*http.Request": {},
"*http.Response": {},
},
}
// 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(httpHeadersTests, httpheaders)
}
var httpHeadersTests = []testCase{
{
Name: "httpheaders.0",
In: `package headertest
import (
"http"
)
type Other struct {
Referer string
UserAgent string
Cookie []*http.Cookie
}
func f(req *http.Request, res *http.Response, other *Other) {
_ = req.Referer
_ = req.UserAgent
_ = req.Cookie
_ = res.Cookie
_ = other.Referer
_ = other.UserAgent
_ = other.Cookie
_ = req.Referer()
_ = req.UserAgent()
_ = req.Cookies()
_ = res.Cookies()
}
`,
Out: `package headertest
import (
"http"
)
type Other struct {
Referer string
UserAgent string
Cookie []*http.Cookie
}
func f(req *http.Request, res *http.Response, other *Other) {
_ = req.Referer()
_ = req.UserAgent()
_ = req.Cookies()
_ = res.Cookies()
_ = other.Referer
_ = other.UserAgent
_ = other.Cookie
_ = req.Referer()
_ = req.UserAgent()
_ = req.Cookies()
_ = res.Cookies()
}
`,
},
}
// 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"
)
func init() {
register(httpserverFix)
}
var httpserverFix = fix{
"httpserver",
"2011-03-15",
httpserver,
`Adapt http server methods and functions to changes
made to the http ResponseWriter interface.
http://codereview.appspot.com/4245064 Hijacker
http://codereview.appspot.com/4239076 Header
http://codereview.appspot.com/4239077 Flusher
http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS
`,
}
func httpserver(f *ast.File) bool {
if !imports(f, "http") {
return false
}
fixed := false
for _, decl := range f.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
w, req, ok := isServeHTTP(fn)
if !ok {
continue
}
walk(fn.Body, func(n interface{}) {
// Want to replace expression sometimes,
// so record pointer to it for updating below.
ptr, ok := n.(*ast.Expr)
if ok {
n = *ptr
}
// Look for w.UsingTLS() and w.Remoteaddr().
call, ok := n.(*ast.CallExpr)
if !ok || (len(call.Args) != 0 && len(call.Args) != 2) {
return
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return
}
if !refersTo(sel.X, w) {
return
}
switch sel.Sel.String() {
case "Hijack":
// replace w with w.(http.Hijacker)
sel.X = &ast.TypeAssertExpr{
X: sel.X,
Type: ast.NewIdent("http.Hijacker"),
}
fixed = true
case "Flush":
// replace w with w.(http.Flusher)
sel.X = &ast.TypeAssertExpr{
X: sel.X,
Type: ast.NewIdent("http.Flusher"),
}
fixed = true
case "UsingTLS":
if ptr == nil {
// can only replace expression if we have pointer to it
break
}
// replace with req.TLS != nil
*ptr = &ast.BinaryExpr{
X: &ast.SelectorExpr{
X: ast.NewIdent(req.String()),
Sel: ast.NewIdent("TLS"),
},
Op: token.NEQ,
Y: ast.NewIdent("nil"),
}
fixed = true
case "RemoteAddr":
if ptr == nil {
// can only replace expression if we have pointer to it
break
}
// replace with req.RemoteAddr
*ptr = &ast.SelectorExpr{
X: ast.NewIdent(req.String()),
Sel: ast.NewIdent("RemoteAddr"),
}
fixed = true
case "SetHeader":
// replace w.SetHeader with w.Header().Set
// or w.Header().Del if second argument is ""
sel.X = &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent(w.String()),
Sel: ast.NewIdent("Header"),
},
}
sel.Sel = ast.NewIdent("Set")
if len(call.Args) == 2 && isEmptyString(call.Args[1]) {
sel.Sel = ast.NewIdent("Del")
call.Args = call.Args[:1]
}
fixed = true
}
})
}
return fixed
}
func isServeHTTP(fn *ast.FuncDecl) (w, req *ast.Ident, ok bool) {
for _, field := range fn.Type.Params.List {
if isPkgDot(field.Type, "http", "ResponseWriter") {
w = field.Names[0]
continue
}
if isPtrPkgDot(field.Type, "http", "Request") {
req = field.Names[0]
continue
}
}
ok = w != nil && req != nil
return
}
// 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(httpserverTests, httpserver)
}
var httpserverTests = []testCase{
{
Name: "httpserver.0",
In: `package main
import "http"
func f(xyz http.ResponseWriter, abc *http.Request, b string) {
xyz.SetHeader("foo", "bar")
xyz.SetHeader("baz", "")
xyz.Hijack()
xyz.Flush()
go xyz.Hijack()
defer xyz.Flush()
_ = xyz.UsingTLS()
_ = true == xyz.UsingTLS()
_ = xyz.RemoteAddr()
_ = xyz.RemoteAddr() == "hello"
if xyz.UsingTLS() {
}
}
`,
Out: `package main
import "http"
func f(xyz http.ResponseWriter, abc *http.Request, b string) {
xyz.Header().Set("foo", "bar")
xyz.Header().Del("baz")
xyz.(http.Hijacker).Hijack()
xyz.(http.Flusher).Flush()
go xyz.(http.Hijacker).Hijack()
defer xyz.(http.Flusher).Flush()
_ = abc.TLS != nil
_ = true == (abc.TLS != nil)
_ = abc.RemoteAddr
_ = abc.RemoteAddr == "hello"
if abc.TLS != nil {
}
}
`,
},
}
// 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(imagecolorFix)
}
var imagecolorFix = fix{
"imagecolor",
"2011-10-04",
imagecolor,
`Adapt code to types moved from image to color.
http://codereview.appspot.com/5132048
`,
}
var colorRenames = []struct{ in, out string }{
{"Color", "Color"},
{"ColorModel", "Model"},
{"ColorModelFunc", "ModelFunc"},
{"PalettedColorModel", "Palette"},
{"RGBAColor", "RGBA"},
{"RGBA64Color", "RGBA64"},
{"NRGBAColor", "NRGBA"},
{"NRGBA64Color", "NRGBA64"},
{"AlphaColor", "Alpha"},
{"Alpha16Color", "Alpha16"},
{"GrayColor", "Gray"},
{"Gray16Color", "Gray16"},
{"RGBAColorModel", "RGBAModel"},
{"RGBA64ColorModel", "RGBA64Model"},
{"NRGBAColorModel", "NRGBAModel"},
{"NRGBA64ColorModel", "NRGBA64Model"},
{"AlphaColorModel", "AlphaModel"},
{"Alpha16ColorModel", "Alpha16Model"},
{"GrayColorModel", "GrayModel"},
{"Gray16ColorModel", "Gray16Model"},
}
func imagecolor(f *ast.File) (fixed bool) {
if !imports(f, "image") {
return
}
walk(f, func(n interface{}) {
s, ok := n.(*ast.SelectorExpr)
if !ok || !isTopName(s.X, "image") {
return
}
switch sel := s.Sel.String(); {
case sel == "ColorImage":
s.Sel = &ast.Ident{Name: "Uniform"}
fixed = true
case sel == "NewColorImage":
s.Sel = &ast.Ident{Name: "NewUniform"}
fixed = true
default:
for _, rename := range colorRenames {
if sel == rename.in {
addImport(f, "image/color")
s.X.(*ast.Ident).Name = "color"
s.Sel.Name = rename.out
fixed = true
}
}
}
})
if fixed && !usesImport(f, "image") {
deleteImport(f, "image")
}
return
}
// 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(colorTests, imagecolor)
}
var colorTests = []testCase{
{
Name: "color.0",
In: `package main
import (
"image"
)
var (
_ image.Image
_ image.RGBA
_ image.Black
_ image.Color
_ image.ColorModel
_ image.ColorModelFunc
_ image.PalettedColorModel
_ image.RGBAColor
_ image.RGBA64Color
_ image.NRGBAColor
_ image.NRGBA64Color
_ image.AlphaColor
_ image.Alpha16Color
_ image.GrayColor
_ image.Gray16Color
)
func f() {
_ = image.RGBAColorModel
_ = image.RGBA64ColorModel
_ = image.NRGBAColorModel
_ = image.NRGBA64ColorModel
_ = image.AlphaColorModel
_ = image.Alpha16ColorModel
_ = image.GrayColorModel
_ = image.Gray16ColorModel
}
`,
Out: `package main
import (
"image"
"image/color"
)
var (
_ image.Image
_ image.RGBA
_ image.Black
_ color.Color
_ color.Model
_ color.ModelFunc
_ color.Palette
_ color.RGBA
_ color.RGBA64
_ color.NRGBA
_ color.NRGBA64
_ color.Alpha
_ color.Alpha16
_ color.Gray
_ color.Gray16
)
func f() {
_ = color.RGBAModel
_ = color.RGBA64Model
_ = color.NRGBAModel
_ = color.NRGBA64Model
_ = color.AlphaModel
_ = color.Alpha16Model
_ = color.GrayModel
_ = color.Gray16Model
}
`,
},
{
Name: "color.1",
In: `package main
import (
"fmt"
"image"
)
func f() {
fmt.Println(image.RGBAColor{1, 2, 3, 4}.RGBA())
}
`,
Out: `package main
import (
"fmt"
"image/color"
)
func f() {
fmt.Println(color.RGBA{1, 2, 3, 4}.RGBA())
}
`,
},
{
Name: "color.2",
In: `package main
import "image"
var c *image.ColorImage = image.NewColorImage(nil)
`,
Out: `package main
import "image"
var c *image.Uniform = image.NewUniform(nil)
`,
},
}
// 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(imagenewFix)
}
var imagenewFix = fix{
"imagenew",
"2011-09-14",
imagenew,
`Adapt image.NewXxx calls to pass an image.Rectangle instead of (w, h int).
http://codereview.appspot.com/4964073
`,
}
var imagenewFuncs = map[string]bool{
"NewRGBA": true,
"NewRGBA64": true,
"NewNRGBA": true,
"NewNRGBA64": true,
"NewAlpha": true,
"NewAlpha16": true,
"NewGray": true,
"NewGray16": true,
}
func imagenew(f *ast.File) bool {
if !imports(f, "image") {
return false
}
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if !ok {
return
}
isNewFunc := false
for newFunc := range imagenewFuncs {
if len(call.Args) == 2 && isPkgDot(call.Fun, "image", newFunc) {
isNewFunc = true
break
}
}
if len(call.Args) == 3 && isPkgDot(call.Fun, "image", "NewPaletted") {
isNewFunc = true
}
if !isNewFunc {
return
}
// Replace image.NewXxx(w, h) with image.NewXxx(image.Rect(0, 0, w, h)).
rectArgs := []ast.Expr{
&ast.BasicLit{Value: "0"},
&ast.BasicLit{Value: "0"},
}
rectArgs = append(rectArgs, call.Args[:2]...)
rect := []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{
Name: "image",
},
Sel: &ast.Ident{
Name: "Rect",
},
},
Args: rectArgs,
},
}
call.Args = append(rect, call.Args[2:]...)
fixed = true
})
return fixed
}
// 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(imagenewTests, imagenew)
}
var imagenewTests = []testCase{
{
Name: "imagenew.0",
In: `package main
import (
"image"
)
func f() {
image.NewRGBA(1, 2)
image.NewRGBA64(1, 2)
image.NewNRGBA(1, 2)
image.NewNRGBA64(1, 2)
image.NewAlpha(1, 2)
image.NewAlpha16(1, 2)
image.NewGray(1, 2)
image.NewGray16(1, 2)
image.NewPaletted(1, 2, nil)
}
`,
Out: `package main
import (
"image"
)
func f() {
image.NewRGBA(image.Rect(0, 0, 1, 2))
image.NewRGBA64(image.Rect(0, 0, 1, 2))
image.NewNRGBA(image.Rect(0, 0, 1, 2))
image.NewNRGBA64(image.Rect(0, 0, 1, 2))
image.NewAlpha(image.Rect(0, 0, 1, 2))
image.NewAlpha16(image.Rect(0, 0, 1, 2))
image.NewGray(image.Rect(0, 0, 1, 2))
image.NewGray16(image.Rect(0, 0, 1, 2))
image.NewPaletted(image.Rect(0, 0, 1, 2), nil)
}
`,
},
}
// 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(imageycbcrFix)
}
var imageycbcrFix = fix{
"imageycbcr",
"2011-12-20",
imageycbcr,
`Adapt code to types moved from image/ycbcr to image and image/color.
http://codereview.appspot.com/5493084
`,
}
func imageycbcr(f *ast.File) (fixed bool) {
if !imports(f, "image/ycbcr") {
return
}
walk(f, func(n interface{}) {
s, ok := n.(*ast.SelectorExpr)
if !ok || !isTopName(s.X, "ycbcr") {
return
}
switch s.Sel.String() {
case "RGBToYCbCr", "YCbCrToRGB":
addImport(f, "image/color")
s.X.(*ast.Ident).Name = "color"
case "YCbCrColor":
addImport(f, "image/color")
s.X.(*ast.Ident).Name = "color"
s.Sel.Name = "YCbCr"
case "YCbCrColorModel":
addImport(f, "image/color")
s.X.(*ast.Ident).Name = "color"
s.Sel.Name = "YCbCrModel"
case "SubsampleRatio", "SubsampleRatio444", "SubsampleRatio422", "SubsampleRatio420":
addImport(f, "image")
s.X.(*ast.Ident).Name = "image"
s.Sel.Name = "YCbCr" + s.Sel.Name
case "YCbCr":
addImport(f, "image")
s.X.(*ast.Ident).Name = "image"
default:
return
}
fixed = true
})
deleteImport(f, "image/ycbcr")
return
}
// 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(ycbcrTests, imageycbcr)
}
var ycbcrTests = []testCase{
{
Name: "ycbcr.0",
In: `package main
import (
"image/ycbcr"
)
func f() {
_ = ycbcr.RGBToYCbCr
_ = ycbcr.YCbCrToRGB
_ = ycbcr.YCbCrColorModel
var _ ycbcr.YCbCrColor
var _ ycbcr.YCbCr
var (
_ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio444
_ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio422
_ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio420
)
}
`,
Out: `package main
import (
"image"
"image/color"
)
func f() {
_ = color.RGBToYCbCr
_ = color.YCbCrToRGB
_ = color.YCbCrModel
var _ color.YCbCr
var _ image.YCbCr
var (
_ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio444
_ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio422
_ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio420
)
}
`,
},
}
// 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(ioCopyNFix)
}
var ioCopyNFix = fix{
"iocopyn",
"2011-09-30",
ioCopyN,
`Rename io.Copyn to io.CopyN.
http://codereview.appspot.com/5157045
`,
}
func ioCopyN(f *ast.File) bool {
if !imports(f, "io") {
return false
}
fixed := false
walk(f, func(n interface{}) {
if expr, ok := n.(ast.Expr); ok {
if isPkgDot(expr, "io", "Copyn") {
expr.(*ast.SelectorExpr).Sel.Name = "CopyN"
fixed = true
return
}
}
})
return fixed
}
// 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(ioCopyNTests, ioCopyN)
}
var ioCopyNTests = []testCase{
{
Name: "io.CopyN.0",
In: `package main
import (
"io"
)
func f() {
io.Copyn(dst, src)
foo.Copyn(dst, src)
}
`,
Out: `package main
import (
"io"
)
func f() {
io.CopyN(dst, src)
foo.Copyn(dst, src)
}
`,
},
}
// 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(mapdeleteFix)
}
var mapdeleteFix = fix{
"mapdelete",
"2011-10-18",
mapdelete,
`Use delete(m, k) instead of m[k] = 0, false.
http://codereview.appspot.com/5272045
`,
}
func mapdelete(f *ast.File) bool {
fixed := false
walk(f, func(n interface{}) {
stmt, ok := n.(*ast.Stmt)
if !ok {
return
}
as, ok := (*stmt).(*ast.AssignStmt)
if !ok || len(as.Lhs) != 1 || len(as.Rhs) != 2 {
return
}
ix, ok := as.Lhs[0].(*ast.IndexExpr)
if !ok {
return
}
if !isTopName(as.Rhs[1], "false") {
warn(as.Pos(), "two-element map assignment with non-false second value")
return
}
if !canDrop(as.Rhs[0]) {
warn(as.Pos(), "two-element map assignment with non-trivial first value")
return
}
*stmt = &ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.Ident{
NamePos: as.Pos(),
Name: "delete",
},
Args: []ast.Expr{ix.X, ix.Index},
},
}
fixed = true
})
return fixed
}
// canDrop reports whether it is safe to drop the
// evaluation of n from the program.
// It is very conservative.
func canDrop(n ast.Expr) bool {
switch n := n.(type) {
case *ast.Ident, *ast.BasicLit:
return true
case *ast.ParenExpr:
return canDrop(n.X)
case *ast.SelectorExpr:
return canDrop(n.X)
case *ast.CompositeLit:
if !canDrop(n.Type) {
return false
}
for _, e := range n.Elts {
if !canDrop(e) {
return false
}
}
return true
case *ast.StarExpr:
// Dropping *x is questionable,
// but we have to be able to drop (*T)(nil).
return canDrop(n.X)
case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType:
return true
}
return false
}
// 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(mapdeleteTests, mapdelete)
}
var mapdeleteTests = []testCase{
{
Name: "mapdelete.0",
In: `package main
func f() {
m[x] = 0, false
m[x] = g(), false
m[x] = 1
delete(m, x)
m[x] = 0, b
}
func g(false bool) {
m[x] = 0, false
}
`,
Out: `package main
func f() {
delete(m, x)
m[x] = g(), false
m[x] = 1
delete(m, x)
m[x] = 0, b
}
func g(false bool) {
m[x] = 0, false
}
`,
},
}
// 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(mathFix)
}
var mathFix = fix{
"math",
"2011-09-29",
math,
`Remove the leading F from math functions such as Fabs.
http://codereview.appspot.com/5158043
`,
}
var mathRenames = []struct{ in, out string }{
{"Fabs", "Abs"},
{"Fdim", "Dim"},
{"Fmax", "Max"},
{"Fmin", "Min"},
{"Fmod", "Mod"},
}
func math(f *ast.File) bool {
if !imports(f, "math") {
return false
}
fixed := false
walk(f, func(n interface{}) {
// Rename functions.
if expr, ok := n.(ast.Expr); ok {
for _, s := range mathRenames {
if isPkgDot(expr, "math", s.in) {
expr.(*ast.SelectorExpr).Sel.Name = s.out
fixed = true
return
}
}
}
})
return fixed
}
// 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(mathTests, math)
}
var mathTests = []testCase{
{
Name: "math.0",
In: `package main
import (
"math"
)
func f() {
math.Fabs(1)
math.Fdim(1)
math.Fmax(1)
math.Fmin(1)
math.Fmod(1)
math.Abs(1)
foo.Fabs(1)
}
`,
Out: `package main
import (
"math"
)
func f() {
math.Abs(1)
math.Dim(1)
math.Max(1)
math.Min(1)
math.Mod(1)
math.Abs(1)
foo.Fabs(1)
}
`,
},
}
// 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(netdialFix)
register(tlsdialFix)
register(netlookupFix)
}
var netdialFix = fix{
"netdial",
"2011-03-28",
netdial,
`Adapt 3-argument calls of net.Dial to use 2-argument form.
http://codereview.appspot.com/4244055
`,
}
var tlsdialFix = fix{
"tlsdial",
"2011-03-28",
tlsdial,
`Adapt 4-argument calls of tls.Dial to use 3-argument form.
http://codereview.appspot.com/4244055
`,
}
var netlookupFix = fix{
"netlookup",
"2011-03-28",
netlookup,
`Adapt 3-result calls to net.LookupHost to use 2-result form.
http://codereview.appspot.com/4244055
`,
}
func netdial(f *ast.File) bool {
if !imports(f, "net") {
return false
}
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if !ok || !isPkgDot(call.Fun, "net", "Dial") || len(call.Args) != 3 {
return
}
// net.Dial(a, "", b) -> net.Dial(a, b)
if !isEmptyString(call.Args[1]) {
warn(call.Pos(), "call to net.Dial with non-empty second argument")
return
}
call.Args[1] = call.Args[2]
call.Args = call.Args[:2]
fixed = true
})
return fixed
}
func tlsdial(f *ast.File) bool {
if !imports(f, "crypto/tls") {
return false
}
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if !ok || !isPkgDot(call.Fun, "tls", "Dial") || len(call.Args) != 4 {
return
}
// tls.Dial(a, "", b, c) -> tls.Dial(a, b, c)
if !isEmptyString(call.Args[1]) {
warn(call.Pos(), "call to tls.Dial with non-empty second argument")
return
}
call.Args[1] = call.Args[2]
call.Args[2] = call.Args[3]
call.Args = call.Args[:3]
fixed = true
})
return fixed
}
func netlookup(f *ast.File) bool {
if !imports(f, "net") {
return false
}
fixed := false
walk(f, func(n interface{}) {
as, ok := n.(*ast.AssignStmt)
if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 {
return
}
call, ok := as.Rhs[0].(*ast.CallExpr)
if !ok || !isPkgDot(call.Fun, "net", "LookupHost") {
return
}
if !isBlank(as.Lhs[2]) {
warn(as.Pos(), "call to net.LookupHost expecting cname; use net.LookupCNAME")
return
}
as.Lhs = as.Lhs[:2]
fixed = true
})
return fixed
}
// 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(netdialTests, nil)
}
var netdialTests = []testCase{
{
Name: "netdial.0",
Fn: netdial,
In: `package main
import "net"
func f() {
c, err := net.Dial(net, "", addr)
c, err = net.Dial(net, "", addr)
}
`,
Out: `package main
import "net"
func f() {
c, err := net.Dial(net, addr)
c, err = net.Dial(net, addr)
}
`,
},
{
Name: "netlookup.0",
Fn: netlookup,
In: `package main
import "net"
func f() {
foo, bar, _ := net.LookupHost(host)
foo, bar, _ = net.LookupHost(host)
}
`,
Out: `package main
import "net"
func f() {
foo, bar := net.LookupHost(host)
foo, bar = net.LookupHost(host)
}
`,
},
}
// 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(netudpgroupFix)
}
var netudpgroupFix = fix{
"netudpgroup",
"2011-08-18",
netudpgroup,
`Adapt 1-argument calls of net.(*UDPConn).JoinGroup, LeaveGroup to use 2-argument form.
http://codereview.appspot.com/4815074
`,
}
func netudpgroup(f *ast.File) bool {
if !imports(f, "net") {
return false
}
fixed := false
for _, d := range f.Decls {
fd, ok := d.(*ast.FuncDecl)
if !ok || fd.Body == nil {
continue
}
walk(fd.Body, func(n interface{}) {
ce, ok := n.(*ast.CallExpr)
if !ok {
return
}
se, ok := ce.Fun.(*ast.SelectorExpr)
if !ok || len(ce.Args) != 1 {
return
}
switch se.Sel.String() {
case "JoinGroup", "LeaveGroup":
// c.JoinGroup(a) -> c.JoinGroup(nil, a)
// c.LeaveGroup(a) -> c.LeaveGroup(nil, a)
arg := ce.Args[0]
ce.Args = make([]ast.Expr, 2)
ce.Args[0] = ast.NewIdent("nil")
ce.Args[1] = arg
fixed = true
}
})
}
return fixed
}
// 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(netudpgroupTests, netudpgroup)
}
var netudpgroupTests = []testCase{
{
Name: "netudpgroup.0",
In: `package main
import "net"
func f() {
err := x.JoinGroup(gaddr)
err = y.LeaveGroup(gaddr)
}
`,
Out: `package main
import "net"
func f() {
err := x.JoinGroup(nil, gaddr)
err = y.LeaveGroup(nil, gaddr)
}
`,
},
// Innocent function with no body.
{
Name: "netudpgroup.1",
In: `package main
import "net"
func f()
var _ net.IP
`,
Out: `package main
import "net"
func f()
var _ net.IP
`,
},
}
// Copyright 2012 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(newWriterFix)
}
var newWriterFix = fix{
"newWriter",
"2012-02-14",
newWriter,
`Adapt bufio, gzip and zlib NewWriterXxx calls for whether they return errors.
Also rename gzip.Compressor and gzip.Decompressor to gzip.Writer and gzip.Reader.
http://codereview.appspot.com/5639057 and
http://codereview.appspot.com/5642054
`,
}
func newWriter(f *ast.File) bool {
if !imports(f, "bufio") && !imports(f, "compress/gzip") && !imports(f, "compress/zlib") {
return false
}
fixed := false
walk(f, func(n interface{}) {
switch n := n.(type) {
case *ast.SelectorExpr:
if isTopName(n.X, "gzip") {
switch n.Sel.String() {
case "Compressor":
n.Sel = &ast.Ident{Name: "Writer"}
fixed = true
case "Decompressor":
n.Sel = &ast.Ident{Name: "Reader"}
fixed = true
}
} else if isTopName(n.X, "zlib") {
if n.Sel.String() == "NewWriterDict" {
n.Sel = &ast.Ident{Name: "NewWriterLevelDict"}
fixed = true
}
}
case *ast.AssignStmt:
// Drop the ", _" in assignments of the form:
// w0, _ = gzip.NewWriter(w1)
if len(n.Lhs) != 2 || len(n.Rhs) != 1 {
return
}
i, ok := n.Lhs[1].(*ast.Ident)
if !ok {
return
}
if i.String() != "_" {
return
}
c, ok := n.Rhs[0].(*ast.CallExpr)
if !ok {
return
}
s, ok := c.Fun.(*ast.SelectorExpr)
if !ok {
return
}
sel := s.Sel.String()
switch {
case isTopName(s.X, "bufio") && (sel == "NewReaderSize" || sel == "NewWriterSize"):
// No-op.
case isTopName(s.X, "gzip") && sel == "NewWriter":
// No-op.
case isTopName(s.X, "zlib") && sel == "NewWriter":
// No-op.
default:
return
}
n.Lhs = n.Lhs[:1]
fixed = true
}
})
return fixed
}
// Copyright 2012 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(newWriterTests, newWriter)
}
var newWriterTests = []testCase{
{
Name: "newWriter.0",
In: `package main
import (
"bufio"
"compress/gzip"
"compress/zlib"
"io"
"foo"
)
func f() *gzip.Compressor {
var (
_ gzip.Compressor
_ *gzip.Decompressor
_ struct {
W *gzip.Compressor
R gzip.Decompressor
}
)
var w io.Writer
br := bufio.NewReader(nil)
br, _ = bufio.NewReaderSize(nil, 256)
bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable.
bw, _ = bufio.NewWriterSize(w, 256)
fw, _ := foo.NewWriter(w)
gw, _ := gzip.NewWriter(w)
gw, _ = gzip.NewWriter(w)
zw, _ := zlib.NewWriter(w)
_ = zlib.NewWriterDict(zw, 0, nil)
return gw
}
`,
Out: `package main
import (
"bufio"
"compress/gzip"
"compress/zlib"
"io"
"foo"
)
func f() *gzip.Writer {
var (
_ gzip.Writer
_ *gzip.Reader
_ struct {
W *gzip.Writer
R gzip.Reader
}
)
var w io.Writer
br := bufio.NewReader(nil)
br = bufio.NewReaderSize(nil, 256)
bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable.
bw = bufio.NewWriterSize(w, 256)
fw, _ := foo.NewWriter(w)
gw := gzip.NewWriter(w)
gw = gzip.NewWriter(w)
zw := zlib.NewWriter(w)
_ = zlib.NewWriterLevelDict(zw, 0, nil)
return gw
}
`,
},
}
// 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(oserrorstringFix)
}
var oserrorstringFix = fix{
"oserrorstring",
"2011-06-22",
oserrorstring,
`Replace os.ErrorString() conversions with calls to os.NewError().
http://codereview.appspot.com/4607052
`,
}
func oserrorstring(f *ast.File) bool {
if !imports(f, "os") {
return false
}
fixed := false
walk(f, func(n interface{}) {
// The conversion os.ErrorString(x) looks like a call
// of os.ErrorString with one argument.
if call := callExpr(n, "os", "ErrorString"); call != nil {
// os.ErrorString(args) -> os.NewError(args)
call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError"
// os.ErrorString(args) -> os.NewError(args)
call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError"
fixed = true
return
}
// Remove os.Error type from variable declarations initialized
// with an os.NewError.
// (An *ast.ValueSpec may also be used in a const declaration
// but those won't be initialized with a call to os.NewError.)
if spec, ok := n.(*ast.ValueSpec); ok &&
len(spec.Names) == 1 &&
isPkgDot(spec.Type, "os", "Error") &&
len(spec.Values) == 1 &&
callExpr(spec.Values[0], "os", "NewError") != nil {
// var name os.Error = os.NewError(x) ->
// var name = os.NewError(x)
spec.Type = nil
fixed = true
return
}
// Other occurrences of os.ErrorString are not fixed
// but they are rare.
})
return fixed
}
// callExpr returns the call expression if x is a call to pkg.name with one argument;
// otherwise it returns nil.
func callExpr(x interface{}, pkg, name string) *ast.CallExpr {
if call, ok := x.(*ast.CallExpr); ok &&
len(call.Args) == 1 &&
isPkgDot(call.Fun, pkg, name) {
return call
}
return nil
}
// 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(oserrorstringTests, oserrorstring)
}
var oserrorstringTests = []testCase{
{
Name: "oserrorstring.0",
In: `package main
import "os"
var _ = os.ErrorString("foo")
var _ os.Error = os.ErrorString("bar1")
var _ os.Error = os.NewError("bar2")
var _ os.Error = MyError("bal") // don't rewrite this one
var (
_ = os.ErrorString("foo")
_ os.Error = os.ErrorString("bar1")
_ os.Error = os.NewError("bar2")
_ os.Error = MyError("bal") // don't rewrite this one
)
func _() (err os.Error) {
err = os.ErrorString("foo")
return os.ErrorString("foo")
}
`,
Out: `package main
import "os"
var _ = os.NewError("foo")
var _ = os.NewError("bar1")
var _ = os.NewError("bar2")
var _ os.Error = MyError("bal") // don't rewrite this one
var (
_ = os.NewError("foo")
_ = os.NewError("bar1")
_ = os.NewError("bar2")
_ os.Error = MyError("bal") // don't rewrite this one
)
func _() (err os.Error) {
err = os.NewError("foo")
return os.NewError("foo")
}
`,
},
}
// 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(osopenFix)
}
var osopenFix = fix{
"osopen",
"2011-04-04",
osopen,
`Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE.
http://codereview.appspot.com/4357052
`,
}
func osopen(f *ast.File) bool {
if !imports(f, "os") {
return false
}
fixed := false
walk(f, func(n interface{}) {
// Rename O_CREAT to O_CREATE.
if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") {
expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE"
fixed = true
return
}
// Fix up calls to Open.
call, ok := n.(*ast.CallExpr)
if !ok || len(call.Args) != 3 {
return
}
if !isPkgDot(call.Fun, "os", "Open") {
return
}
sel := call.Fun.(*ast.SelectorExpr)
args := call.Args
// os.Open(a, os.O_RDONLY, c) -> os.Open(a)
if isPkgDot(args[1], "os", "O_RDONLY") || isPkgDot(args[1], "syscall", "O_RDONLY") {
call.Args = call.Args[0:1]
fixed = true
return
}
// os.Open(a, createlike_flags, c) -> os.Create(a, c)
if isCreateFlag(args[1]) {
sel.Sel.Name = "Create"
if !isSimplePerm(args[2]) {
warn(sel.Pos(), "rewrote os.Open to os.Create with permission not 0666")
}
call.Args = args[0:1]
fixed = true
return
}
// Fallback: os.Open(a, b, c) -> os.OpenFile(a, b, c)
sel.Sel.Name = "OpenFile"
fixed = true
})
return fixed
}
func isCreateFlag(flag ast.Expr) bool {
foundCreate := false
foundTrunc := false
// OR'ing of flags: is O_CREATE on? + or | would be fine; we just look for os.O_CREATE
// and don't worry about the actual operator.
p := flag.Pos()
for {
lhs := flag
expr, isBinary := flag.(*ast.BinaryExpr)
if isBinary {
lhs = expr.Y
}
sel, ok := lhs.(*ast.SelectorExpr)
if !ok || !isTopName(sel.X, "os") {
return false
}
switch sel.Sel.Name {
case "O_CREATE":
foundCreate = true
case "O_TRUNC":
foundTrunc = true
case "O_RDONLY", "O_WRONLY", "O_RDWR":
// okay
default:
// Unexpected flag, like O_APPEND or O_EXCL.
// Be conservative and do not rewrite.
return false
}
if !isBinary {
break
}
flag = expr.X
}
if !foundCreate {
return false
}
if !foundTrunc {
warn(p, "rewrote os.Open with O_CREATE but not O_TRUNC to os.Create")
}
return foundCreate
}
func isSimplePerm(perm ast.Expr) bool {
basicLit, ok := perm.(*ast.BasicLit)
if !ok {
return false
}
switch basicLit.Value {
case "0666":
return true
}
return false
}
// 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(osopenTests, osopen)
}
var osopenTests = []testCase{
{
Name: "osopen.0",
In: `package main
import (
"os"
)
func f() {
os.OpenFile(a, b, c)
os.Open(a, os.O_RDONLY, 0)
os.Open(a, os.O_RDONLY, 0666)
os.Open(a, os.O_RDWR, 0)
os.Open(a, os.O_CREAT, 0666)
os.Open(a, os.O_CREAT|os.O_TRUNC, 0664)
os.Open(a, os.O_CREATE, 0666)
os.Open(a, os.O_CREATE|os.O_TRUNC, 0664)
os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
os.Open(a, os.O_SURPRISE|os.O_CREATE, 0666)
_ = os.O_CREAT
}
`,
Out: `package main
import (
"os"
)
func f() {
os.OpenFile(a, b, c)
os.Open(a)
os.Open(a)
os.OpenFile(a, os.O_RDWR, 0)
os.Create(a)
os.Create(a)
os.Create(a)
os.Create(a)
os.Create(a)
os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666)
_ = os.O_CREATE
}
`,
},
{
Name: "osopen.1",
In: `package main
import (
"os"
)
func f() {
_ = os.O_CREAT
}
`,
Out: `package main
import (
"os"
)
func f() {
_ = os.O_CREATE
}
`,
},
}
// 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"
)
func init() {
register(procattrFix)
}
var procattrFix = fix{
"procattr",
"2011-03-15",
procattr,
`Adapt calls to os.StartProcess to use new ProcAttr type.
http://codereview.appspot.com/4253052
`,
}
func procattr(f *ast.File) bool {
if !imports(f, "os") && !imports(f, "syscall") {
return false
}
fixed := false
walk(f, func(n interface{}) {
call, ok := n.(*ast.CallExpr)
if !ok || len(call.Args) != 5 {
return
}
var pkg string
if isPkgDot(call.Fun, "os", "StartProcess") {
pkg = "os"
} else if isPkgDot(call.Fun, "syscall", "StartProcess") {
pkg = "syscall"
} else {
return
}
// os.StartProcess(a, b, c, d, e) -> os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e})
lit := &ast.CompositeLit{Type: ast.NewIdent(pkg + ".ProcAttr")}
env, dir, files := call.Args[2], call.Args[3], call.Args[4]
if !isName(env, "nil") && !isCall(env, "os", "Environ") {
lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Env"), Value: env})
}
if !isEmptyString(dir) {
lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Dir"), Value: dir})
}
if !isName(files, "nil") {
lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Files"), Value: files})
}
call.Args[2] = &ast.UnaryExpr{Op: token.AND, X: lit}
call.Args = call.Args[:3]
fixed = true
})
return fixed
}
// 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(procattrTests, procattr)
}
var procattrTests = []testCase{
{
Name: "procattr.0",
In: `package main
import (
"os"
"syscall"
)
func f() {
os.StartProcess(a, b, c, d, e)
os.StartProcess(a, b, os.Environ(), d, e)
os.StartProcess(a, b, nil, d, e)
os.StartProcess(a, b, c, "", e)
os.StartProcess(a, b, c, d, nil)
os.StartProcess(a, b, nil, "", nil)
os.StartProcess(
a,
b,
c,
d,
e,
)
syscall.StartProcess(a, b, c, d, e)
syscall.StartProcess(a, b, os.Environ(), d, e)
syscall.StartProcess(a, b, nil, d, e)
syscall.StartProcess(a, b, c, "", e)
syscall.StartProcess(a, b, c, d, nil)
syscall.StartProcess(a, b, nil, "", nil)
}
`,
Out: `package main
import (
"os"
"syscall"
)
func f() {
os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e})
os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e})
os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e})
os.StartProcess(a, b, &os.ProcAttr{Env: c, Files: e})
os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d})
os.StartProcess(a, b, &os.ProcAttr{})
os.StartProcess(
a,
b, &os.ProcAttr{Env: c, Dir: d, Files: e},
)
syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d, Files: e})
syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e})
syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e})
syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Files: e})
syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d})
syscall.StartProcess(a, b, &syscall.ProcAttr{})
}
`,
},
}
This diff is collapsed.
// 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
// Too slow under race detector.
// +build !race
package main
import (
"io/ioutil"
"log"
"path/filepath"
)
func init() {
addTestCases(reflectTests(), reflectFn)
}
func reflectTests() []testCase {
var tests []testCase
names, _ := filepath.Glob("testdata/reflect.*.in")
for _, in := range names {
out := in[:len(in)-len(".in")] + ".out"
inb, err := ioutil.ReadFile(in)
if err != nil {
log.Fatal(err)
}
outb, err := ioutil.ReadFile(out)
if err != nil {
log.Fatal(err)
}
tests = append(tests, testCase{Name: in, In: string(inb), Out: string(outb)})
}
return tests
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment