summaryrefslogtreecommitdiff
path: root/src/cmd/gofix
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-06-30 15:34:22 +0200
committerOndřej Surý <ondrej@sury.org>2011-06-30 15:34:22 +0200
commitd39f5aa373a4422f7a5f3ee764fb0f6b0b719d61 (patch)
tree1833f8b72a4b3a8f00d0d143b079a8fcad01c6ae /src/cmd/gofix
parent8652e6c371b8905498d3d314491d36c58d5f68d5 (diff)
downloadgolang-upstream/58.tar.gz
Imported Upstream version 58upstream/58
Diffstat (limited to 'src/cmd/gofix')
-rw-r--r--src/cmd/gofix/Makefile2
-rw-r--r--src/cmd/gofix/fix.go159
-rw-r--r--src/cmd/gofix/httpfinalurl.go56
-rw-r--r--src/cmd/gofix/httpfinalurl_test.go37
-rw-r--r--src/cmd/gofix/main.go18
-rw-r--r--src/cmd/gofix/signal.go49
-rw-r--r--src/cmd/gofix/signal_test.go96
7 files changed, 401 insertions, 16 deletions
diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile
index 12f09b4e4..c4127d93e 100644
--- a/src/cmd/gofix/Makefile
+++ b/src/cmd/gofix/Makefile
@@ -10,9 +10,11 @@ GOFILES=\
netdial.go\
main.go\
osopen.go\
+ httpfinalurl.go\
httpserver.go\
procattr.go\
reflect.go\
+ signal.go\
typecheck.go\
include ../../Make.cmd
diff --git a/src/cmd/gofix/fix.go b/src/cmd/gofix/fix.go
index 0852ce21e..c1c5a746c 100644
--- a/src/cmd/gofix/fix.go
+++ b/src/cmd/gofix/fix.go
@@ -10,6 +10,7 @@ import (
"go/token"
"os"
"strconv"
+ "strings"
)
type fix struct {
@@ -258,13 +259,28 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
// imports returns true if f imports path.
func imports(f *ast.File, path string) bool {
+ return importSpec(f, path) != nil
+}
+
+// importSpec returns the import spec if f imports path,
+// or nil otherwise.
+func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
- t, err := strconv.Unquote(s.Path.Value)
- if err == nil && t == path {
- return true
+ if importPath(s) == path {
+ return s
}
}
- return false
+ return nil
+}
+
+// importPath returns the unquoted import path of s,
+// or "" if the path is not properly quoted.
+func importPath(s *ast.ImportSpec) string {
+ t, err := strconv.Unquote(s.Path.Value)
+ if err == nil {
+ return t
+ }
+ return ""
}
// isPkgDot returns true if t is the expression "pkg.name"
@@ -420,3 +436,138 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
},
}
}
+
+// addImport adds the import path to the file f, if absent.
+func addImport(f *ast.File, path string) {
+ if imports(f, path) {
+ return
+ }
+
+ newImport := &ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(path),
+ },
+ }
+
+ var impdecl *ast.GenDecl
+
+ // Find an import decl to add to.
+ for _, decl := range f.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+
+ if ok && gen.Tok == token.IMPORT {
+ impdecl = gen
+ break
+ }
+ }
+
+ // No import decl found. Add one.
+ if impdecl == nil {
+ impdecl = &ast.GenDecl{
+ Tok: token.IMPORT,
+ }
+ f.Decls = append(f.Decls, nil)
+ copy(f.Decls[1:], f.Decls)
+ f.Decls[0] = impdecl
+ }
+
+ // Ensure the import decl has parentheses, if needed.
+ if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() {
+ impdecl.Lparen = impdecl.Pos()
+ }
+
+ // Assume the import paths are alphabetically ordered.
+ // If they are not, the result is ugly, but legal.
+ insertAt := len(impdecl.Specs) // default to end of specs
+ for i, spec := range impdecl.Specs {
+ impspec := spec.(*ast.ImportSpec)
+ if importPath(impspec) > path {
+ insertAt = i
+ break
+ }
+ }
+
+ impdecl.Specs = append(impdecl.Specs, nil)
+ copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:])
+ impdecl.Specs[insertAt] = newImport
+
+ f.Imports = append(f.Imports, newImport)
+}
+
+// deleteImport deletes the import path from the file f, if present.
+func deleteImport(f *ast.File, path string) {
+ oldImport := importSpec(f, path)
+
+ // Find the import node that imports path, if any.
+ for i, decl := range f.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ continue
+ }
+ for j, spec := range gen.Specs {
+ impspec := spec.(*ast.ImportSpec)
+
+ if oldImport != impspec {
+ continue
+ }
+
+ // We found an import spec that imports path.
+ // Delete it.
+ copy(gen.Specs[j:], gen.Specs[j+1:])
+ gen.Specs = gen.Specs[:len(gen.Specs)-1]
+
+ // If this was the last import spec in this decl,
+ // delete the decl, too.
+ if len(gen.Specs) == 0 {
+ copy(f.Decls[i:], f.Decls[i+1:])
+ f.Decls = f.Decls[:len(f.Decls)-1]
+ } else if len(gen.Specs) == 1 {
+ gen.Lparen = token.NoPos // drop parens
+ }
+
+ break
+ }
+ }
+
+ // Delete it from f.Imports.
+ for i, imp := range f.Imports {
+ if imp == oldImport {
+ copy(f.Imports[i:], f.Imports[i+1:])
+ f.Imports = f.Imports[:len(f.Imports)-1]
+ break
+ }
+ }
+}
+
+func usesImport(f *ast.File, path string) (used bool) {
+ spec := importSpec(f, path)
+ if spec == nil {
+ return
+ }
+
+ name := spec.Name.String()
+ switch name {
+ case "<nil>":
+ // If the package name is not explicitly specified,
+ // make an educated guess. This is not guaranteed to be correct.
+ lastSlash := strings.LastIndex(path, "/")
+ if lastSlash == -1 {
+ name = path
+ } else {
+ name = path[lastSlash+1:]
+ }
+ case "_", ".":
+ // Not sure if this import is used - err on the side of caution.
+ return true
+ }
+
+ walk(f, func(n interface{}) {
+ sel, ok := n.(*ast.SelectorExpr)
+ if ok && isTopName(sel.X, name) {
+ used = true
+ }
+ })
+
+ return
+}
diff --git a/src/cmd/gofix/httpfinalurl.go b/src/cmd/gofix/httpfinalurl.go
new file mode 100644
index 000000000..53642b22f
--- /dev/null
+++ b/src/cmd/gofix/httpfinalurl.go
@@ -0,0 +1,56 @@
+// 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"
+)
+
+var httpFinalURLFix = fix{
+ "httpfinalurl",
+ httpfinalurl,
+ `Adapt http Get calls to not have a finalURL result parameter.
+
+ http://codereview.appspot.com/4535056/
+`,
+}
+
+func init() {
+ register(httpFinalURLFix)
+}
+
+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
+}
diff --git a/src/cmd/gofix/httpfinalurl_test.go b/src/cmd/gofix/httpfinalurl_test.go
new file mode 100644
index 000000000..9e7d6242d
--- /dev/null
+++ b/src/cmd/gofix/httpfinalurl_test.go
@@ -0,0 +1,37 @@
+// 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)
+}
+
+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
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go
index 4f7e923e3..1b091c18a 100644
--- a/src/cmd/gofix/main.go
+++ b/src/cmd/gofix/main.go
@@ -248,17 +248,11 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) {
f1.Write(b1)
f2.Write(b2)
- diffcmd, err := exec.LookPath("diff")
- if err != nil {
- return nil, err
+ data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
+ if len(data) > 0 {
+ // diff exits with a non-zero status when the files don't match.
+ // Ignore that failure as long as we get output.
+ err = nil
}
-
- c, err := exec.Run(diffcmd, []string{"diff", f1.Name(), f2.Name()}, nil, "",
- exec.DevNull, exec.Pipe, exec.MergeWithStdout)
- if err != nil {
- return nil, err
- }
- defer c.Close()
-
- return ioutil.ReadAll(c.Stdout)
+ return
}
diff --git a/src/cmd/gofix/signal.go b/src/cmd/gofix/signal.go
new file mode 100644
index 000000000..53c338851
--- /dev/null
+++ b/src/cmd/gofix/signal.go
@@ -0,0 +1,49 @@
+// 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(fix{
+ "signal",
+ signal,
+ `Adapt code to types moved from os/signal to signal.
+
+http://codereview.appspot.com/4437091
+`,
+ })
+}
+
+func signal(f *ast.File) (fixed bool) {
+ if !imports(f, "os/signal") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+
+ if !ok || !isTopName(s.X, "signal") {
+ return
+ }
+
+ sel := s.Sel.String()
+ if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") {
+ s.X = &ast.Ident{Name: "os"}
+ fixed = true
+ }
+ })
+
+ if fixed {
+ addImport(f, "os")
+ if !usesImport(f, "os/signal") {
+ deleteImport(f, "os/signal")
+ }
+ }
+ return
+}
diff --git a/src/cmd/gofix/signal_test.go b/src/cmd/gofix/signal_test.go
new file mode 100644
index 000000000..2500e9cee
--- /dev/null
+++ b/src/cmd/gofix/signal_test.go
@@ -0,0 +1,96 @@
+// 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(signalTests)
+}
+
+var signalTests = []testCase{
+ {
+ Name: "signal.0",
+ In: `package main
+
+import (
+ _ "a"
+ "os/signal"
+ _ "z"
+)
+
+type T1 signal.UnixSignal
+type T2 signal.Signal
+
+func f() {
+ _ = signal.SIGHUP
+ _ = signal.Incoming
+}
+`,
+ Out: `package main
+
+import (
+ _ "a"
+ "os"
+ "os/signal"
+ _ "z"
+)
+
+type T1 os.UnixSignal
+type T2 os.Signal
+
+func f() {
+ _ = os.SIGHUP
+ _ = signal.Incoming
+}
+`,
+ },
+ {
+ Name: "signal.1",
+ In: `package main
+
+import (
+ "os"
+ "os/signal"
+)
+
+func f() {
+ var _ os.Error
+ _ = signal.SIGHUP
+}
+`,
+ Out: `package main
+
+import "os"
+
+
+func f() {
+ var _ os.Error
+ _ = os.SIGHUP
+}
+`,
+ },
+ {
+ Name: "signal.2",
+ In: `package main
+
+import "os"
+import "os/signal"
+
+func f() {
+ var _ os.Error
+ _ = signal.SIGHUP
+}
+`,
+ Out: `package main
+
+import "os"
+
+
+func f() {
+ var _ os.Error
+ _ = os.SIGHUP
+}
+`,
+ },
+}