diff options
Diffstat (limited to 'src/cmd/fix/error.go')
-rw-r--r-- | src/cmd/fix/error.go | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/src/cmd/fix/error.go b/src/cmd/fix/error.go deleted file mode 100644 index 55613210a..000000000 --- a/src/cmd/fix/error.go +++ /dev/null @@ -1,353 +0,0 @@ -// 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 "" -} |