Commit 83550f07 authored by Robert Hencke's avatar Robert Hencke Committed by Andrew Gerrand

gofix: fixes for os/signal changes

Fixes #1971.

R=adg, rsc
CC=golang-dev
https://golang.org/cl/4630056
parent d6f80e1a
......@@ -18,6 +18,7 @@ GOFILES=\
osopen.go\
procattr.go\
reflect.go\
signal.go\
sortslice.go\
stringssplit.go\
typecheck.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
}
// 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
}
// 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
}
`,
},
}
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