blob: 19c19b5b63d38ddaf55c77b0f7791da5378789e7 [file] [log] [blame]
// 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 osopenFix = fix{
"osopen",
osopen,
`Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE.
http://codereview.appspot.com/4357052
`,
}
func init() {
register(osopenFix)
}
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
}