staticcheck (#313)
* CI: use staticcheck for linting This commit switches the linter for Go code from golint to staticcheck. Golint has been deprecated since last year and staticcheck is a recommended replacement. Signed-off-by: Lucas Servén Marín <lserven@gmail.com> * revendor Signed-off-by: Lucas Servén Marín <lserven@gmail.com> * cmd,pkg: fix lint warnings Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
committed by
GitHub
parent
93f46e03ea
commit
50fbc2eec2
107
vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go
vendored
Normal file
107
vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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"
|
||||
|
||||
"honnef.co/go/tools/go/ir"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
type noReturn struct {
|
||||
Kind ir.NoReturn
|
||||
}
|
||||
|
||||
func (*noReturn) AFact() {}
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildir",
|
||||
Doc: "build IR for later passes",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(IR)),
|
||||
FactTypes: []analysis.Fact{new(noReturn)},
|
||||
}
|
||||
|
||||
// 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 noRet noReturn
|
||||
if pass.ImportObjectFact(fn.Object(), &noRet) {
|
||||
fn.NoReturn = noRet.Kind
|
||||
}
|
||||
}
|
||||
}
|
||||
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.NoReturn > 0 {
|
||||
pass.ExportObjectFact(fn.Object(), &noReturn{fn.NoReturn})
|
||||
}
|
||||
}
|
||||
|
||||
return &IR{Pkg: irpkg, SrcFuncs: funcs}, nil
|
||||
}
|
2
vendor/honnef.co/go/tools/internal/renameio/UPSTREAM
vendored
Normal file
2
vendor/honnef.co/go/tools/internal/renameio/UPSTREAM
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
This package is a copy of cmd/go/internal/renameio.
|
||||
The upstream package no longer exists, as the Go project replaced all of its uses with the lockedfile package.
|
93
vendor/honnef.co/go/tools/internal/renameio/renameio.go
vendored
Normal file
93
vendor/honnef.co/go/tools/internal/renameio/renameio.go
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 renameio writes files atomically by renaming temporary files.
|
||||
package renameio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"honnef.co/go/tools/internal/robustio"
|
||||
)
|
||||
|
||||
const patternSuffix = ".tmp"
|
||||
|
||||
// Pattern returns a glob pattern that matches the unrenamed temporary files
|
||||
// created when writing to filename.
|
||||
func Pattern(filename string) string {
|
||||
return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
|
||||
}
|
||||
|
||||
// WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary
|
||||
// file in the same directory as filename, then renames it atomically to the
|
||||
// final name.
|
||||
//
|
||||
// That ensures that the final location, if it exists, is always a complete file.
|
||||
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, perm os.FileMode) (err error) {
|
||||
f, err := tempFile(filepath.Dir(filename), filepath.Base(filename), perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
// Only call os.Remove on f.Name() if we failed to rename it: otherwise,
|
||||
// some other process may have created a new file with the same name after
|
||||
// that.
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := io.Copy(f, data); err != nil {
|
||||
return err
|
||||
}
|
||||
// Sync the file before renaming it: otherwise, after a crash the reader may
|
||||
// observe a 0-length file instead of the actual contents.
|
||||
// See https://golang.org/issue/22397#issuecomment-380831736.
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return robustio.Rename(f.Name(), filename)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
6
vendor/honnef.co/go/tools/internal/robustio/UPSTREAM
vendored
Normal file
6
vendor/honnef.co/go/tools/internal/robustio/UPSTREAM
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
This package is a copy of cmd/go/internal/robustio.
|
||||
It is mostly in sync with upstream according to the last commit we've looked at,
|
||||
with the exception of still using I/O functions that work with older Go versions.
|
||||
|
||||
The last upstream commit we've looked at was:
|
||||
06ac303f6a14b133254f757e54599c48e3c2a4ad
|
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)
|
||||
}
|
21
vendor/honnef.co/go/tools/internal/robustio/robustio_darwin.go
vendored
Normal file
21
vendor/honnef.co/go/tools/internal/robustio/robustio_darwin.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const errFileNotFound = syscall.ENOENT
|
||||
|
||||
// isEphemeralError returns true if err may be resolved by waiting.
|
||||
func isEphemeralError(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
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.
|
||||
|
||||
//go:build windows || darwin
|
||||
// +build windows darwin
|
||||
|
||||
package robustio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const arbitraryTimeout = 2000 * time.Millisecond
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) && (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) && !errors.Is(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)
|
||||
})
|
||||
}
|
29
vendor/honnef.co/go/tools/internal/robustio/robustio_other.go
vendored
Normal file
29
vendor/honnef.co/go/tools/internal/robustio/robustio_other.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.
|
||||
|
||||
//go:build !windows && !darwin
|
||||
// +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
|
||||
}
|
27
vendor/honnef.co/go/tools/internal/robustio/robustio_windows.go
vendored
Normal file
27
vendor/honnef.co/go/tools/internal/robustio/robustio_windows.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ERROR_SHARING_VIOLATION = 32
|
||||
const errFileNotFound = syscall.ERROR_FILE_NOT_FOUND
|
||||
|
||||
// isEphemeralError returns true if err may be resolved by waiting.
|
||||
func isEphemeralError(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
switch errno {
|
||||
case syscall.ERROR_ACCESS_DENIED,
|
||||
syscall.ERROR_FILE_NOT_FOUND,
|
||||
ERROR_SHARING_VIOLATION:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
208
vendor/honnef.co/go/tools/internal/sharedcheck/lint.go
vendored
Normal file
208
vendor/honnef.co/go/tools/internal/sharedcheck/lint.go
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
package sharedcheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/analysis/code"
|
||||
"honnef.co/go/tools/analysis/edit"
|
||||
"honnef.co/go/tools/analysis/facts"
|
||||
"honnef.co/go/tools/analysis/report"
|
||||
"honnef.co/go/tools/go/ast/astutil"
|
||||
"honnef.co/go/tools/go/ir"
|
||||
"honnef.co/go/tools/go/ir/irutil"
|
||||
"honnef.co/go/tools/go/types/typeutil"
|
||||
"honnef.co/go/tools/internal/passes/buildir"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
)
|
||||
|
||||
func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
|
||||
cb := func(node ast.Node) bool {
|
||||
rng, ok := node.(*ast.RangeStmt)
|
||||
if !ok || !astutil.IsBlank(rng.Key) {
|
||||
return true
|
||||
}
|
||||
|
||||
v, _ := fn.ValueForExpr(rng.X)
|
||||
|
||||
// Check that we're converting from string to []rune
|
||||
val, _ := v.(*ir.Convert)
|
||||
if val == nil {
|
||||
return true
|
||||
}
|
||||
Tsrc, ok := typeutil.CoreType(val.X.Type()).(*types.Basic)
|
||||
if !ok || Tsrc.Kind() != types.String {
|
||||
return true
|
||||
}
|
||||
Tdst, ok := typeutil.CoreType(val.Type()).(*types.Slice)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
TdstElem, ok := Tdst.Elem().(*types.Basic)
|
||||
if !ok || TdstElem.Kind() != types.Int32 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check that the result of the conversion is only used to
|
||||
// range over
|
||||
refs := val.Referrers()
|
||||
if refs == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Expect two refs: one for obtaining the length of the slice,
|
||||
// one for accessing the elements
|
||||
if len(irutil.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
|
||||
// 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.
|
||||
return true
|
||||
}
|
||||
|
||||
pass.Reportf(rng.Pos(), "should range over string, not []rune(string)")
|
||||
|
||||
return true
|
||||
}
|
||||
if source := fn.Source(); source != nil {
|
||||
ast.Inspect(source, cb)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RedundantTypeInDeclarationChecker returns a checker that flags variable declarations with redundantly specified types.
|
||||
// That is, it flags 'var v T = e' where e's type is identical to T and 'var v = e' (or 'v := e') would have the same effect.
|
||||
//
|
||||
// It does not flag variables under the following conditions, to reduce the number of false positives:
|
||||
// - global variables – these often specify types to aid godoc
|
||||
// - files that use cgo – cgo code generation and pointer checking emits redundant types
|
||||
//
|
||||
// It does not flag variables under the following conditions, unless flagHelpfulTypes is true, to reduce the number of noisy positives:
|
||||
// - packages that import syscall or unsafe – these sometimes use this form of assignment to make sure types are as expected
|
||||
// - variables named the blank identifier – a pattern used to confirm the types of variables
|
||||
// - untyped expressions on the rhs – the explicitness might aid readability
|
||||
func RedundantTypeInDeclarationChecker(verb string, flagHelpfulTypes bool) *analysis.Analyzer {
|
||||
fn := func(pass *analysis.Pass) (interface{}, error) {
|
||||
eval := func(expr ast.Expr) (types.TypeAndValue, error) {
|
||||
info := &types.Info{
|
||||
Types: map[ast.Expr]types.TypeAndValue{},
|
||||
}
|
||||
err := types.CheckExpr(pass.Fset, pass.Pkg, expr.Pos(), expr, info)
|
||||
return info.Types[expr], err
|
||||
}
|
||||
|
||||
if !flagHelpfulTypes {
|
||||
// Don't look at code in low-level packages
|
||||
for _, imp := range pass.Pkg.Imports() {
|
||||
if imp.Path() == "syscall" || imp.Path() == "unsafe" {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn := func(node ast.Node) {
|
||||
decl := node.(*ast.GenDecl)
|
||||
if decl.Tok != token.VAR {
|
||||
return
|
||||
}
|
||||
|
||||
gen, _ := code.Generator(pass, decl.Pos())
|
||||
if gen == facts.Cgo {
|
||||
// TODO(dh): remove this exception once we can use UsesCgo
|
||||
return
|
||||
}
|
||||
|
||||
// Delay looking up parent AST nodes until we have to
|
||||
checkedDecl := false
|
||||
|
||||
specLoop:
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
if spec.Type == nil {
|
||||
continue
|
||||
}
|
||||
if len(spec.Names) != len(spec.Values) {
|
||||
continue
|
||||
}
|
||||
Tlhs := pass.TypesInfo.TypeOf(spec.Type)
|
||||
for i, v := range spec.Values {
|
||||
if !flagHelpfulTypes && spec.Names[i].Name == "_" {
|
||||
continue specLoop
|
||||
}
|
||||
Trhs := pass.TypesInfo.TypeOf(v)
|
||||
if !types.Identical(Tlhs, Trhs) {
|
||||
continue specLoop
|
||||
}
|
||||
|
||||
// Some expressions are untyped and get converted to the lhs type implicitly.
|
||||
// This applies to untyped constants, shift operations with an untyped lhs, and possibly others.
|
||||
//
|
||||
// Check if the type is truly redundant, i.e. if the type on the lhs doesn't match the default type of the untyped constant.
|
||||
tv, err := eval(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if b, ok := tv.Type.(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
|
||||
if Tlhs != types.Default(b) {
|
||||
// The rhs is untyped and its default type differs from the explicit type on the lhs
|
||||
continue specLoop
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case *ast.Ident:
|
||||
// Only flag named constant rhs if it's a predeclared identifier.
|
||||
// Don't flag other named constants, as the explicit type may aid readability.
|
||||
if pass.TypesInfo.ObjectOf(v).Pkg() != nil && !flagHelpfulTypes {
|
||||
continue specLoop
|
||||
}
|
||||
case *ast.BasicLit:
|
||||
// Do flag basic literals
|
||||
default:
|
||||
// Don't flag untyped rhs expressions unless flagHelpfulTypes is set
|
||||
if !flagHelpfulTypes {
|
||||
continue specLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !checkedDecl {
|
||||
// Don't flag global variables. These often have explicit types for godoc's sake.
|
||||
path, _ := astutil.PathEnclosingInterval(code.File(pass, decl), decl.Pos(), decl.Pos())
|
||||
pathLoop:
|
||||
for _, el := range path {
|
||||
switch el.(type) {
|
||||
case *ast.FuncDecl, *ast.FuncLit:
|
||||
checkedDecl = true
|
||||
break pathLoop
|
||||
}
|
||||
}
|
||||
if !checkedDecl {
|
||||
// decl is not inside a function
|
||||
break specLoop
|
||||
}
|
||||
}
|
||||
|
||||
report.Report(pass, spec.Type, fmt.Sprintf("%s omit type %s from declaration; it will be inferred from the right-hand side", verb, report.Render(pass, spec.Type)), report.FilterGenerated(),
|
||||
report.Fixes(edit.Fix("Remove redundant type", edit.Delete(spec.Type))))
|
||||
}
|
||||
}
|
||||
code.Preorder(pass, fn, (*ast.GenDecl)(nil))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &analysis.Analyzer{
|
||||
Run: fn,
|
||||
Requires: []*analysis.Analyzer{facts.Generated, inspect.Analyzer, facts.TokenFile, facts.Generated},
|
||||
}
|
||||
}
|
36
vendor/honnef.co/go/tools/internal/sync/sync.go
vendored
Normal file
36
vendor/honnef.co/go/tools/internal/sync/sync.go
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package sync
|
||||
|
||||
type Semaphore struct {
|
||||
ch chan struct{}
|
||||
}
|
||||
|
||||
func NewSemaphore(size int) Semaphore {
|
||||
return Semaphore{
|
||||
ch: make(chan struct{}, size),
|
||||
}
|
||||
}
|
||||
|
||||
func (sem Semaphore) Acquire() {
|
||||
sem.ch <- struct{}{}
|
||||
}
|
||||
|
||||
func (sem Semaphore) AcquireMaybe() bool {
|
||||
select {
|
||||
case sem.ch <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (sem Semaphore) Release() {
|
||||
<-sem.ch
|
||||
}
|
||||
|
||||
func (sem Semaphore) Len() int {
|
||||
return len(sem.ch)
|
||||
}
|
||||
|
||||
func (sem Semaphore) Cap() int {
|
||||
return cap(sem.ch)
|
||||
}
|
Reference in New Issue
Block a user