Update and fix staticcheck
This commit is contained in:
@ -177,7 +177,7 @@ func (c *Cache) get(id ActionID) (Entry, error) {
|
||||
i++
|
||||
}
|
||||
tm, err := strconv.ParseInt(string(etime[i:]), 10, 64)
|
||||
if err != nil || size < 0 {
|
||||
if err != nil || tm < 0 {
|
||||
return missing()
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ func (c *Cache) Trim() {
|
||||
// We maintain in dir/trim.txt the time of the last completed cache trim.
|
||||
// If the cache has been trimmed recently enough, do nothing.
|
||||
// This is the common case.
|
||||
data, _ := ioutil.ReadFile(filepath.Join(c.dir, "trim.txt"))
|
||||
data, _ := renameio.ReadFile(filepath.Join(c.dir, "trim.txt"))
|
||||
t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
|
||||
if err == nil && now.Sub(time.Unix(t, 0)) < trimInterval {
|
||||
return
|
||||
@ -282,7 +282,7 @@ func (c *Cache) Trim() {
|
||||
|
||||
// Ignore errors from here: if we don't write the complete timestamp, the
|
||||
// cache will appear older than it is, and we'll trim it again next time.
|
||||
renameio.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix())))
|
||||
renameio.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix())), 0666)
|
||||
}
|
||||
|
||||
// trimSubdir trims a single cache subdirectory.
|
||||
@ -326,7 +326,8 @@ func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify
|
||||
// in verify mode we are double-checking that the cache entries
|
||||
// are entirely reproducible. As just noted, this may be unrealistic
|
||||
// in some cases but the check is also useful for shaking out real bugs.
|
||||
entry := []byte(fmt.Sprintf("v1 %x %x %20d %20d\n", id, out, size, time.Now().UnixNano()))
|
||||
entry := fmt.Sprintf("v1 %x %x %20d %20d\n", id, out, size, time.Now().UnixNano())
|
||||
|
||||
if verify && allowVerify {
|
||||
old, err := c.get(id)
|
||||
if err == nil && (old.OutputID != out || old.Size != size) {
|
||||
@ -336,7 +337,28 @@ func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify
|
||||
}
|
||||
}
|
||||
file := c.fileName(id, "a")
|
||||
if err := ioutil.WriteFile(file, entry, 0666); err != nil {
|
||||
|
||||
// Copy file to cache directory.
|
||||
mode := os.O_WRONLY | os.O_CREATE
|
||||
f, err := os.OpenFile(file, mode, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.WriteString(entry)
|
||||
if err == nil {
|
||||
// Truncate the file only *after* writing it.
|
||||
// (This should be a no-op, but truncate just in case of previous corruption.)
|
||||
//
|
||||
// This differs from ioutil.WriteFile, which truncates to 0 *before* writing
|
||||
// via os.O_TRUNC. Truncating only after writing ensures that a second write
|
||||
// of the same content to the same file is idempotent, and does not — even
|
||||
// temporarily! — undo the effect of the first write.
|
||||
err = f.Truncate(int64(len(entry)))
|
||||
}
|
||||
if closeErr := f.Close(); err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
if err != nil {
|
||||
// TODO(bcmills): This Remove potentially races with another go command writing to file.
|
||||
// Can we eliminate it?
|
||||
os.Remove(file)
|
||||
|
113
vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go
vendored
Normal file
113
vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2018 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 buildir defines an Analyzer that constructs the IR
|
||||
// of an error-free package and returns the set of all
|
||||
// functions within it. It does not report any diagnostics itself but
|
||||
// may be used as an input to other analyzers.
|
||||
//
|
||||
// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
|
||||
package buildir
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"honnef.co/go/tools/ir"
|
||||
)
|
||||
|
||||
type willExit struct{}
|
||||
type willUnwind struct{}
|
||||
|
||||
func (*willExit) AFact() {}
|
||||
func (*willUnwind) AFact() {}
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildir",
|
||||
Doc: "build IR for later passes",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(IR)),
|
||||
FactTypes: []analysis.Fact{new(willExit), new(willUnwind)},
|
||||
}
|
||||
|
||||
// IR provides intermediate representation for all the
|
||||
// non-blank source functions in the current package.
|
||||
type IR struct {
|
||||
Pkg *ir.Package
|
||||
SrcFuncs []*ir.Function
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Plundered from ssautil.BuildPackage.
|
||||
|
||||
// We must create a new Program for each Package because the
|
||||
// analysis API provides no place to hang a Program shared by
|
||||
// all Packages. Consequently, IR Packages and Functions do not
|
||||
// have a canonical representation across an analysis session of
|
||||
// multiple packages. This is unlikely to be a problem in
|
||||
// practice because the analysis API essentially forces all
|
||||
// packages to be analysed independently, so any given call to
|
||||
// Analysis.Run on a package will see only IR objects belonging
|
||||
// to a single Program.
|
||||
|
||||
mode := ir.GlobalDebug
|
||||
|
||||
prog := ir.NewProgram(pass.Fset, mode)
|
||||
|
||||
// Create IR packages for all imports.
|
||||
// Order is not significant.
|
||||
created := make(map[*types.Package]bool)
|
||||
var createAll func(pkgs []*types.Package)
|
||||
createAll = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !created[p] {
|
||||
created[p] = true
|
||||
irpkg := prog.CreatePackage(p, nil, nil, true)
|
||||
for _, fn := range irpkg.Functions {
|
||||
if ast.IsExported(fn.Name()) {
|
||||
var exit willExit
|
||||
var unwind willUnwind
|
||||
if pass.ImportObjectFact(fn.Object(), &exit) {
|
||||
fn.WillExit = true
|
||||
}
|
||||
if pass.ImportObjectFact(fn.Object(), &unwind) {
|
||||
fn.WillUnwind = true
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(p.Imports())
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(pass.Pkg.Imports())
|
||||
|
||||
// Create and build the primary package.
|
||||
irpkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
|
||||
irpkg.Build()
|
||||
|
||||
// Compute list of source functions, including literals,
|
||||
// in source order.
|
||||
var addAnons func(f *ir.Function)
|
||||
funcs := make([]*ir.Function, len(irpkg.Functions))
|
||||
copy(funcs, irpkg.Functions)
|
||||
addAnons = func(f *ir.Function) {
|
||||
for _, anon := range f.AnonFuncs {
|
||||
funcs = append(funcs, anon)
|
||||
addAnons(anon)
|
||||
}
|
||||
}
|
||||
for _, fn := range irpkg.Functions {
|
||||
addAnons(fn)
|
||||
if fn.WillExit {
|
||||
pass.ExportObjectFact(fn.Object(), new(willExit))
|
||||
}
|
||||
if fn.WillUnwind {
|
||||
pass.ExportObjectFact(fn.Object(), new(willUnwind))
|
||||
}
|
||||
}
|
||||
|
||||
return &IR{Pkg: irpkg, SrcFuncs: funcs}, nil
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
// Copyright 2018 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 buildssa defines an Analyzer that constructs the SSA
|
||||
// representation of an error-free package and returns the set of all
|
||||
// functions within it. It does not report any diagnostics itself but
|
||||
// may be used as an input to other analyzers.
|
||||
//
|
||||
// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
|
||||
package buildssa
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildssa",
|
||||
Doc: "build SSA-form IR for later passes",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(SSA)),
|
||||
}
|
||||
|
||||
// SSA provides SSA-form intermediate representation for all the
|
||||
// non-blank source functions in the current package.
|
||||
type SSA struct {
|
||||
Pkg *ssa.Package
|
||||
SrcFuncs []*ssa.Function
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Plundered from ssautil.BuildPackage.
|
||||
|
||||
// We must create a new Program for each Package because the
|
||||
// analysis API provides no place to hang a Program shared by
|
||||
// all Packages. Consequently, SSA Packages and Functions do not
|
||||
// have a canonical representation across an analysis session of
|
||||
// multiple packages. This is unlikely to be a problem in
|
||||
// practice because the analysis API essentially forces all
|
||||
// packages to be analysed independently, so any given call to
|
||||
// Analysis.Run on a package will see only SSA objects belonging
|
||||
// to a single Program.
|
||||
|
||||
mode := ssa.GlobalDebug
|
||||
|
||||
prog := ssa.NewProgram(pass.Fset, mode)
|
||||
|
||||
// Create SSA packages for all imports.
|
||||
// Order is not significant.
|
||||
created := make(map[*types.Package]bool)
|
||||
var createAll func(pkgs []*types.Package)
|
||||
createAll = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !created[p] {
|
||||
created[p] = true
|
||||
prog.CreatePackage(p, nil, nil, true)
|
||||
createAll(p.Imports())
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(pass.Pkg.Imports())
|
||||
|
||||
// Create and build the primary package.
|
||||
ssapkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
|
||||
ssapkg.Build()
|
||||
|
||||
// Compute list of source functions, including literals,
|
||||
// in source order.
|
||||
var funcs []*ssa.Function
|
||||
var addAnons func(f *ssa.Function)
|
||||
addAnons = func(f *ssa.Function) {
|
||||
funcs = append(funcs, f)
|
||||
for _, anon := range f.AnonFuncs {
|
||||
addAnons(anon)
|
||||
}
|
||||
}
|
||||
addAnons(ssapkg.Members["init"].(*ssa.Function))
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if fdecl, ok := decl.(*ast.FuncDecl); ok {
|
||||
|
||||
// SSA will not build a Function
|
||||
// for a FuncDecl named blank.
|
||||
// That's arguably too strict but
|
||||
// relaxing it would break uniqueness of
|
||||
// names of package members.
|
||||
if fdecl.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
// (init functions have distinct Func
|
||||
// objects named "init" and distinct
|
||||
// ssa.Functions named "init#1", ...)
|
||||
|
||||
fn := pass.TypesInfo.Defs[fdecl.Name].(*types.Func)
|
||||
if fn == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
f := ssapkg.Prog.FuncValue(fn)
|
||||
if f == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
addAnons(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SSA{Pkg: ssapkg, SrcFuncs: funcs}, nil
|
||||
}
|
@ -8,15 +8,15 @@ package renameio
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"strconv"
|
||||
|
||||
"honnef.co/go/tools/internal/robustio"
|
||||
)
|
||||
|
||||
const patternSuffix = "*.tmp"
|
||||
const patternSuffix = ".tmp"
|
||||
|
||||
// Pattern returns a glob pattern that matches the unrenamed temporary files
|
||||
// created when writing to filename.
|
||||
@ -29,14 +29,14 @@ func Pattern(filename string) string {
|
||||
// final name.
|
||||
//
|
||||
// That ensures that the final location, if it exists, is always a complete file.
|
||||
func WriteFile(filename string, data []byte) (err error) {
|
||||
return WriteToFile(filename, bytes.NewReader(data))
|
||||
func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
|
||||
return WriteToFile(filename, bytes.NewReader(data), perm)
|
||||
}
|
||||
|
||||
// WriteToFile is a variant of WriteFile that accepts the data as an io.Reader
|
||||
// instead of a slice.
|
||||
func WriteToFile(filename string, data io.Reader) (err error) {
|
||||
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
|
||||
func WriteToFile(filename string, data io.Reader, perm os.FileMode) (err error) {
|
||||
f, err := tempFile(filepath.Dir(filename), filepath.Base(filename), perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -63,21 +63,31 @@ func WriteToFile(filename string, data io.Reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var start time.Time
|
||||
for {
|
||||
err := os.Rename(f.Name(), filename)
|
||||
if err == nil || runtime.GOOS != "windows" || !strings.HasSuffix(err.Error(), "Access is denied.") {
|
||||
return err
|
||||
}
|
||||
|
||||
// Windows seems to occasionally trigger spurious "Access is denied" errors
|
||||
// here (see golang.org/issue/31247). We're not sure why. It's probably
|
||||
// worth a little extra latency to avoid propagating the spurious errors.
|
||||
if start.IsZero() {
|
||||
start = time.Now()
|
||||
} else if time.Since(start) >= 500*time.Millisecond {
|
||||
return err
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
return robustio.Rename(f.Name(), filename)
|
||||
}
|
||||
|
||||
// tempFile creates a new temporary file with given permission bits.
|
||||
func tempFile(dir, prefix string, perm os.FileMode) (f *os.File, err error) {
|
||||
for i := 0; i < 10000; i++ {
|
||||
name := filepath.Join(dir, prefix+strconv.Itoa(rand.Intn(1000000000))+patternSuffix)
|
||||
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
|
||||
if os.IsExist(err) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadFile is like ioutil.ReadFile, but on Windows retries spurious errors that
|
||||
// may occur if the file is concurrently replaced.
|
||||
//
|
||||
// Errors are classified heuristically and retries are bounded, so even this
|
||||
// function may occasionally return a spurious error on Windows.
|
||||
// If so, the error will likely wrap one of:
|
||||
// - syscall.ERROR_ACCESS_DENIED
|
||||
// - syscall.ERROR_FILE_NOT_FOUND
|
||||
// - internal/syscall/windows.ERROR_SHARING_VIOLATION
|
||||
func ReadFile(filename string) ([]byte, error) {
|
||||
return robustio.ReadFile(filename)
|
||||
}
|
||||
|
53
vendor/honnef.co/go/tools/internal/robustio/robustio.go
vendored
Normal file
53
vendor/honnef.co/go/tools/internal/robustio/robustio.go
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2019 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 robustio wraps I/O functions that are prone to failure on Windows,
|
||||
// transparently retrying errors up to an arbitrary timeout.
|
||||
//
|
||||
// Errors are classified heuristically and retries are bounded, so the functions
|
||||
// in this package do not completely eliminate spurious errors. However, they do
|
||||
// significantly reduce the rate of failure in practice.
|
||||
//
|
||||
// If so, the error will likely wrap one of:
|
||||
// The functions in this package do not completely eliminate spurious errors,
|
||||
// but substantially reduce their rate of occurrence in practice.
|
||||
package robustio
|
||||
|
||||
// Rename is like os.Rename, but on Windows retries errors that may occur if the
|
||||
// file is concurrently read or overwritten.
|
||||
//
|
||||
// (See golang.org/issue/31247 and golang.org/issue/32188.)
|
||||
func Rename(oldpath, newpath string) error {
|
||||
return rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
// ReadFile is like ioutil.ReadFile, but on Windows retries errors that may
|
||||
// occur if the file is concurrently replaced.
|
||||
//
|
||||
// (See golang.org/issue/31247 and golang.org/issue/32188.)
|
||||
func ReadFile(filename string) ([]byte, error) {
|
||||
return readFile(filename)
|
||||
}
|
||||
|
||||
// RemoveAll is like os.RemoveAll, but on Windows retries errors that may occur
|
||||
// if an executable file in the directory has recently been executed.
|
||||
//
|
||||
// (See golang.org/issue/19491.)
|
||||
func RemoveAll(path string) error {
|
||||
return removeAll(path)
|
||||
}
|
||||
|
||||
// IsEphemeralError reports whether err is one of the errors that the functions
|
||||
// in this package attempt to mitigate.
|
||||
//
|
||||
// Errors considered ephemeral include:
|
||||
// - syscall.ERROR_ACCESS_DENIED
|
||||
// - syscall.ERROR_FILE_NOT_FOUND
|
||||
// - internal/syscall/windows.ERROR_SHARING_VIOLATION
|
||||
//
|
||||
// This set may be expanded in the future; programs must not rely on the
|
||||
// non-ephemerality of any given error.
|
||||
func IsEphemeralError(err error) bool {
|
||||
return isEphemeralError(err)
|
||||
}
|
29
vendor/honnef.co/go/tools/internal/robustio/robustio_darwin.go
vendored
Normal file
29
vendor/honnef.co/go/tools/internal/robustio/robustio_darwin.go
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2019 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 robustio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const errFileNotFound = syscall.ENOENT
|
||||
|
||||
// isEphemeralError returns true if err may be resolved by waiting.
|
||||
func isEphemeralError(err error) bool {
|
||||
switch werr := err.(type) {
|
||||
case *os.PathError:
|
||||
err = werr.Err
|
||||
case *os.LinkError:
|
||||
err = werr.Err
|
||||
case *os.SyscallError:
|
||||
err = werr.Err
|
||||
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
return errno == errFileNotFound
|
||||
}
|
||||
return false
|
||||
}
|
93
vendor/honnef.co/go/tools/internal/robustio/robustio_flaky.go
vendored
Normal file
93
vendor/honnef.co/go/tools/internal/robustio/robustio_flaky.go
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
// +build windows darwin
|
||||
|
||||
package robustio
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const arbitraryTimeout = 500 * time.Millisecond
|
||||
|
||||
const ERROR_SHARING_VIOLATION = 32
|
||||
|
||||
// retry retries ephemeral errors from f up to an arbitrary timeout
|
||||
// to work around filesystem flakiness on Windows and Darwin.
|
||||
func retry(f func() (err error, mayRetry bool)) error {
|
||||
var (
|
||||
bestErr error
|
||||
lowestErrno syscall.Errno
|
||||
start time.Time
|
||||
nextSleep time.Duration = 1 * time.Millisecond
|
||||
)
|
||||
for {
|
||||
err, mayRetry := f()
|
||||
if err == nil || !mayRetry {
|
||||
return err
|
||||
}
|
||||
|
||||
if errno, ok := err.(syscall.Errno); ok && (lowestErrno == 0 || errno < lowestErrno) {
|
||||
bestErr = err
|
||||
lowestErrno = errno
|
||||
} else if bestErr == nil {
|
||||
bestErr = err
|
||||
}
|
||||
|
||||
if start.IsZero() {
|
||||
start = time.Now()
|
||||
} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {
|
||||
break
|
||||
}
|
||||
time.Sleep(nextSleep)
|
||||
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
|
||||
}
|
||||
|
||||
return bestErr
|
||||
}
|
||||
|
||||
// rename is like os.Rename, but retries ephemeral errors.
|
||||
//
|
||||
// On windows it wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with
|
||||
// MOVEFILE_REPLACE_EXISTING.
|
||||
//
|
||||
// Windows also provides a different system call, ReplaceFile,
|
||||
// that provides similar semantics, but perhaps preserves more metadata. (The
|
||||
// documentation on the differences between the two is very sparse.)
|
||||
//
|
||||
// Empirical error rates with MoveFileEx are lower under modest concurrency, so
|
||||
// for now we're sticking with what the os package already provides.
|
||||
func rename(oldpath, newpath string) (err error) {
|
||||
return retry(func() (err error, mayRetry bool) {
|
||||
err = os.Rename(oldpath, newpath)
|
||||
return err, isEphemeralError(err)
|
||||
})
|
||||
}
|
||||
|
||||
// readFile is like ioutil.ReadFile, but retries ephemeral errors.
|
||||
func readFile(filename string) ([]byte, error) {
|
||||
var b []byte
|
||||
err := retry(func() (err error, mayRetry bool) {
|
||||
b, err = ioutil.ReadFile(filename)
|
||||
|
||||
// Unlike in rename, we do not retry errFileNotFound here: it can occur
|
||||
// as a spurious error, but the file may also genuinely not exist, so the
|
||||
// increase in robustness is probably not worth the extra latency.
|
||||
|
||||
return err, isEphemeralError(err) && err != errFileNotFound
|
||||
})
|
||||
return b, err
|
||||
}
|
||||
|
||||
func removeAll(path string) error {
|
||||
return retry(func() (err error, mayRetry bool) {
|
||||
err = os.RemoveAll(path)
|
||||
return err, isEphemeralError(err)
|
||||
})
|
||||
}
|
28
vendor/honnef.co/go/tools/internal/robustio/robustio_other.go
vendored
Normal file
28
vendor/honnef.co/go/tools/internal/robustio/robustio_other.go
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
//+build !windows,!darwin
|
||||
|
||||
package robustio
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func rename(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
func readFile(filename string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
}
|
||||
|
||||
func removeAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func isEphemeralError(err error) bool {
|
||||
return false
|
||||
}
|
33
vendor/honnef.co/go/tools/internal/robustio/robustio_windows.go
vendored
Normal file
33
vendor/honnef.co/go/tools/internal/robustio/robustio_windows.go
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2019 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 robustio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const errFileNotFound = syscall.ERROR_FILE_NOT_FOUND
|
||||
|
||||
// isEphemeralError returns true if err may be resolved by waiting.
|
||||
func isEphemeralError(err error) bool {
|
||||
switch werr := err.(type) {
|
||||
case *os.PathError:
|
||||
err = werr.Err
|
||||
case *os.LinkError:
|
||||
err = werr.Err
|
||||
case *os.SyscallError:
|
||||
err = werr.Err
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
switch errno {
|
||||
case syscall.ERROR_ACCESS_DENIED,
|
||||
syscall.ERROR_FILE_NOT_FOUND,
|
||||
ERROR_SHARING_VIOLATION:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -5,23 +5,24 @@ import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"honnef.co/go/tools/internal/passes/buildssa"
|
||||
"honnef.co/go/tools/code"
|
||||
"honnef.co/go/tools/internal/passes/buildir"
|
||||
"honnef.co/go/tools/ir"
|
||||
. "honnef.co/go/tools/lint/lintdsl"
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
|
||||
fn := func(node ast.Node) bool {
|
||||
for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
|
||||
cb := func(node ast.Node) bool {
|
||||
rng, ok := node.(*ast.RangeStmt)
|
||||
if !ok || !IsBlank(rng.Key) {
|
||||
if !ok || !code.IsBlank(rng.Key) {
|
||||
return true
|
||||
}
|
||||
|
||||
v, _ := ssafn.ValueForExpr(rng.X)
|
||||
v, _ := fn.ValueForExpr(rng.X)
|
||||
|
||||
// Check that we're converting from string to []rune
|
||||
val, _ := v.(*ssa.Convert)
|
||||
val, _ := v.(*ir.Convert)
|
||||
if val == nil {
|
||||
return true
|
||||
}
|
||||
@ -47,13 +48,13 @@ func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
|
||||
|
||||
// Expect two refs: one for obtaining the length of the slice,
|
||||
// one for accessing the elements
|
||||
if len(FilterDebug(*refs)) != 2 {
|
||||
if len(code.FilterDebug(*refs)) != 2 {
|
||||
// TODO(dh): right now, we check that only one place
|
||||
// refers to our slice. This will miss cases such as
|
||||
// ranging over the slice twice. Ideally, we'd ensure that
|
||||
// the slice is only used for ranging over (without
|
||||
// accessing the key), but that is harder to do because in
|
||||
// SSA form, ranging over a slice looks like an ordinary
|
||||
// IR form, ranging over a slice looks like an ordinary
|
||||
// loop with index increments and slice accesses. We'd
|
||||
// have to look at the associated AST node to check that
|
||||
// it's a range statement.
|
||||
@ -64,7 +65,7 @@ func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
|
||||
|
||||
return true
|
||||
}
|
||||
Inspect(ssafn.Syntax(), fn)
|
||||
Inspect(fn.Source(), cb)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user