mirror of
https://github.com/golang/go
synced 2024-10-04 17:21:20 -06:00
71d83b72ef
fix, vet yacc is also fixed (it was wrong before) All that's left is the commands used during compilation This looks like a huge CL, but it's almost all file renames. The action is in cmd/go/pkg.go, the Makefiles, and .../doc.go. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5595044
354 lines
9.8 KiB
Go
354 lines
9.8 KiB
Go
// 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"
|
|
"strings"
|
|
)
|
|
|
|
func init() {
|
|
register(errorFix)
|
|
}
|
|
|
|
var errorFix = fix{
|
|
"error",
|
|
"2011-11-02",
|
|
errorFn,
|
|
`Use error instead of os.Error.
|
|
|
|
This fix rewrites code using os.Error to use error:
|
|
|
|
os.Error -> error
|
|
os.NewError -> errors.New
|
|
os.EOF -> io.EOF
|
|
|
|
Seeing the old names above (os.Error and so on) triggers the following
|
|
heuristic rewrites. The heuristics can be forced using the -force=error flag.
|
|
|
|
A top-level function, variable, or constant named error is renamed error_.
|
|
|
|
Error implementations—those types used as os.Error or named
|
|
XxxError—have their String methods renamed to Error. Any existing
|
|
Error field or method is renamed to Err.
|
|
|
|
Error values—those with type os.Error or named e, err, error, err1,
|
|
and so on—have method calls and field references rewritten just
|
|
as the types do (String to Error, Error to Err). Also, a type assertion
|
|
of the form err.(*os.Waitmsg) becomes err.(*exec.ExitError).
|
|
|
|
http://codereview.appspot.com/5305066
|
|
`,
|
|
}
|
|
|
|
// At minimum, this fix applies the following rewrites:
|
|
//
|
|
// os.Error -> error
|
|
// os.NewError -> errors.New
|
|
// os.EOF -> io.EOF
|
|
//
|
|
// However, if can apply any of those rewrites, it assumes that the
|
|
// file predates the error type and tries to update the code to use
|
|
// the new definition for error - an Error method, not a String method.
|
|
// This more heuristic procedure may not be 100% accurate, so it is
|
|
// only run when the file needs updating anyway. The heuristic can
|
|
// be forced to run using -force=error.
|
|
//
|
|
// First, we must identify the implementations of os.Error.
|
|
// These include the type of any value returned as or assigned to an os.Error.
|
|
// To that set we add any type whose name contains "Error" or "error".
|
|
// The heuristic helps for implementations that are not used as os.Error
|
|
// in the file in which they are defined.
|
|
//
|
|
// In any implementation of os.Error, we rename an existing struct field
|
|
// or method named Error to Err and rename the String method to Error.
|
|
//
|
|
// Second, we must identify the values of type os.Error.
|
|
// These include any value that obviously has type os.Error.
|
|
// To that set we add any variable whose name is e or err or error
|
|
// possibly followed by _ or a numeric or capitalized suffix.
|
|
// The heuristic helps for variables that are initialized using calls
|
|
// to functions in other packages. The type checker does not have
|
|
// information about those packages available, and in general cannot
|
|
// (because the packages may themselves not compile).
|
|
//
|
|
// For any value of type os.Error, we replace a call to String with a call to Error.
|
|
// We also replace type assertion err.(*os.Waitmsg) with err.(*exec.ExitError).
|
|
|
|
// Variables matching this regexp are assumed to have type os.Error.
|
|
var errVar = regexp.MustCompile(`^(e|err|error)_?([A-Z0-9].*)?$`)
|
|
|
|
// Types matching this regexp are assumed to be implementations of os.Error.
|
|
var errType = regexp.MustCompile(`^\*?([Ee]rror|.*Error)$`)
|
|
|
|
// Type-checking configuration: tell the type-checker this basic
|
|
// information about types, functions, and variables in external packages.
|
|
var errorTypeConfig = &TypeConfig{
|
|
Type: map[string]*Type{
|
|
"os.Error": {},
|
|
},
|
|
Func: map[string]string{
|
|
"fmt.Errorf": "os.Error",
|
|
"os.NewError": "os.Error",
|
|
},
|
|
Var: map[string]string{
|
|
"os.EPERM": "os.Error",
|
|
"os.ENOENT": "os.Error",
|
|
"os.ESRCH": "os.Error",
|
|
"os.EINTR": "os.Error",
|
|
"os.EIO": "os.Error",
|
|
"os.ENXIO": "os.Error",
|
|
"os.E2BIG": "os.Error",
|
|
"os.ENOEXEC": "os.Error",
|
|
"os.EBADF": "os.Error",
|
|
"os.ECHILD": "os.Error",
|
|
"os.EDEADLK": "os.Error",
|
|
"os.ENOMEM": "os.Error",
|
|
"os.EACCES": "os.Error",
|
|
"os.EFAULT": "os.Error",
|
|
"os.EBUSY": "os.Error",
|
|
"os.EEXIST": "os.Error",
|
|
"os.EXDEV": "os.Error",
|
|
"os.ENODEV": "os.Error",
|
|
"os.ENOTDIR": "os.Error",
|
|
"os.EISDIR": "os.Error",
|
|
"os.EINVAL": "os.Error",
|
|
"os.ENFILE": "os.Error",
|
|
"os.EMFILE": "os.Error",
|
|
"os.ENOTTY": "os.Error",
|
|
"os.EFBIG": "os.Error",
|
|
"os.ENOSPC": "os.Error",
|
|
"os.ESPIPE": "os.Error",
|
|
"os.EROFS": "os.Error",
|
|
"os.EMLINK": "os.Error",
|
|
"os.EPIPE": "os.Error",
|
|
"os.EAGAIN": "os.Error",
|
|
"os.EDOM": "os.Error",
|
|
"os.ERANGE": "os.Error",
|
|
"os.EADDRINUSE": "os.Error",
|
|
"os.ECONNREFUSED": "os.Error",
|
|
"os.ENAMETOOLONG": "os.Error",
|
|
"os.EAFNOSUPPORT": "os.Error",
|
|
"os.ETIMEDOUT": "os.Error",
|
|
"os.ENOTCONN": "os.Error",
|
|
},
|
|
}
|
|
|
|
func errorFn(f *ast.File) bool {
|
|
if !imports(f, "os") && !force["error"] {
|
|
return false
|
|
}
|
|
|
|
// Fix gets called once to run the heuristics described above
|
|
// when we notice that this file definitely needs fixing
|
|
// (it mentions os.Error or something similar).
|
|
var fixed bool
|
|
var didHeuristic bool
|
|
heuristic := func() {
|
|
if didHeuristic {
|
|
return
|
|
}
|
|
didHeuristic = true
|
|
|
|
// We have identified a necessary fix (like os.Error -> error)
|
|
// but have not applied it or any others yet. Prepare the file
|
|
// for fixing and apply heuristic fixes.
|
|
|
|
// Rename error to error_ to make room for error.
|
|
fixed = renameTop(f, "error", "error_") || fixed
|
|
|
|
// Use type checker to build list of error implementations.
|
|
typeof, assign := typecheck(errorTypeConfig, f)
|
|
|
|
isError := map[string]bool{}
|
|
for _, val := range assign["os.Error"] {
|
|
t := typeof[val]
|
|
if strings.HasPrefix(t, "*") {
|
|
t = t[1:]
|
|
}
|
|
if t != "" && !strings.HasPrefix(t, "func(") {
|
|
isError[t] = true
|
|
}
|
|
}
|
|
|
|
// We use both the type check results and the "Error" name heuristic
|
|
// to identify implementations of os.Error.
|
|
isErrorImpl := func(typ string) bool {
|
|
return isError[typ] || errType.MatchString(typ)
|
|
}
|
|
|
|
isErrorVar := func(x ast.Expr) bool {
|
|
if typ := typeof[x]; typ != "" {
|
|
return isErrorImpl(typ) || typ == "os.Error"
|
|
}
|
|
if sel, ok := x.(*ast.SelectorExpr); ok {
|
|
return sel.Sel.Name == "Error" || sel.Sel.Name == "Err"
|
|
}
|
|
if id, ok := x.(*ast.Ident); ok {
|
|
return errVar.MatchString(id.Name)
|
|
}
|
|
return false
|
|
}
|
|
|
|
walk(f, func(n interface{}) {
|
|
// In method declaration on error implementation type,
|
|
// rename String() to Error() and Error() to Err().
|
|
fn, ok := n.(*ast.FuncDecl)
|
|
if ok &&
|
|
fn.Recv != nil &&
|
|
len(fn.Recv.List) == 1 &&
|
|
isErrorImpl(typeName(fn.Recv.List[0].Type)) {
|
|
// Rename.
|
|
switch fn.Name.Name {
|
|
case "String":
|
|
fn.Name.Name = "Error"
|
|
fixed = true
|
|
case "Error":
|
|
fn.Name.Name = "Err"
|
|
fixed = true
|
|
}
|
|
return
|
|
}
|
|
|
|
// In type definition of an error implementation type,
|
|
// rename Error field to Err to make room for method.
|
|
// Given type XxxError struct { ... Error T } rename field to Err.
|
|
d, ok := n.(*ast.GenDecl)
|
|
if ok {
|
|
for _, s := range d.Specs {
|
|
switch s := s.(type) {
|
|
case *ast.TypeSpec:
|
|
if isErrorImpl(typeName(s.Name)) {
|
|
st, ok := s.Type.(*ast.StructType)
|
|
if ok {
|
|
for _, f := range st.Fields.List {
|
|
for _, n := range f.Names {
|
|
if n.Name == "Error" {
|
|
n.Name = "Err"
|
|
fixed = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// For values that are an error implementation type,
|
|
// rename .Error to .Err and .String to .Error
|
|
sel, selok := n.(*ast.SelectorExpr)
|
|
if selok && isErrorImpl(typeof[sel.X]) {
|
|
switch sel.Sel.Name {
|
|
case "Error":
|
|
sel.Sel.Name = "Err"
|
|
fixed = true
|
|
case "String":
|
|
sel.Sel.Name = "Error"
|
|
fixed = true
|
|
}
|
|
}
|
|
|
|
// Assume x.Err is an error value and rename .String to .Error
|
|
// Children have been processed so the rewrite from Error to Err
|
|
// has already happened there.
|
|
if selok {
|
|
if subsel, ok := sel.X.(*ast.SelectorExpr); ok && subsel.Sel.Name == "Err" && sel.Sel.Name == "String" {
|
|
sel.Sel.Name = "Error"
|
|
fixed = true
|
|
}
|
|
}
|
|
|
|
// For values that are an error variable, rename .String to .Error.
|
|
if selok && isErrorVar(sel.X) && sel.Sel.Name == "String" {
|
|
sel.Sel.Name = "Error"
|
|
fixed = true
|
|
}
|
|
|
|
// Rewrite composite literal of error type to turn Error: into Err:.
|
|
lit, ok := n.(*ast.CompositeLit)
|
|
if ok && isErrorImpl(typeof[lit]) {
|
|
for _, e := range lit.Elts {
|
|
if kv, ok := e.(*ast.KeyValueExpr); ok && isName(kv.Key, "Error") {
|
|
kv.Key.(*ast.Ident).Name = "Err"
|
|
fixed = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rename os.Waitmsg to exec.ExitError
|
|
// when used in a type assertion on an error.
|
|
ta, ok := n.(*ast.TypeAssertExpr)
|
|
if ok && isErrorVar(ta.X) && isPtrPkgDot(ta.Type, "os", "Waitmsg") {
|
|
addImport(f, "exec")
|
|
sel := ta.Type.(*ast.StarExpr).X.(*ast.SelectorExpr)
|
|
sel.X.(*ast.Ident).Name = "exec"
|
|
sel.Sel.Name = "ExitError"
|
|
fixed = true
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
fix := func() {
|
|
if fixed {
|
|
return
|
|
}
|
|
fixed = true
|
|
heuristic()
|
|
}
|
|
|
|
if force["error"] {
|
|
heuristic()
|
|
}
|
|
|
|
walk(f, func(n interface{}) {
|
|
p, ok := n.(*ast.Expr)
|
|
if !ok {
|
|
return
|
|
}
|
|
sel, ok := (*p).(*ast.SelectorExpr)
|
|
if !ok {
|
|
return
|
|
}
|
|
switch {
|
|
case isPkgDot(sel, "os", "Error"):
|
|
fix()
|
|
*p = &ast.Ident{NamePos: sel.Pos(), Name: "error"}
|
|
case isPkgDot(sel, "os", "NewError"):
|
|
fix()
|
|
addImport(f, "errors")
|
|
sel.X.(*ast.Ident).Name = "errors"
|
|
sel.Sel.Name = "New"
|
|
case isPkgDot(sel, "os", "EOF"):
|
|
fix()
|
|
addImport(f, "io")
|
|
sel.X.(*ast.Ident).Name = "io"
|
|
}
|
|
})
|
|
|
|
if fixed && !usesImport(f, "os") {
|
|
deleteImport(f, "os")
|
|
}
|
|
|
|
return fixed
|
|
}
|
|
|
|
func typeName(typ ast.Expr) string {
|
|
if p, ok := typ.(*ast.StarExpr); ok {
|
|
typ = p.X
|
|
}
|
|
id, ok := typ.(*ast.Ident)
|
|
if ok {
|
|
return id.Name
|
|
}
|
|
sel, ok := typ.(*ast.SelectorExpr)
|
|
if ok {
|
|
return typeName(sel.X) + "." + sel.Sel.Name
|
|
}
|
|
return ""
|
|
}
|