mirror of
https://github.com/hoernschen/dendrite.git
synced 2024-12-27 07:28:27 +00:00
Update gometalinter
This commit is contained in:
parent
cc12fc930a
commit
7a060094fc
44 changed files with 2273 additions and 652 deletions
2
vendor/manifest
vendored
2
vendor/manifest
vendored
|
@ -10,7 +10,7 @@
|
||||||
{
|
{
|
||||||
"importpath": "github.com/alecthomas/gometalinter",
|
"importpath": "github.com/alecthomas/gometalinter",
|
||||||
"repository": "https://github.com/alecthomas/gometalinter",
|
"repository": "https://github.com/alecthomas/gometalinter",
|
||||||
"revision": "0262fb20957a4c2d3bb7c834a6a125ae3884a2c6",
|
"revision": "212b1b91e362ea0b0e441c9b53ce31e81405c240",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright 2014 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 importgraph computes the forward and reverse import
|
||||||
|
// dependency graphs for all packages in a Go workspace.
|
||||||
|
package importgraph // import "golang.org/x/tools/refactor/importgraph"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/build"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Graph is an import dependency graph, either forward or reverse.
|
||||||
|
//
|
||||||
|
// The graph maps each node (a package import path) to the set of its
|
||||||
|
// successors in the graph. For a forward graph, this is the set of
|
||||||
|
// imported packages (prerequisites); for a reverse graph, it is the set
|
||||||
|
// of importing packages (clients).
|
||||||
|
//
|
||||||
|
// Graph construction inspects all imports in each package's directory,
|
||||||
|
// including those in _test.go files, so the resulting graph may be cyclic.
|
||||||
|
type Graph map[string]map[string]bool
|
||||||
|
|
||||||
|
func (g Graph) addEdge(from, to string) {
|
||||||
|
edges := g[from]
|
||||||
|
if edges == nil {
|
||||||
|
edges = make(map[string]bool)
|
||||||
|
g[from] = edges
|
||||||
|
}
|
||||||
|
edges[to] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search returns all the nodes of the graph reachable from
|
||||||
|
// any of the specified roots, by following edges forwards.
|
||||||
|
// Relationally, this is the reflexive transitive closure.
|
||||||
|
func (g Graph) Search(roots ...string) map[string]bool {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var visit func(x string)
|
||||||
|
visit = func(x string) {
|
||||||
|
if !seen[x] {
|
||||||
|
seen[x] = true
|
||||||
|
for y := range g[x] {
|
||||||
|
visit(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, root := range roots {
|
||||||
|
visit(root)
|
||||||
|
}
|
||||||
|
return seen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build scans the specified Go workspace and builds the forward and
|
||||||
|
// reverse import dependency graphs for all its packages.
|
||||||
|
// It also returns a mapping from canonical import paths to errors for packages
|
||||||
|
// whose loading was not entirely successful.
|
||||||
|
// A package may appear in the graph and in the errors mapping.
|
||||||
|
// All package paths are canonical and may contain "/vendor/".
|
||||||
|
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
|
||||||
|
type importEdge struct {
|
||||||
|
from, to string
|
||||||
|
}
|
||||||
|
type pathError struct {
|
||||||
|
path string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan interface{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sema := make(chan int, 20) // I/O concurrency limiting semaphore
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
buildutil.ForEachPackage(ctxt, func(path string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
ch <- pathError{path, err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
sema <- 1
|
||||||
|
bp, err := ctxt.Import(path, "", 0)
|
||||||
|
<-sema
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*build.NoGoError); ok {
|
||||||
|
// empty directory is not an error
|
||||||
|
} else {
|
||||||
|
ch <- pathError{path, err}
|
||||||
|
}
|
||||||
|
// Even in error cases, Import usually returns a package.
|
||||||
|
}
|
||||||
|
|
||||||
|
// absolutize resolves an import path relative
|
||||||
|
// to the current package bp.
|
||||||
|
// The absolute form may contain "vendor".
|
||||||
|
//
|
||||||
|
// The vendoring feature slows down Build by 3×.
|
||||||
|
// Here are timings from a 1400 package workspace:
|
||||||
|
// 1100ms: current code (with vendor check)
|
||||||
|
// 880ms: with a nonblocking cache around ctxt.IsDir
|
||||||
|
// 840ms: nonblocking cache with duplicate suppression
|
||||||
|
// 340ms: original code (no vendor check)
|
||||||
|
// TODO(adonovan): optimize, somehow.
|
||||||
|
memo := make(map[string]string)
|
||||||
|
absolutize := func(path string) string {
|
||||||
|
canon, ok := memo[path]
|
||||||
|
if !ok {
|
||||||
|
sema <- 1
|
||||||
|
bp2, _ := ctxt.Import(path, bp.Dir, build.FindOnly)
|
||||||
|
<-sema
|
||||||
|
|
||||||
|
if bp2 != nil {
|
||||||
|
canon = bp2.ImportPath
|
||||||
|
} else {
|
||||||
|
canon = path
|
||||||
|
}
|
||||||
|
memo[path] = canon
|
||||||
|
}
|
||||||
|
return canon
|
||||||
|
}
|
||||||
|
|
||||||
|
if bp != nil {
|
||||||
|
for _, imp := range bp.Imports {
|
||||||
|
ch <- importEdge{path, absolutize(imp)}
|
||||||
|
}
|
||||||
|
for _, imp := range bp.TestImports {
|
||||||
|
ch <- importEdge{path, absolutize(imp)}
|
||||||
|
}
|
||||||
|
for _, imp := range bp.XTestImports {
|
||||||
|
ch <- importEdge{path, absolutize(imp)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
forward = make(Graph)
|
||||||
|
reverse = make(Graph)
|
||||||
|
|
||||||
|
for e := range ch {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case pathError:
|
||||||
|
if errors == nil {
|
||||||
|
errors = make(map[string]error)
|
||||||
|
}
|
||||||
|
errors[e.path] = e.err
|
||||||
|
|
||||||
|
case importEdge:
|
||||||
|
if e.to == "C" {
|
||||||
|
continue // "C" is fake
|
||||||
|
}
|
||||||
|
forward.addEdge(e.from, e.to)
|
||||||
|
reverse.addEdge(e.to, e.from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return forward, reverse, errors
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package main // import "honnef.co/go/tools/cmd/errcheck-ng"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/errcheck"
|
||||||
|
"honnef.co/go/tools/lint/lintutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := lintutil.CheckerConfig{
|
||||||
|
Checker: errcheck.NewChecker(),
|
||||||
|
ExitNonZero: true,
|
||||||
|
}
|
||||||
|
lintutil.ProcessArgs("errcheck-ng", []lintutil.CheckerConfig{c}, os.Args[1:])
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -13,6 +13,9 @@ func main() {
|
||||||
fs.Parse(os.Args[1:])
|
fs.Parse(os.Args[1:])
|
||||||
c := simple.NewChecker()
|
c := simple.NewChecker()
|
||||||
c.CheckGenerated = *gen
|
c.CheckGenerated = *gen
|
||||||
|
cfg := lintutil.CheckerConfig{
|
||||||
lintutil.ProcessFlagSet(c, fs)
|
Checker: c,
|
||||||
|
ExitNonZero: true,
|
||||||
|
}
|
||||||
|
lintutil.ProcessFlagSet([]lintutil.CheckerConfig{cfg}, fs)
|
||||||
}
|
}
|
||||||
|
|
401
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/cmd/keyify/keyify.go
vendored
Normal file
401
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/cmd/keyify/keyify.go
vendored
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
// keyify transforms unkeyed struct literals into a keyed ones.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
|
"go/constant"
|
||||||
|
"go/printer"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fRecursive bool
|
||||||
|
fOneLine bool
|
||||||
|
fJSON bool
|
||||||
|
fMinify bool
|
||||||
|
fModified bool
|
||||||
|
fVersion bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&fRecursive, "r", false, "keyify struct initializers recursively")
|
||||||
|
flag.BoolVar(&fOneLine, "o", false, "print new struct initializer on a single line")
|
||||||
|
flag.BoolVar(&fJSON, "json", false, "print new struct initializer as JSON")
|
||||||
|
flag.BoolVar(&fMinify, "m", false, "omit fields that are set to their zero value")
|
||||||
|
flag.BoolVar(&fModified, "modified", false, "read an archive of modified files from standard input")
|
||||||
|
flag.BoolVar(&fVersion, "version", false, "Print version and exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage() {
|
||||||
|
fmt.Printf("Usage: %s [flags] <position>\n\n", os.Args[0])
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
flag.Usage = usage
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if fVersion {
|
||||||
|
version.Print()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.NArg() != 1 {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
pos := flag.Args()[0]
|
||||||
|
name, start, _, err := parsePos(pos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
eval, err := filepath.EvalSymlinks(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
name, err = filepath.Abs(eval)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
ctx := &build.Default
|
||||||
|
if fModified {
|
||||||
|
overlay, err := buildutil.ParseOverlayArchive(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
ctx = buildutil.OverlayContext(ctx, overlay)
|
||||||
|
}
|
||||||
|
bpkg, err := buildutil.ContainingPackage(ctx, cwd, name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
conf := &loader.Config{
|
||||||
|
Build: ctx,
|
||||||
|
}
|
||||||
|
conf.TypeCheckFuncBodies = func(s string) bool {
|
||||||
|
return s == bpkg.ImportPath || s == bpkg.ImportPath+"_test"
|
||||||
|
}
|
||||||
|
conf.ImportWithTests(bpkg.ImportPath)
|
||||||
|
lprog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var tf *token.File
|
||||||
|
var af *ast.File
|
||||||
|
pkg := lprog.InitialPackages()[0]
|
||||||
|
for _, ff := range pkg.Files {
|
||||||
|
file := lprog.Fset.File(ff.Pos())
|
||||||
|
if file.Name() == name {
|
||||||
|
af = ff
|
||||||
|
tf = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tstart, tend, err := fileOffsetToPos(tf, start, start)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
path, _ := astutil.PathEnclosingInterval(af, tstart, tend)
|
||||||
|
var complit *ast.CompositeLit
|
||||||
|
for _, p := range path {
|
||||||
|
if p, ok := p.(*ast.CompositeLit); ok {
|
||||||
|
complit = p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if complit == nil {
|
||||||
|
log.Fatal("no composite literal found near point")
|
||||||
|
}
|
||||||
|
if len(complit.Elts) == 0 {
|
||||||
|
printComplit(complit, complit, lprog.Fset, lprog.Fset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := complit.Elts[0].(*ast.KeyValueExpr); ok {
|
||||||
|
lit := complit
|
||||||
|
if fOneLine {
|
||||||
|
lit = copyExpr(complit, 1).(*ast.CompositeLit)
|
||||||
|
}
|
||||||
|
printComplit(complit, lit, lprog.Fset, lprog.Fset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, ok := pkg.TypeOf(complit).Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
log.Fatal("not a struct initialiser")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newComplit, lines := keyify(pkg, complit)
|
||||||
|
newFset := token.NewFileSet()
|
||||||
|
newFile := newFset.AddFile("", -1, lines)
|
||||||
|
for i := 1; i <= lines; i++ {
|
||||||
|
newFile.AddLine(i)
|
||||||
|
}
|
||||||
|
printComplit(complit, newComplit, lprog.Fset, newFset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyify(
|
||||||
|
pkg *loader.PackageInfo,
|
||||||
|
complit *ast.CompositeLit,
|
||||||
|
) (*ast.CompositeLit, int) {
|
||||||
|
var calcPos func(int) token.Pos
|
||||||
|
if fOneLine {
|
||||||
|
calcPos = func(int) token.Pos { return token.Pos(1) }
|
||||||
|
} else {
|
||||||
|
calcPos = func(i int) token.Pos { return token.Pos(2 + i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
st, _ := pkg.TypeOf(complit).Underlying().(*types.Struct)
|
||||||
|
newComplit := &ast.CompositeLit{
|
||||||
|
Type: complit.Type,
|
||||||
|
Lbrace: 1,
|
||||||
|
Rbrace: token.Pos(st.NumFields() + 2),
|
||||||
|
}
|
||||||
|
if fOneLine {
|
||||||
|
newComplit.Rbrace = 1
|
||||||
|
}
|
||||||
|
numLines := 2 + st.NumFields()
|
||||||
|
n := 0
|
||||||
|
for i := 0; i < st.NumFields(); i++ {
|
||||||
|
field := st.Field(i)
|
||||||
|
val := complit.Elts[i]
|
||||||
|
if fRecursive {
|
||||||
|
if val2, ok := val.(*ast.CompositeLit); ok {
|
||||||
|
if _, ok := pkg.TypeOf(val2.Type).Underlying().(*types.Struct); ok {
|
||||||
|
var lines int
|
||||||
|
numLines += lines
|
||||||
|
val, lines = keyify(pkg, val2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, isIface := st.Field(i).Type().Underlying().(*types.Interface)
|
||||||
|
if fMinify && (isNil(val, pkg) || (!isIface && isZero(val, pkg))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elt := &ast.KeyValueExpr{
|
||||||
|
Key: &ast.Ident{NamePos: calcPos(n), Name: field.Name()},
|
||||||
|
Value: copyExpr(val, calcPos(n)),
|
||||||
|
}
|
||||||
|
newComplit.Elts = append(newComplit.Elts, elt)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return newComplit, numLines
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNil(val ast.Expr, pkg *loader.PackageInfo) bool {
|
||||||
|
ident, ok := val.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := pkg.ObjectOf(ident).(*types.Nil); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c, ok := pkg.ObjectOf(ident).(*types.Const); ok {
|
||||||
|
if c.Val().Kind() != constant.Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !constant.BoolVal(c.Val())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZero(val ast.Expr, pkg *loader.PackageInfo) bool {
|
||||||
|
switch val := val.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
switch val.Value {
|
||||||
|
case `""`, "``", "0", "0.0", "0i", "0.":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case *ast.Ident:
|
||||||
|
return isNil(val, pkg)
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
typ := pkg.TypeOf(val.Type)
|
||||||
|
if typ == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
isIface := false
|
||||||
|
switch typ := typ.Underlying().(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
case *types.Array:
|
||||||
|
_, isIface = typ.Elem().Underlying().(*types.Interface)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, elt := range val.Elts {
|
||||||
|
if isNil(elt, pkg) || (!isIface && !isZero(elt, pkg)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func printComplit(oldlit, newlit *ast.CompositeLit, oldfset, newfset *token.FileSet) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
|
||||||
|
_ = cfg.Fprint(buf, newfset, newlit)
|
||||||
|
if fJSON {
|
||||||
|
output := struct {
|
||||||
|
Start int `json:"start"`
|
||||||
|
End int `json:"end"`
|
||||||
|
Replacement string `json:"replacement"`
|
||||||
|
}{
|
||||||
|
oldfset.Position(oldlit.Pos()).Offset,
|
||||||
|
oldfset.Position(oldlit.End()).Offset,
|
||||||
|
buf.String(),
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(os.Stdout).Encode(output)
|
||||||
|
} else {
|
||||||
|
fmt.Println(buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyExpr(expr ast.Expr, line token.Pos) ast.Expr {
|
||||||
|
switch expr := expr.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
cp := *expr
|
||||||
|
cp.ValuePos = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.OpPos = 0
|
||||||
|
cp.Y = copyExpr(cp.Y, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.CallExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.Fun = copyExpr(cp.Fun, line)
|
||||||
|
cp.Lparen = 0
|
||||||
|
for i, v := range cp.Args {
|
||||||
|
cp.Args[i] = copyExpr(v, line)
|
||||||
|
}
|
||||||
|
if cp.Ellipsis != 0 {
|
||||||
|
cp.Ellipsis = line
|
||||||
|
}
|
||||||
|
cp.Rparen = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
cp := *expr
|
||||||
|
cp.Type = copyExpr(cp.Type, line)
|
||||||
|
cp.Lbrace = 0
|
||||||
|
for i, v := range cp.Elts {
|
||||||
|
cp.Elts[i] = copyExpr(v, line)
|
||||||
|
}
|
||||||
|
cp.Rbrace = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.Ident:
|
||||||
|
cp := *expr
|
||||||
|
cp.NamePos = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.Lbrack = 0
|
||||||
|
cp.Index = copyExpr(cp.Index, line)
|
||||||
|
cp.Rbrack = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.KeyValueExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.Key = copyExpr(cp.Key, line)
|
||||||
|
cp.Colon = 0
|
||||||
|
cp.Value = copyExpr(cp.Value, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.Lparen = 0
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.Rparen = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.Sel = copyExpr(cp.Sel, line).(*ast.Ident)
|
||||||
|
return &cp
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.Lbrack = 0
|
||||||
|
cp.Low = copyExpr(cp.Low, line)
|
||||||
|
cp.High = copyExpr(cp.High, line)
|
||||||
|
cp.Max = copyExpr(cp.Max, line)
|
||||||
|
cp.Rbrack = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.StarExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.Star = 0
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.TypeAssertExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
cp.Lparen = 0
|
||||||
|
cp.Type = copyExpr(cp.Type, line)
|
||||||
|
cp.Rparen = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
cp := *expr
|
||||||
|
cp.OpPos = 0
|
||||||
|
cp.X = copyExpr(cp.X, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.MapType:
|
||||||
|
cp := *expr
|
||||||
|
cp.Map = 0
|
||||||
|
cp.Key = copyExpr(cp.Key, line)
|
||||||
|
cp.Value = copyExpr(cp.Value, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.ArrayType:
|
||||||
|
cp := *expr
|
||||||
|
cp.Lbrack = 0
|
||||||
|
cp.Len = copyExpr(cp.Len, line)
|
||||||
|
cp.Elt = copyExpr(cp.Elt, line)
|
||||||
|
return &cp
|
||||||
|
case *ast.Ellipsis:
|
||||||
|
cp := *expr
|
||||||
|
cp.Elt = copyExpr(cp.Elt, line)
|
||||||
|
cp.Ellipsis = line
|
||||||
|
return &cp
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
cp := *expr
|
||||||
|
cp.Interface = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.StructType:
|
||||||
|
cp := *expr
|
||||||
|
cp.Struct = 0
|
||||||
|
return &cp
|
||||||
|
case *ast.FuncLit:
|
||||||
|
return expr
|
||||||
|
case *ast.ChanType:
|
||||||
|
cp := *expr
|
||||||
|
cp.Arrow = 0
|
||||||
|
cp.Begin = 0
|
||||||
|
cp.Value = copyExpr(cp.Value, line)
|
||||||
|
return &cp
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("shouldn't happen: unknown ast.Expr of type %T", expr))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2013 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 (
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseOctothorpDecimal(s string) int {
|
||||||
|
if s != "" && s[0] == '#' {
|
||||||
|
if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil {
|
||||||
|
return int(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePos(pos string) (filename string, startOffset, endOffset int, err error) {
|
||||||
|
if pos == "" {
|
||||||
|
err = fmt.Errorf("no source position specified")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
colon := strings.LastIndex(pos, ":")
|
||||||
|
if colon < 0 {
|
||||||
|
err = fmt.Errorf("bad position syntax %q", pos)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filename, offset := pos[:colon], pos[colon+1:]
|
||||||
|
startOffset = -1
|
||||||
|
endOffset = -1
|
||||||
|
if hyphen := strings.Index(offset, ","); hyphen < 0 {
|
||||||
|
// e.g. "foo.go:#123"
|
||||||
|
startOffset = parseOctothorpDecimal(offset)
|
||||||
|
endOffset = startOffset
|
||||||
|
} else {
|
||||||
|
// e.g. "foo.go:#123,#456"
|
||||||
|
startOffset = parseOctothorpDecimal(offset[:hyphen])
|
||||||
|
endOffset = parseOctothorpDecimal(offset[hyphen+1:])
|
||||||
|
}
|
||||||
|
if startOffset < 0 || endOffset < 0 {
|
||||||
|
err = fmt.Errorf("invalid offset %q in query position", offset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
|
||||||
|
// Range check [start..end], inclusive of both end-points.
|
||||||
|
|
||||||
|
if 0 <= startOffset && startOffset <= file.Size() {
|
||||||
|
start = file.Pos(int(startOffset))
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("start position is beyond end of file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if 0 <= endOffset && endOffset <= file.Size() {
|
||||||
|
end = file.Pos(int(endOffset))
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("end position is beyond end of file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -4,42 +4,23 @@ package main // import "honnef.co/go/tools/cmd/megacheck"
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"honnef.co/go/tools/lint"
|
|
||||||
"honnef.co/go/tools/lint/lintutil"
|
"honnef.co/go/tools/lint/lintutil"
|
||||||
"honnef.co/go/tools/simple"
|
"honnef.co/go/tools/simple"
|
||||||
"honnef.co/go/tools/staticcheck"
|
"honnef.co/go/tools/staticcheck"
|
||||||
"honnef.co/go/tools/unused"
|
"honnef.co/go/tools/unused"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Checker struct {
|
|
||||||
Checkers []lint.Checker
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checker) Init(prog *lint.Program) {
|
|
||||||
for _, cc := range c.Checkers {
|
|
||||||
cc.Init(prog)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checker) Funcs() map[string]lint.Func {
|
|
||||||
fns := map[string]lint.Func{}
|
|
||||||
for _, cc := range c.Checkers {
|
|
||||||
for k, v := range cc.Funcs() {
|
|
||||||
fns[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fns
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var flags struct {
|
var flags struct {
|
||||||
staticcheck struct {
|
staticcheck struct {
|
||||||
enabled bool
|
enabled bool
|
||||||
generated bool
|
generated bool
|
||||||
|
exitNonZero bool
|
||||||
}
|
}
|
||||||
gosimple struct {
|
gosimple struct {
|
||||||
enabled bool
|
enabled bool
|
||||||
generated bool
|
generated bool
|
||||||
|
exitNonZero bool
|
||||||
}
|
}
|
||||||
unused struct {
|
unused struct {
|
||||||
enabled bool
|
enabled bool
|
||||||
|
@ -51,6 +32,7 @@ func main() {
|
||||||
debug string
|
debug string
|
||||||
wholeProgram bool
|
wholeProgram bool
|
||||||
reflection bool
|
reflection bool
|
||||||
|
exitNonZero bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs := lintutil.FlagSet("megacheck")
|
fs := lintutil.FlagSet("megacheck")
|
||||||
|
@ -58,11 +40,15 @@ func main() {
|
||||||
"simple.enabled", true, "Run gosimple")
|
"simple.enabled", true, "Run gosimple")
|
||||||
fs.BoolVar(&flags.gosimple.generated,
|
fs.BoolVar(&flags.gosimple.generated,
|
||||||
"simple.generated", false, "Check generated code")
|
"simple.generated", false, "Check generated code")
|
||||||
|
fs.BoolVar(&flags.gosimple.exitNonZero,
|
||||||
|
"simple.exit-non-zero", false, "Exit non-zero if any problems were found")
|
||||||
|
|
||||||
fs.BoolVar(&flags.staticcheck.enabled,
|
fs.BoolVar(&flags.staticcheck.enabled,
|
||||||
"staticcheck.enabled", true, "Run staticcheck")
|
"staticcheck.enabled", true, "Run staticcheck")
|
||||||
fs.BoolVar(&flags.staticcheck.generated,
|
fs.BoolVar(&flags.staticcheck.generated,
|
||||||
"staticcheck.generated", false, "Check generated code (only applies to a subset of checks)")
|
"staticcheck.generated", false, "Check generated code (only applies to a subset of checks)")
|
||||||
|
fs.BoolVar(&flags.staticcheck.exitNonZero,
|
||||||
|
"staticcheck.exit-non-zero", true, "Exit non-zero if any problems were found")
|
||||||
|
|
||||||
fs.BoolVar(&flags.unused.enabled,
|
fs.BoolVar(&flags.unused.enabled,
|
||||||
"unused.enabled", true, "Run unused")
|
"unused.enabled", true, "Run unused")
|
||||||
|
@ -78,22 +64,31 @@ func main() {
|
||||||
"unused.vars", true, "Report unused variables")
|
"unused.vars", true, "Report unused variables")
|
||||||
fs.BoolVar(&flags.unused.wholeProgram,
|
fs.BoolVar(&flags.unused.wholeProgram,
|
||||||
"unused.exported", false, "Treat arguments as a program and report unused exported identifiers")
|
"unused.exported", false, "Treat arguments as a program and report unused exported identifiers")
|
||||||
fs.BoolVar(&flags.unused.reflection, "unused.reflect", true, "Consider identifiers as used when it's likely they'll be accessed via reflection")
|
fs.BoolVar(&flags.unused.reflection,
|
||||||
|
"unused.reflect", true, "Consider identifiers as used when it's likely they'll be accessed via reflection")
|
||||||
|
fs.BoolVar(&flags.unused.exitNonZero,
|
||||||
|
"unused.exit-non-zero", true, "Exit non-zero if any problems were found")
|
||||||
|
|
||||||
fs.Parse(os.Args[1:])
|
fs.Parse(os.Args[1:])
|
||||||
|
|
||||||
c := &Checker{}
|
var checkers []lintutil.CheckerConfig
|
||||||
|
|
||||||
if flags.staticcheck.enabled {
|
if flags.staticcheck.enabled {
|
||||||
sac := staticcheck.NewChecker()
|
sac := staticcheck.NewChecker()
|
||||||
sac.CheckGenerated = flags.staticcheck.generated
|
sac.CheckGenerated = flags.staticcheck.generated
|
||||||
c.Checkers = append(c.Checkers, sac)
|
checkers = append(checkers, lintutil.CheckerConfig{
|
||||||
|
Checker: sac,
|
||||||
|
ExitNonZero: flags.staticcheck.exitNonZero,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.gosimple.enabled {
|
if flags.gosimple.enabled {
|
||||||
sc := simple.NewChecker()
|
sc := simple.NewChecker()
|
||||||
sc.CheckGenerated = flags.gosimple.generated
|
sc.CheckGenerated = flags.gosimple.generated
|
||||||
c.Checkers = append(c.Checkers, sc)
|
checkers = append(checkers, lintutil.CheckerConfig{
|
||||||
|
Checker: sc,
|
||||||
|
ExitNonZero: flags.gosimple.exitNonZero,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.unused.enabled {
|
if flags.unused.enabled {
|
||||||
|
@ -116,8 +111,12 @@ func main() {
|
||||||
uc := unused.NewChecker(mode)
|
uc := unused.NewChecker(mode)
|
||||||
uc.WholeProgram = flags.unused.wholeProgram
|
uc.WholeProgram = flags.unused.wholeProgram
|
||||||
uc.ConsiderReflection = flags.unused.reflection
|
uc.ConsiderReflection = flags.unused.reflection
|
||||||
c.Checkers = append(c.Checkers, unused.NewLintChecker(uc))
|
checkers = append(checkers, lintutil.CheckerConfig{
|
||||||
|
Checker: unused.NewLintChecker(uc),
|
||||||
|
ExitNonZero: flags.unused.exitNonZero,
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lintutil.ProcessFlagSet(c, fs)
|
lintutil.ProcessFlagSet(checkers, fs)
|
||||||
}
|
}
|
||||||
|
|
86
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/cmd/rdeps/rdeps.go
vendored
Normal file
86
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/cmd/rdeps/rdeps.go
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// rdeps scans GOPATH for all reverse dependencies of a set of Go
|
||||||
|
// packages.
|
||||||
|
//
|
||||||
|
// rdeps will not sort its output, and the order of the output is
|
||||||
|
// undefined. Pipe its output through sort if you need stable output.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
|
||||||
|
"github.com/kisielk/gotool"
|
||||||
|
"golang.org/x/tools/go/buildutil"
|
||||||
|
"golang.org/x/tools/refactor/importgraph"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var tags buildutil.TagsFlag
|
||||||
|
flag.Var(&tags, "tags", "List of build tags")
|
||||||
|
stdin := flag.Bool("stdin", false, "Read packages from stdin instead of the command line")
|
||||||
|
recursive := flag.Bool("r", false, "Print reverse dependencies recursively")
|
||||||
|
printVersion := flag.Bool("version", false, "Print version and exit")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *printVersion {
|
||||||
|
version.Print()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := build.Default
|
||||||
|
ctx.BuildTags = tags
|
||||||
|
var args []string
|
||||||
|
if *stdin {
|
||||||
|
s := bufio.NewScanner(os.Stdin)
|
||||||
|
for s.Scan() {
|
||||||
|
args = append(args, s.Text())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = flag.Args()
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
pkgs := gotool.ImportPaths(args)
|
||||||
|
for i, pkg := range pkgs {
|
||||||
|
bpkg, err := ctx.Import(pkg, wd, build.FindOnly)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkgs[i] = bpkg.ImportPath
|
||||||
|
}
|
||||||
|
_, reverse, errors := importgraph.Build(&ctx)
|
||||||
|
_ = errors
|
||||||
|
|
||||||
|
seen := map[string]bool{}
|
||||||
|
var printRDeps func(pkg string)
|
||||||
|
printRDeps = func(pkg string) {
|
||||||
|
for rdep := range reverse[pkg] {
|
||||||
|
if seen[rdep] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[rdep] = true
|
||||||
|
fmt.Println(rdep)
|
||||||
|
if *recursive {
|
||||||
|
printRDeps(rdep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
printRDeps(pkg)
|
||||||
|
}
|
||||||
|
for pkg, err := range errors {
|
||||||
|
fmt.Fprintf(os.Stderr, "error in package %s: %s\n", pkg, err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -15,5 +15,9 @@ func main() {
|
||||||
fs.Parse(os.Args[1:])
|
fs.Parse(os.Args[1:])
|
||||||
c := staticcheck.NewChecker()
|
c := staticcheck.NewChecker()
|
||||||
c.CheckGenerated = *gen
|
c.CheckGenerated = *gen
|
||||||
lintutil.ProcessFlagSet(c, fs)
|
cfg := lintutil.CheckerConfig{
|
||||||
|
Checker: c,
|
||||||
|
ExitNonZero: true,
|
||||||
|
}
|
||||||
|
lintutil.ProcessFlagSet([]lintutil.CheckerConfig{cfg}, fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
// structlayout-optimize reorders struct fields to minimize the amount
|
||||||
|
// of padding.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
st "honnef.co/go/tools/structlayout"
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fJSON bool
|
||||||
|
fRecurse bool
|
||||||
|
fVersion bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&fJSON, "json", false, "Format data as JSON")
|
||||||
|
flag.BoolVar(&fRecurse, "r", false, "Break up structs and reorder their fields freely")
|
||||||
|
flag.BoolVar(&fVersion, "version", false, "Print version and exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if fVersion {
|
||||||
|
version.Print()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var in []st.Field
|
||||||
|
if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(in) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !fRecurse {
|
||||||
|
in = combine(in)
|
||||||
|
}
|
||||||
|
var fields []st.Field
|
||||||
|
for _, field := range in {
|
||||||
|
if field.IsPadding {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields = append(fields, field)
|
||||||
|
}
|
||||||
|
optimize(fields)
|
||||||
|
fields = pad(fields)
|
||||||
|
|
||||||
|
if fJSON {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(fields)
|
||||||
|
} else {
|
||||||
|
for _, field := range fields {
|
||||||
|
fmt.Println(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func combine(fields []st.Field) []st.Field {
|
||||||
|
new := st.Field{}
|
||||||
|
cur := ""
|
||||||
|
var out []st.Field
|
||||||
|
wasPad := true
|
||||||
|
for _, field := range fields {
|
||||||
|
var prefix string
|
||||||
|
if field.IsPadding {
|
||||||
|
wasPad = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p := strings.Split(field.Name, ".")
|
||||||
|
prefix = strings.Join(p[:2], ".")
|
||||||
|
if field.Align > new.Align {
|
||||||
|
new.Align = field.Align
|
||||||
|
}
|
||||||
|
if !wasPad {
|
||||||
|
new.End = field.Start
|
||||||
|
new.Size = new.End - new.Start
|
||||||
|
}
|
||||||
|
if prefix != cur {
|
||||||
|
if cur != "" {
|
||||||
|
out = append(out, new)
|
||||||
|
}
|
||||||
|
cur = prefix
|
||||||
|
new = field
|
||||||
|
new.Name = prefix
|
||||||
|
} else {
|
||||||
|
new.Type = "struct"
|
||||||
|
}
|
||||||
|
wasPad = false
|
||||||
|
}
|
||||||
|
new.Size = new.End - new.Start
|
||||||
|
out = append(out, new)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func optimize(fields []st.Field) {
|
||||||
|
sort.Sort(&byAlignAndSize{fields})
|
||||||
|
}
|
||||||
|
|
||||||
|
func pad(fields []st.Field) []st.Field {
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var out []st.Field
|
||||||
|
pos := int64(0)
|
||||||
|
offsets := offsetsof(fields)
|
||||||
|
alignment := int64(1)
|
||||||
|
for i, field := range fields {
|
||||||
|
if field.Align > alignment {
|
||||||
|
alignment = field.Align
|
||||||
|
}
|
||||||
|
if offsets[i] > pos {
|
||||||
|
padding := offsets[i] - pos
|
||||||
|
out = append(out, st.Field{
|
||||||
|
IsPadding: true,
|
||||||
|
Start: pos,
|
||||||
|
End: pos + padding,
|
||||||
|
Size: padding,
|
||||||
|
})
|
||||||
|
pos += padding
|
||||||
|
}
|
||||||
|
field.Start = pos
|
||||||
|
field.End = pos + field.Size
|
||||||
|
out = append(out, field)
|
||||||
|
pos += field.Size
|
||||||
|
}
|
||||||
|
sz := size(out)
|
||||||
|
pad := align(sz, alignment) - sz
|
||||||
|
if pad > 0 {
|
||||||
|
field := out[len(out)-1]
|
||||||
|
out = append(out, st.Field{
|
||||||
|
IsPadding: true,
|
||||||
|
Start: field.End,
|
||||||
|
End: field.End + pad,
|
||||||
|
Size: pad,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func size(fields []st.Field) int64 {
|
||||||
|
n := int64(0)
|
||||||
|
for _, field := range fields {
|
||||||
|
n += field.Size
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type byAlignAndSize struct {
|
||||||
|
fields []st.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *byAlignAndSize) Len() int { return len(s.fields) }
|
||||||
|
func (s *byAlignAndSize) Swap(i, j int) {
|
||||||
|
s.fields[i], s.fields[j] = s.fields[j], s.fields[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *byAlignAndSize) Less(i, j int) bool {
|
||||||
|
// Place zero sized objects before non-zero sized objects.
|
||||||
|
if s.fields[i].Size == 0 && s.fields[j].Size != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s.fields[j].Size == 0 && s.fields[i].Size != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, place more tightly aligned objects before less tightly aligned objects.
|
||||||
|
if s.fields[i].Align != s.fields[j].Align {
|
||||||
|
return s.fields[i].Align > s.fields[j].Align
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lastly, order by size.
|
||||||
|
if s.fields[i].Size != s.fields[j].Size {
|
||||||
|
return s.fields[i].Size > s.fields[j].Size
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func offsetsof(fields []st.Field) []int64 {
|
||||||
|
offsets := make([]int64, len(fields))
|
||||||
|
var o int64
|
||||||
|
for i, f := range fields {
|
||||||
|
a := f.Align
|
||||||
|
o = align(o, a)
|
||||||
|
offsets[i] = o
|
||||||
|
o += f.Size
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
// align returns the smallest y >= x such that y % a == 0.
|
||||||
|
func align(x, a int64) int64 {
|
||||||
|
y := x + a - 1
|
||||||
|
return y - y%a
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// structlayout-pretty formats the output of structlayout with ASCII
|
||||||
|
// art.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
st "honnef.co/go/tools/structlayout"
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fVerbose bool
|
||||||
|
fVersion bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&fVerbose, "v", false, "Do not compact consecutive bytes of fields")
|
||||||
|
flag.BoolVar(&fVersion, "version", false, "Print version and exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if fVersion {
|
||||||
|
version.Print()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields []st.Field
|
||||||
|
if err := json.NewDecoder(os.Stdin).Decode(&fields); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
max := fields[len(fields)-1].End
|
||||||
|
maxLength := len(fmt.Sprintf("%d", max))
|
||||||
|
padding := strings.Repeat(" ", maxLength+2)
|
||||||
|
format := fmt.Sprintf(" %%%dd ", maxLength)
|
||||||
|
pos := int64(0)
|
||||||
|
fmt.Println(padding + "+--------+")
|
||||||
|
for _, field := range fields {
|
||||||
|
name := field.Name + " " + field.Type
|
||||||
|
if field.IsPadding {
|
||||||
|
name = "padding"
|
||||||
|
}
|
||||||
|
fmt.Printf(format+"| | <- %s (size %d, align %d)\n", pos, name, field.Size, field.Align)
|
||||||
|
fmt.Println(padding + "+--------+")
|
||||||
|
|
||||||
|
if fVerbose {
|
||||||
|
for i := int64(0); i < field.Size-1; i++ {
|
||||||
|
fmt.Printf(format+"| |\n", pos+i+1)
|
||||||
|
fmt.Println(padding + "+--------+")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if field.Size > 2 {
|
||||||
|
fmt.Println(padding + "-........-")
|
||||||
|
fmt.Println(padding + "+--------+")
|
||||||
|
fmt.Printf(format+"| |\n", pos+field.Size-1)
|
||||||
|
fmt.Println(padding + "+--------+")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += field.Size
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
// structlayout displays the layout (field sizes and padding) of structs.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/gcsizes"
|
||||||
|
st "honnef.co/go/tools/structlayout"
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fJSON bool
|
||||||
|
fVersion bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&fJSON, "json", false, "Format data as JSON")
|
||||||
|
flag.BoolVar(&fVersion, "version", false, "Print version and exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if fVersion {
|
||||||
|
version.Print()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(flag.Args()) != 2 {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := loader.Config{
|
||||||
|
Build: &build.Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkg string
|
||||||
|
var typName string
|
||||||
|
pkg = flag.Args()[0]
|
||||||
|
typName = flag.Args()[1]
|
||||||
|
conf.Import(pkg)
|
||||||
|
|
||||||
|
lprog, err := conf.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var typ types.Type
|
||||||
|
obj := lprog.Package(pkg).Pkg.Scope().Lookup(typName)
|
||||||
|
if obj == nil {
|
||||||
|
log.Fatal("couldn't find type")
|
||||||
|
}
|
||||||
|
typ = obj.Type()
|
||||||
|
|
||||||
|
st, ok := typ.Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
log.Fatal("identifier is not a struct type")
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := sizes(st, typ.(*types.Named).Obj().Name(), 0, nil)
|
||||||
|
if fJSON {
|
||||||
|
emitJSON(fields)
|
||||||
|
} else {
|
||||||
|
emitText(fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitJSON(fields []st.Field) {
|
||||||
|
if fields == nil {
|
||||||
|
fields = []st.Field{}
|
||||||
|
}
|
||||||
|
json.NewEncoder(os.Stdout).Encode(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitText(fields []st.Field) {
|
||||||
|
for _, field := range fields {
|
||||||
|
fmt.Println(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func sizes(typ *types.Struct, prefix string, base int64, out []st.Field) []st.Field {
|
||||||
|
s := gcsizes.ForArch(build.Default.GOARCH)
|
||||||
|
n := typ.NumFields()
|
||||||
|
var fields []*types.Var
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
fields = append(fields, typ.Field(i))
|
||||||
|
}
|
||||||
|
offsets := s.Offsetsof(fields)
|
||||||
|
for i := range offsets {
|
||||||
|
offsets[i] += base
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := base
|
||||||
|
for i, field := range fields {
|
||||||
|
if offsets[i] > pos {
|
||||||
|
padding := offsets[i] - pos
|
||||||
|
out = append(out, st.Field{
|
||||||
|
IsPadding: true,
|
||||||
|
Start: pos,
|
||||||
|
End: pos + padding,
|
||||||
|
Size: padding,
|
||||||
|
})
|
||||||
|
pos += padding
|
||||||
|
}
|
||||||
|
size := s.Sizeof(field.Type())
|
||||||
|
if typ2, ok := field.Type().Underlying().(*types.Struct); ok && typ2.NumFields() != 0 {
|
||||||
|
out = sizes(typ2, prefix+"."+field.Name(), pos, out)
|
||||||
|
} else {
|
||||||
|
out = append(out, st.Field{
|
||||||
|
Name: prefix + "." + field.Name(),
|
||||||
|
Type: field.Type().String(),
|
||||||
|
Start: offsets[i],
|
||||||
|
End: offsets[i] + size,
|
||||||
|
Size: size,
|
||||||
|
Align: s.Alignof(field.Type()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pos += size
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out) == 0 {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
field := &out[len(out)-1]
|
||||||
|
if field.Size == 0 {
|
||||||
|
field.Size = 1
|
||||||
|
field.End++
|
||||||
|
}
|
||||||
|
pad := s.Sizeof(typ) - field.End
|
||||||
|
if pad > 0 {
|
||||||
|
out = append(out, st.Field{
|
||||||
|
IsPadding: true,
|
||||||
|
Start: field.End,
|
||||||
|
End: field.End + pad,
|
||||||
|
Size: pad,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -70,5 +70,9 @@ func main() {
|
||||||
|
|
||||||
checker := newChecker(mode)
|
checker := newChecker(mode)
|
||||||
l := unused.NewLintChecker(checker)
|
l := unused.NewLintChecker(checker)
|
||||||
lintutil.ProcessFlagSet(l, fs)
|
cfg := lintutil.CheckerConfig{
|
||||||
|
Checker: l,
|
||||||
|
ExitNonZero: true,
|
||||||
|
}
|
||||||
|
lintutil.ProcessFlagSet([]lintutil.CheckerConfig{cfg}, fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package deprecated
|
||||||
|
|
||||||
|
type Deprecation struct {
|
||||||
|
DeprecatedSince int
|
||||||
|
AlternativeAvailableSince int
|
||||||
|
}
|
||||||
|
|
||||||
|
var Stdlib = map[string]Deprecation{
|
||||||
|
"image/jpeg.Reader": {4, 0},
|
||||||
|
// FIXME(dh): AllowBinary isn't being detected as deprecated
|
||||||
|
// because the comment has a newline right after "Deprecated:"
|
||||||
|
"go/build.AllowBinary": {7, 7},
|
||||||
|
"(archive/zip.FileHeader).CompressedSize": {1, 1},
|
||||||
|
"(archive/zip.FileHeader).UncompressedSize": {1, 1},
|
||||||
|
"(go/doc.Package).Bugs": {1, 1},
|
||||||
|
"os.SEEK_SET": {7, 7},
|
||||||
|
"os.SEEK_CUR": {7, 7},
|
||||||
|
"os.SEEK_END": {7, 7},
|
||||||
|
"(net.Dialer).Cancel": {7, 7},
|
||||||
|
"runtime.CPUProfile": {9, 0},
|
||||||
|
"compress/flate.ReadError": {6, 6},
|
||||||
|
"compress/flate.WriteError": {6, 6},
|
||||||
|
"path/filepath.HasPrefix": {0, 0},
|
||||||
|
"(net/http.Transport).Dial": {7, 7},
|
||||||
|
"(*net/http.Transport).CancelRequest": {6, 5},
|
||||||
|
"net/http.ErrWriteAfterFlush": {7, 0},
|
||||||
|
"net/http.ErrHeaderTooLong": {8, 0},
|
||||||
|
"net/http.ErrShortBody": {8, 0},
|
||||||
|
"net/http.ErrMissingContentLength": {8, 0},
|
||||||
|
"net/http/httputil.ErrPersistEOF": {0, 0},
|
||||||
|
"net/http/httputil.ErrClosed": {0, 0},
|
||||||
|
"net/http/httputil.ErrPipeline": {0, 0},
|
||||||
|
"net/http/httputil.ServerConn": {0, 0},
|
||||||
|
"net/http/httputil.NewServerConn": {0, 0},
|
||||||
|
"net/http/httputil.ClientConn": {0, 0},
|
||||||
|
"net/http/httputil.NewClientConn": {0, 0},
|
||||||
|
"net/http/httputil.NewProxyClientConn": {0, 0},
|
||||||
|
"(net/http.Request).Cancel": {7, 7},
|
||||||
|
"(text/template/parse.PipeNode).Line": {1, 1},
|
||||||
|
"(text/template/parse.ActionNode).Line": {1, 1},
|
||||||
|
"(text/template/parse.BranchNode).Line": {1, 1},
|
||||||
|
"(text/template/parse.TemplateNode).Line": {1, 1},
|
||||||
|
"database/sql/driver.ColumnConverter": {9, 9},
|
||||||
|
"database/sql/driver.Execer": {8, 8},
|
||||||
|
"database/sql/driver.Queryer": {8, 8},
|
||||||
|
"(database/sql/driver.Conn).Begin": {8, 8},
|
||||||
|
"(database/sql/driver.Stmt).Exec": {8, 8},
|
||||||
|
"(database/sql/driver.Stmt).Query": {8, 8},
|
||||||
|
"syscall.StringByteSlice": {1, 1},
|
||||||
|
"syscall.StringBytePtr": {1, 1},
|
||||||
|
"syscall.StringSlicePtr": {1, 1},
|
||||||
|
"syscall.StringToUTF16": {1, 1},
|
||||||
|
"syscall.StringToUTF16Ptr": {1, 1},
|
||||||
|
}
|
157
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/errcheck/errcheck.go
vendored
Normal file
157
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/errcheck/errcheck.go
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package errcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/types"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/functions"
|
||||||
|
"honnef.co/go/tools/lint"
|
||||||
|
"honnef.co/go/tools/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Checker struct {
|
||||||
|
funcDescs *functions.Descriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChecker() *Checker {
|
||||||
|
return &Checker{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Checker) Name() string { return "errcheck" }
|
||||||
|
func (*Checker) Prefix() string { return "ERR" }
|
||||||
|
|
||||||
|
func (c *Checker) Funcs() map[string]lint.Func {
|
||||||
|
return map[string]lint.Func{
|
||||||
|
"ERR1000": c.CheckErrcheck,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Checker) Init(prog *lint.Program) {
|
||||||
|
c.funcDescs = functions.NewDescriptions(prog.SSA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Checker) CheckErrcheck(j *lint.Job) {
|
||||||
|
for _, ssafn := range j.Program.InitialFunctions {
|
||||||
|
for _, b := range ssafn.Blocks {
|
||||||
|
for _, ins := range b.Instrs {
|
||||||
|
ssacall, ok := ins.(ssa.CallInstruction)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch lint.CallName(ssacall.Common()) {
|
||||||
|
case "fmt.Print", "fmt.Println", "fmt.Printf":
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isRecover := false
|
||||||
|
if builtin, ok := ssacall.Common().Value.(*ssa.Builtin); ok {
|
||||||
|
isRecover = ok && builtin.Name() == "recover"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ins := ins.(type) {
|
||||||
|
case ssa.Value:
|
||||||
|
refs := ins.Referrers()
|
||||||
|
if refs == nil || len(lint.FilterDebug(*refs)) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case ssa.Instruction:
|
||||||
|
// will be a 'go' or 'defer', neither of which has usable return values
|
||||||
|
default:
|
||||||
|
// shouldn't happen
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ssacall.Common().IsInvoke() {
|
||||||
|
if sc, ok := ssacall.Common().Value.(*ssa.Call); ok {
|
||||||
|
// TODO(dh): support multiple levels of
|
||||||
|
// interfaces, not just one
|
||||||
|
ssafn := sc.Common().StaticCallee()
|
||||||
|
if ssafn != nil {
|
||||||
|
ct := c.funcDescs.Get(ssafn).ConcreteReturnTypes
|
||||||
|
// TODO(dh): support >1 concrete types
|
||||||
|
if ct != nil && len(ct) == 1 {
|
||||||
|
// TODO(dh): do we have access to a
|
||||||
|
// cached method set somewhere?
|
||||||
|
ms := types.NewMethodSet(ct[0].At(ct[0].Len() - 1).Type())
|
||||||
|
// TODO(dh): where can we get the pkg
|
||||||
|
// for Lookup? Passing nil works fine
|
||||||
|
// for exported methods, but will fail
|
||||||
|
// on unexported ones
|
||||||
|
// TODO(dh): holy nesting and poor
|
||||||
|
// variable names, clean this up
|
||||||
|
fn, _ := ms.Lookup(nil, ssacall.Common().Method.Name()).Obj().(*types.Func)
|
||||||
|
if fn != nil {
|
||||||
|
ssafn := j.Program.SSA.FuncValue(fn)
|
||||||
|
if ssafn != nil {
|
||||||
|
if c.funcDescs.Get(ssafn).NilError {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ssafn := ssacall.Common().StaticCallee()
|
||||||
|
if ssafn != nil {
|
||||||
|
if c.funcDescs.Get(ssafn).NilError {
|
||||||
|
// Don't complain when the error is known to be nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch lint.CallName(ssacall.Common()) {
|
||||||
|
case "(*os.File).Close":
|
||||||
|
recv := ssacall.Common().Args[0]
|
||||||
|
if isReadOnlyFile(recv, nil) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := ssacall.Common().Signature().Results()
|
||||||
|
if res.Len() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !isRecover {
|
||||||
|
last := res.At(res.Len() - 1)
|
||||||
|
if types.TypeString(last.Type(), nil) != "error" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j.Errorf(ins, "unchecked error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isReadOnlyFile(val ssa.Value, seen map[ssa.Value]bool) bool {
|
||||||
|
if seen == nil {
|
||||||
|
seen = map[ssa.Value]bool{}
|
||||||
|
}
|
||||||
|
if seen[val] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
seen[val] = true
|
||||||
|
switch val := val.(type) {
|
||||||
|
case *ssa.Phi:
|
||||||
|
for _, edge := range val.Edges {
|
||||||
|
if !isReadOnlyFile(edge, seen) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case *ssa.Extract:
|
||||||
|
call, ok := val.Tuple.(*ssa.Call)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch lint.CallName(call.Common()) {
|
||||||
|
case "os.Open":
|
||||||
|
return true
|
||||||
|
case "os.OpenFile":
|
||||||
|
flags, ok := call.Common().Args[1].(*ssa.Const)
|
||||||
|
return ok && flags.Uint64() == 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -48,6 +48,8 @@ var stdlibDescs = map[string]Description{
|
||||||
type Description struct {
|
type Description struct {
|
||||||
// The function is known to be pure
|
// The function is known to be pure
|
||||||
Pure bool
|
Pure bool
|
||||||
|
// The function is known to be a stub
|
||||||
|
Stub bool
|
||||||
// The function is known to never return (panics notwithstanding)
|
// The function is known to never return (panics notwithstanding)
|
||||||
Infinite bool
|
Infinite bool
|
||||||
// Variable ranges
|
// Variable ranges
|
||||||
|
@ -90,6 +92,7 @@ func (d *Descriptions) Get(fn *ssa.Function) Description {
|
||||||
{
|
{
|
||||||
fd.result = stdlibDescs[fn.RelString(nil)]
|
fd.result = stdlibDescs[fn.RelString(nil)]
|
||||||
fd.result.Pure = fd.result.Pure || d.IsPure(fn)
|
fd.result.Pure = fd.result.Pure || d.IsPure(fn)
|
||||||
|
fd.result.Stub = fd.result.Stub || d.IsStub(fn)
|
||||||
fd.result.Infinite = fd.result.Infinite || !terminates(fn)
|
fd.result.Infinite = fd.result.Infinite || !terminates(fn)
|
||||||
fd.result.Ranges = vrp.BuildGraph(fn).Solve()
|
fd.result.Ranges = vrp.BuildGraph(fn).Solve()
|
||||||
fd.result.Loops = findLoops(fn)
|
fd.result.Loops = findLoops(fn)
|
||||||
|
|
|
@ -5,9 +5,41 @@ import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"honnef.co/go/tools/callgraph"
|
"honnef.co/go/tools/callgraph"
|
||||||
|
"honnef.co/go/tools/lint"
|
||||||
"honnef.co/go/tools/ssa"
|
"honnef.co/go/tools/ssa"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsStub reports whether a function is a stub. A function is
|
||||||
|
// considered a stub if it has no instructions or exactly one
|
||||||
|
// instruction, which must be either returning only constant values or
|
||||||
|
// a panic.
|
||||||
|
func (d *Descriptions) IsStub(fn *ssa.Function) bool {
|
||||||
|
if len(fn.Blocks) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(fn.Blocks) > 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
instrs := lint.FilterDebug(fn.Blocks[0].Instrs)
|
||||||
|
if len(instrs) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch instrs[0].(type) {
|
||||||
|
case *ssa.Return:
|
||||||
|
// Since this is the only instruction, the return value must
|
||||||
|
// be a constant. We consider all constants as stubs, not just
|
||||||
|
// the zero value. This does not, unfortunately, cover zero
|
||||||
|
// initialised structs, as these cause additional
|
||||||
|
// instructions.
|
||||||
|
return true
|
||||||
|
case *ssa.Panic:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Descriptions) IsPure(fn *ssa.Function) bool {
|
func (d *Descriptions) IsPure(fn *ssa.Function) bool {
|
||||||
if fn.Signature.Results().Len() == 0 {
|
if fn.Signature.Results().Len() == 0 {
|
||||||
// A function with no return values is empty or is doing some
|
// A function with no return values is empty or is doing some
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/printer"
|
"go/printer"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
@ -20,6 +21,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
|
@ -30,15 +32,85 @@ import (
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Program *Program
|
Program *Program
|
||||||
|
|
||||||
|
checker string
|
||||||
check string
|
check string
|
||||||
problems []Problem
|
problems []Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ignore struct {
|
type Ignore interface {
|
||||||
|
Match(p Problem) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineIgnore struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Checks []string
|
||||||
|
matched bool
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (li *LineIgnore) Match(p Problem) bool {
|
||||||
|
if p.Position.Filename != li.File || p.Position.Line != li.Line {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range li.Checks {
|
||||||
|
if m, _ := filepath.Match(c, p.Check); m {
|
||||||
|
li.matched = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (li *LineIgnore) String() string {
|
||||||
|
matched := "not matched"
|
||||||
|
if li.matched {
|
||||||
|
matched = "matched"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%d %s (%s)", li.File, li.Line, strings.Join(li.Checks, ", "), matched)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileIgnore struct {
|
||||||
|
File string
|
||||||
|
Checks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *FileIgnore) Match(p Problem) bool {
|
||||||
|
if p.Position.Filename != fi.File {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range fi.Checks {
|
||||||
|
if m, _ := filepath.Match(c, p.Check); m {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type GlobIgnore struct {
|
||||||
Pattern string
|
Pattern string
|
||||||
Checks []string
|
Checks []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gi *GlobIgnore) Match(p Problem) bool {
|
||||||
|
if gi.Pattern != "*" {
|
||||||
|
pkgpath := p.Package.Path()
|
||||||
|
if strings.HasSuffix(pkgpath, "_test") {
|
||||||
|
pkgpath = pkgpath[:len(pkgpath)-len("_test")]
|
||||||
|
}
|
||||||
|
name := filepath.Join(pkgpath, filepath.Base(p.Position.Filename))
|
||||||
|
if m, _ := filepath.Match(gi.Pattern, name); !m {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, c := range gi.Checks {
|
||||||
|
if m, _ := filepath.Match(c, p.Check); m {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Program struct {
|
type Program struct {
|
||||||
SSA *ssa.Program
|
SSA *ssa.Program
|
||||||
Prog *loader.Program
|
Prog *loader.Program
|
||||||
|
@ -58,51 +130,70 @@ type Func func(*Job)
|
||||||
|
|
||||||
// Problem represents a problem in some source code.
|
// Problem represents a problem in some source code.
|
||||||
type Problem struct {
|
type Problem struct {
|
||||||
Position token.Pos // position in source file
|
pos token.Pos
|
||||||
Text string // the prose that describes the problem
|
Position token.Position // position in source file
|
||||||
|
Text string // the prose that describes the problem
|
||||||
|
Check string
|
||||||
|
Checker string
|
||||||
|
Package *types.Package
|
||||||
|
Ignored bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Problem) String() string {
|
func (p *Problem) String() string {
|
||||||
return p.Text
|
if p.Check == "" {
|
||||||
|
return p.Text
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (%s)", p.Text, p.Check)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Checker interface {
|
type Checker interface {
|
||||||
|
Name() string
|
||||||
|
Prefix() string
|
||||||
Init(*Program)
|
Init(*Program)
|
||||||
Funcs() map[string]Func
|
Funcs() map[string]Func
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Linter lints Go source code.
|
// A Linter lints Go source code.
|
||||||
type Linter struct {
|
type Linter struct {
|
||||||
Checker Checker
|
Checker Checker
|
||||||
Ignores []Ignore
|
Ignores []Ignore
|
||||||
GoVersion int
|
GoVersion int
|
||||||
|
ReturnIgnored bool
|
||||||
|
|
||||||
|
automaticIgnores []Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Linter) ignore(j *Job, p Problem) bool {
|
func (l *Linter) ignore(p Problem) bool {
|
||||||
tf := j.Program.SSA.Fset.File(p.Position)
|
ignored := false
|
||||||
f := j.Program.tokenFileMap[tf]
|
for _, ig := range l.automaticIgnores {
|
||||||
pkg := j.Program.astFileMap[f].Pkg
|
// We cannot short-circuit these, as we want to record, for
|
||||||
|
// each ignore, whether it matched or not.
|
||||||
for _, ig := range l.Ignores {
|
if ig.Match(p) {
|
||||||
pkgpath := pkg.Path()
|
ignored = true
|
||||||
if strings.HasSuffix(pkgpath, "_test") {
|
|
||||||
pkgpath = pkgpath[:len(pkgpath)-len("_test")]
|
|
||||||
}
|
|
||||||
name := filepath.Join(pkgpath, filepath.Base(tf.Name()))
|
|
||||||
if m, _ := filepath.Match(ig.Pattern, name); !m {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, c := range ig.Checks {
|
|
||||||
if m, _ := filepath.Match(c, j.check); m {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ignored {
|
||||||
|
// no need to execute other ignores if we've already had a
|
||||||
|
// match.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, ig := range l.Ignores {
|
||||||
|
// We can short-circuit here, as we aren't tracking any
|
||||||
|
// information.
|
||||||
|
if ig.Match(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (prog *Program) File(node Positioner) *ast.File {
|
||||||
|
return prog.tokenFileMap[prog.SSA.Fset.File(node.Pos())]
|
||||||
|
}
|
||||||
|
|
||||||
func (j *Job) File(node Positioner) *ast.File {
|
func (j *Job) File(node Positioner) *ast.File {
|
||||||
return j.Program.tokenFileMap[j.Program.SSA.Fset.File(node.Pos())]
|
return j.Program.File(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dh): switch to sort.Slice when Go 1.9 lands.
|
// TODO(dh): switch to sort.Slice when Go 1.9 lands.
|
||||||
|
@ -116,7 +207,7 @@ func (ps byPosition) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps byPosition) Less(i int, j int) bool {
|
func (ps byPosition) Less(i int, j int) bool {
|
||||||
pi, pj := ps.fset.Position(ps.ps[i].Position), ps.fset.Position(ps.ps[j].Position)
|
pi, pj := ps.ps[i].Position, ps.ps[j].Position
|
||||||
|
|
||||||
if pi.Filename != pj.Filename {
|
if pi.Filename != pj.Filename {
|
||||||
return pi.Filename < pj.Filename
|
return pi.Filename < pj.Filename
|
||||||
|
@ -135,16 +226,40 @@ func (ps byPosition) Swap(i int, j int) {
|
||||||
ps.ps[i], ps.ps[j] = ps.ps[j], ps.ps[i]
|
ps.ps[i], ps.ps[j] = ps.ps[j], ps.ps[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
func parseDirective(s string) (cmd string, args []string) {
|
||||||
|
if !strings.HasPrefix(s, "//lint:") {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
s = strings.TrimPrefix(s, "//lint:")
|
||||||
|
fields := strings.Split(s, " ")
|
||||||
|
return fields[0], fields[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Linter) Lint(lprog *loader.Program, conf *loader.Config) []Problem {
|
||||||
ssaprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
ssaprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
||||||
ssaprog.Build()
|
ssaprog.Build()
|
||||||
pkgMap := map[*ssa.Package]*Pkg{}
|
pkgMap := map[*ssa.Package]*Pkg{}
|
||||||
var pkgs []*Pkg
|
var pkgs []*Pkg
|
||||||
for _, pkginfo := range lprog.InitialPackages() {
|
for _, pkginfo := range lprog.InitialPackages() {
|
||||||
ssapkg := ssaprog.Package(pkginfo.Pkg)
|
ssapkg := ssaprog.Package(pkginfo.Pkg)
|
||||||
|
var bp *build.Package
|
||||||
|
if len(pkginfo.Files) != 0 {
|
||||||
|
path := lprog.Fset.Position(pkginfo.Files[0].Pos()).Filename
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
var err error
|
||||||
|
ctx := conf.Build
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = &build.Default
|
||||||
|
}
|
||||||
|
bp, err = ctx.ImportDir(dir, 0)
|
||||||
|
if err != nil {
|
||||||
|
// shouldn't happen
|
||||||
|
}
|
||||||
|
}
|
||||||
pkg := &Pkg{
|
pkg := &Pkg{
|
||||||
Package: ssapkg,
|
Package: ssapkg,
|
||||||
Info: pkginfo,
|
Info: pkginfo,
|
||||||
|
BuildPkg: bp,
|
||||||
}
|
}
|
||||||
pkgMap[ssapkg] = pkg
|
pkgMap[ssapkg] = pkg
|
||||||
pkgs = append(pkgs, pkg)
|
pkgs = append(pkgs, pkg)
|
||||||
|
@ -158,6 +273,7 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||||
tokenFileMap: map[*token.File]*ast.File{},
|
tokenFileMap: map[*token.File]*ast.File{},
|
||||||
astFileMap: map[*ast.File]*Pkg{},
|
astFileMap: map[*ast.File]*Pkg{},
|
||||||
}
|
}
|
||||||
|
|
||||||
initial := map[*types.Package]struct{}{}
|
initial := map[*types.Package]struct{}{}
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
initial[pkg.Info.Pkg] = struct{}{}
|
initial[pkg.Info.Pkg] = struct{}{}
|
||||||
|
@ -176,9 +292,69 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||||
|
|
||||||
ssapkg := ssaprog.Package(pkg.Info.Pkg)
|
ssapkg := ssaprog.Package(pkg.Info.Pkg)
|
||||||
for _, f := range pkg.Info.Files {
|
for _, f := range pkg.Info.Files {
|
||||||
|
prog.astFileMap[f] = pkgMap[ssapkg]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pkginfo := range lprog.AllPackages {
|
||||||
|
for _, f := range pkginfo.Files {
|
||||||
tf := lprog.Fset.File(f.Pos())
|
tf := lprog.Fset.File(f.Pos())
|
||||||
prog.tokenFileMap[tf] = f
|
prog.tokenFileMap[tf] = f
|
||||||
prog.astFileMap[f] = pkgMap[ssapkg]
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []Problem
|
||||||
|
l.automaticIgnores = nil
|
||||||
|
for _, pkginfo := range lprog.InitialPackages() {
|
||||||
|
for _, f := range pkginfo.Files {
|
||||||
|
cm := ast.NewCommentMap(lprog.Fset, f, f.Comments)
|
||||||
|
for node, cgs := range cm {
|
||||||
|
for _, cg := range cgs {
|
||||||
|
for _, c := range cg.List {
|
||||||
|
if !strings.HasPrefix(c.Text, "//lint:") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd, args := parseDirective(c.Text)
|
||||||
|
switch cmd {
|
||||||
|
case "ignore", "file-ignore":
|
||||||
|
if len(args) < 2 {
|
||||||
|
// FIXME(dh): this causes duplicated warnings when using megacheck
|
||||||
|
p := Problem{
|
||||||
|
pos: c.Pos(),
|
||||||
|
Position: prog.DisplayPosition(c.Pos()),
|
||||||
|
Text: "malformed linter directive; missing the required reason field?",
|
||||||
|
Check: "",
|
||||||
|
Checker: l.Checker.Name(),
|
||||||
|
Package: nil,
|
||||||
|
}
|
||||||
|
out = append(out, p)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// unknown directive, ignore
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
checks := strings.Split(args[0], ",")
|
||||||
|
pos := prog.DisplayPosition(node.Pos())
|
||||||
|
var ig Ignore
|
||||||
|
switch cmd {
|
||||||
|
case "ignore":
|
||||||
|
ig = &LineIgnore{
|
||||||
|
File: pos.Filename,
|
||||||
|
Line: pos.Line,
|
||||||
|
Checks: checks,
|
||||||
|
pos: c.Pos(),
|
||||||
|
}
|
||||||
|
case "file-ignore":
|
||||||
|
ig = &FileIgnore{
|
||||||
|
File: pos.Filename,
|
||||||
|
Checks: checks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.automaticIgnores = append(l.automaticIgnores, ig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +413,7 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
j := &Job{
|
j := &Job{
|
||||||
Program: prog,
|
Program: prog,
|
||||||
|
checker: l.Checker.Name(),
|
||||||
check: k,
|
check: k,
|
||||||
}
|
}
|
||||||
jobs = append(jobs, j)
|
jobs = append(jobs, j)
|
||||||
|
@ -255,15 +432,47 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
var out []Problem
|
|
||||||
for _, j := range jobs {
|
for _, j := range jobs {
|
||||||
for _, p := range j.problems {
|
for _, p := range j.problems {
|
||||||
if !l.ignore(j, p) {
|
p.Ignored = l.ignore(p)
|
||||||
|
if l.ReturnIgnored || !p.Ignored {
|
||||||
out = append(out, p)
|
out = append(out, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, ig := range l.automaticIgnores {
|
||||||
|
ig, ok := ig.(*LineIgnore)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ig.matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, c := range ig.Checks {
|
||||||
|
idx := strings.IndexFunc(c, func(r rune) bool {
|
||||||
|
return unicode.IsNumber(r)
|
||||||
|
})
|
||||||
|
if idx == -1 {
|
||||||
|
// malformed check name, backing out
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c[:idx] != l.Checker.Prefix() {
|
||||||
|
// not for this checker
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p := Problem{
|
||||||
|
pos: ig.pos,
|
||||||
|
Position: prog.DisplayPosition(ig.pos),
|
||||||
|
Text: "this linter directive didn't match anything; should it be removed?",
|
||||||
|
Check: "",
|
||||||
|
Checker: l.Checker.Name(),
|
||||||
|
Package: nil,
|
||||||
|
}
|
||||||
|
out = append(out, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sort.Sort(byPosition{lprog.Fset, out})
|
sort.Sort(byPosition{lprog.Fset, out})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
@ -271,7 +480,8 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||||
// Pkg represents a package being linted.
|
// Pkg represents a package being linted.
|
||||||
type Pkg struct {
|
type Pkg struct {
|
||||||
*ssa.Package
|
*ssa.Package
|
||||||
Info *loader.PackageInfo
|
Info *loader.PackageInfo
|
||||||
|
BuildPkg *build.Package
|
||||||
}
|
}
|
||||||
|
|
||||||
type packager interface {
|
type packager interface {
|
||||||
|
@ -309,10 +519,55 @@ type Positioner interface {
|
||||||
Pos() token.Pos
|
Pos() token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (prog *Program) DisplayPosition(p token.Pos) token.Position {
|
||||||
|
// The //line compiler directive can be used to change the file
|
||||||
|
// name and line numbers associated with code. This can, for
|
||||||
|
// example, be used by code generation tools. The most prominent
|
||||||
|
// example is 'go tool cgo', which uses //line directives to refer
|
||||||
|
// back to the original source code.
|
||||||
|
//
|
||||||
|
// In the context of our linters, we need to treat these
|
||||||
|
// directives differently depending on context. For cgo files, we
|
||||||
|
// want to honour the directives, so that line numbers are
|
||||||
|
// adjusted correctly. For all other files, we want to ignore the
|
||||||
|
// directives, so that problems are reported at their actual
|
||||||
|
// position and not, for example, a yacc grammar file. This also
|
||||||
|
// affects the ignore mechanism, since it operates on the position
|
||||||
|
// information stored within problems. With this implementation, a
|
||||||
|
// user will ignore foo.go, not foo.y
|
||||||
|
|
||||||
|
pkg := prog.astFileMap[prog.tokenFileMap[prog.Prog.Fset.File(p)]]
|
||||||
|
bp := pkg.BuildPkg
|
||||||
|
adjPos := prog.Prog.Fset.Position(p)
|
||||||
|
if bp == nil {
|
||||||
|
// couldn't find the package for some reason (deleted? faulty
|
||||||
|
// file system?)
|
||||||
|
return adjPos
|
||||||
|
}
|
||||||
|
base := filepath.Base(adjPos.Filename)
|
||||||
|
for _, f := range bp.CgoFiles {
|
||||||
|
if f == base {
|
||||||
|
// this is a cgo file, use the adjusted position
|
||||||
|
return adjPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not a cgo file, ignore //line directives
|
||||||
|
return prog.Prog.Fset.PositionFor(p, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem {
|
func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem {
|
||||||
|
tf := j.Program.SSA.Fset.File(n.Pos())
|
||||||
|
f := j.Program.tokenFileMap[tf]
|
||||||
|
pkg := j.Program.astFileMap[f].Pkg
|
||||||
|
|
||||||
|
pos := j.Program.DisplayPosition(n.Pos())
|
||||||
problem := Problem{
|
problem := Problem{
|
||||||
Position: n.Pos(),
|
pos: n.Pos(),
|
||||||
Text: fmt.Sprintf(format, args...) + fmt.Sprintf(" (%s)", j.check),
|
Position: pos,
|
||||||
|
Text: fmt.Sprintf(format, args...),
|
||||||
|
Check: j.check,
|
||||||
|
Checker: j.checker,
|
||||||
|
Package: pkg,
|
||||||
}
|
}
|
||||||
j.problems = append(j.problems, problem)
|
j.problems = append(j.problems, problem)
|
||||||
return &j.problems[len(j.problems)-1]
|
return &j.problems[len(j.problems)-1]
|
||||||
|
@ -422,6 +677,31 @@ func IsGenerated(f *ast.File) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Preamble(f *ast.File) string {
|
||||||
|
cutoff := f.Package
|
||||||
|
if f.Doc != nil {
|
||||||
|
cutoff = f.Doc.Pos()
|
||||||
|
}
|
||||||
|
var out []string
|
||||||
|
for _, cmt := range f.Comments {
|
||||||
|
if cmt.Pos() >= cutoff {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out = append(out, cmt.Text())
|
||||||
|
}
|
||||||
|
return strings.Join(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPointerLike(T types.Type) bool {
|
||||||
|
switch T := T.Underlying().(type) {
|
||||||
|
case *types.Interface, *types.Chan, *types.Map, *types.Pointer:
|
||||||
|
return true
|
||||||
|
case *types.Basic:
|
||||||
|
return T.Kind() == types.UnsafePointer
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (j *Job) IsGoVersion(minor int) bool {
|
func (j *Job) IsGoVersion(minor int) bool {
|
||||||
return j.Program.GoVersion >= minor
|
return j.Program.GoVersion >= minor
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,23 +8,70 @@
|
||||||
package lintutil // import "honnef.co/go/tools/lint/lintutil"
|
package lintutil // import "honnef.co/go/tools/lint/lintutil"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"honnef.co/go/tools/lint"
|
"honnef.co/go/tools/lint"
|
||||||
|
"honnef.co/go/tools/version"
|
||||||
|
|
||||||
"github.com/kisielk/gotool"
|
"github.com/kisielk/gotool"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type OutputFormatter interface {
|
||||||
|
Format(p lint.Problem)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextOutput struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o TextOutput) Format(p lint.Problem) {
|
||||||
|
fmt.Fprintf(o.w, "%v: %s\n", relativePositionString(p.Position), p.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONOutput struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o JSONOutput) Format(p lint.Problem) {
|
||||||
|
type location struct {
|
||||||
|
File string `json:"file"`
|
||||||
|
Line int `json:"line"`
|
||||||
|
Column int `json:"column"`
|
||||||
|
}
|
||||||
|
jp := struct {
|
||||||
|
Checker string `json:"checker"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Severity string `json:"severity,omitempty"`
|
||||||
|
Location location `json:"location"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Ignored bool `json:"ignored"`
|
||||||
|
}{
|
||||||
|
p.Checker,
|
||||||
|
p.Check,
|
||||||
|
"", // TODO(dh): support severity
|
||||||
|
location{
|
||||||
|
p.Position.Filename,
|
||||||
|
p.Position.Line,
|
||||||
|
p.Position.Column,
|
||||||
|
},
|
||||||
|
p.Text,
|
||||||
|
p.Ignored,
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(o.w).Encode(jp)
|
||||||
|
}
|
||||||
func usage(name string, flags *flag.FlagSet) func() {
|
func usage(name string, flags *flag.FlagSet) func() {
|
||||||
return func() {
|
return func() {
|
||||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
|
||||||
|
@ -38,13 +85,14 @@ func usage(name string, flags *flag.FlagSet) func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type runner struct {
|
type runner struct {
|
||||||
checker lint.Checker
|
checker lint.Checker
|
||||||
tags []string
|
tags []string
|
||||||
ignores []lint.Ignore
|
ignores []lint.Ignore
|
||||||
version int
|
version int
|
||||||
|
returnIgnored bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runner runner) resolveRelative(importPaths []string) (goFiles bool, err error) {
|
func resolveRelative(importPaths []string, tags []string) (goFiles bool, err error) {
|
||||||
if len(importPaths) == 0 {
|
if len(importPaths) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -57,7 +105,7 @@ func (runner runner) resolveRelative(importPaths []string) (goFiles bool, err er
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
ctx := build.Default
|
ctx := build.Default
|
||||||
ctx.BuildTags = runner.tags
|
ctx.BuildTags = tags
|
||||||
for i, path := range importPaths {
|
for i, path := range importPaths {
|
||||||
bpkg, err := ctx.Import(path, wd, build.FindOnly)
|
bpkg, err := ctx.Import(path, wd, build.FindOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -80,7 +128,7 @@ func parseIgnore(s string) ([]lint.Ignore, error) {
|
||||||
}
|
}
|
||||||
path := p[0]
|
path := p[0]
|
||||||
checks := strings.Split(p[1], ",")
|
checks := strings.Split(p[1], ",")
|
||||||
out = append(out, lint.Ignore{Pattern: path, Checks: checks})
|
out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
@ -117,6 +165,9 @@ func FlagSet(name string) *flag.FlagSet {
|
||||||
flags.String("tags", "", "List of `build tags`")
|
flags.String("tags", "", "List of `build tags`")
|
||||||
flags.String("ignore", "", "Space separated list of checks to ignore, in the following format: 'import/path/file.go:Check1,Check2,...' Both the import path and file name sections support globbing, e.g. 'os/exec/*_test.go'")
|
flags.String("ignore", "", "Space separated list of checks to ignore, in the following format: 'import/path/file.go:Check1,Check2,...' Both the import path and file name sections support globbing, e.g. 'os/exec/*_test.go'")
|
||||||
flags.Bool("tests", true, "Include tests")
|
flags.Bool("tests", true, "Include tests")
|
||||||
|
flags.Bool("version", false, "Print version and exit")
|
||||||
|
flags.Bool("show-ignored", false, "Don't filter ignored problems")
|
||||||
|
flags.String("f", "text", "Output `format` (valid choices are 'text' and 'json')")
|
||||||
|
|
||||||
tags := build.Default.ReleaseTags
|
tags := build.Default.ReleaseTags
|
||||||
v := tags[len(tags)-1][2:]
|
v := tags[len(tags)-1][2:]
|
||||||
|
@ -129,67 +180,105 @@ func FlagSet(name string) *flag.FlagSet {
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessFlagSet(c lint.Checker, fs *flag.FlagSet) {
|
type CheckerConfig struct {
|
||||||
|
Checker lint.Checker
|
||||||
|
ExitNonZero bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessFlagSet(confs []CheckerConfig, fs *flag.FlagSet) {
|
||||||
tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
|
tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
|
||||||
ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
|
ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
|
||||||
tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
|
tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
|
||||||
version := fs.Lookup("go").Value.(flag.Getter).Get().(int)
|
goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
|
||||||
|
format := fs.Lookup("f").Value.(flag.Getter).Get().(string)
|
||||||
|
printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
|
||||||
|
showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
|
||||||
|
|
||||||
ps, lprog, err := Lint(c, fs.Args(), &Options{
|
if printVersion {
|
||||||
Tags: strings.Fields(tags),
|
version.Print()
|
||||||
LintTests: tests,
|
os.Exit(0)
|
||||||
Ignores: ignore,
|
}
|
||||||
GoVersion: version,
|
|
||||||
|
var cs []lint.Checker
|
||||||
|
for _, conf := range confs {
|
||||||
|
cs = append(cs, conf.Checker)
|
||||||
|
}
|
||||||
|
pss, err := Lint(cs, fs.Args(), &Options{
|
||||||
|
Tags: strings.Fields(tags),
|
||||||
|
LintTests: tests,
|
||||||
|
Ignores: ignore,
|
||||||
|
GoVersion: goVersion,
|
||||||
|
ReturnIgnored: showIgnored,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
unclean := false
|
|
||||||
for _, p := range ps {
|
var ps []lint.Problem
|
||||||
unclean = true
|
for _, p := range pss {
|
||||||
pos := lprog.Fset.Position(p.Position)
|
ps = append(ps, p...)
|
||||||
fmt.Printf("%v: %s\n", relativePositionString(pos), p.Text)
|
|
||||||
}
|
}
|
||||||
if unclean {
|
|
||||||
os.Exit(1)
|
var f OutputFormatter
|
||||||
|
switch format {
|
||||||
|
case "text":
|
||||||
|
f = TextOutput{os.Stdout}
|
||||||
|
case "json":
|
||||||
|
f = JSONOutput{os.Stdout}
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "unsupported output format %q\n", format)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range ps {
|
||||||
|
f.Format(p)
|
||||||
|
}
|
||||||
|
for i, p := range pss {
|
||||||
|
if len(p) != 0 && confs[i].ExitNonZero {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Tags []string
|
Tags []string
|
||||||
LintTests bool
|
LintTests bool
|
||||||
Ignores string
|
Ignores string
|
||||||
GoVersion int
|
GoVersion int
|
||||||
|
ReturnIgnored bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Lint(c lint.Checker, pkgs []string, opt *Options) ([]lint.Problem, *loader.Program, error) {
|
func Lint(cs []lint.Checker, pkgs []string, opt *Options) ([][]lint.Problem, error) {
|
||||||
// TODO(dh): Instead of returning the loader.Program, we should
|
|
||||||
// store token.Position instead of token.Pos in lint.Problem.
|
|
||||||
if opt == nil {
|
if opt == nil {
|
||||||
opt = &Options{}
|
opt = &Options{}
|
||||||
}
|
}
|
||||||
ignores, err := parseIgnore(opt.Ignores)
|
ignores, err := parseIgnore(opt.Ignores)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
|
||||||
runner := &runner{
|
|
||||||
checker: c,
|
|
||||||
tags: opt.Tags,
|
|
||||||
ignores: ignores,
|
|
||||||
version: opt.GoVersion,
|
|
||||||
}
|
}
|
||||||
paths := gotool.ImportPaths(pkgs)
|
paths := gotool.ImportPaths(pkgs)
|
||||||
goFiles, err := runner.resolveRelative(paths)
|
goFiles, err := resolveRelative(paths, opt.Tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ctx := build.Default
|
ctx := build.Default
|
||||||
ctx.BuildTags = runner.tags
|
ctx.BuildTags = opt.Tags
|
||||||
|
hadError := false
|
||||||
conf := &loader.Config{
|
conf := &loader.Config{
|
||||||
Build: &ctx,
|
Build: &ctx,
|
||||||
ParserMode: parser.ParseComments,
|
ParserMode: parser.ParseComments,
|
||||||
ImportPkgs: map[string]bool{},
|
ImportPkgs: map[string]bool{},
|
||||||
|
TypeChecker: types.Config{
|
||||||
|
Error: func(err error) {
|
||||||
|
// Only print the first error found
|
||||||
|
if hadError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hadError = true
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if goFiles {
|
if goFiles {
|
||||||
conf.CreateFromFilenames("adhoc", paths...)
|
conf.CreateFromFilenames("adhoc", paths...)
|
||||||
|
@ -200,9 +289,21 @@ func Lint(c lint.Checker, pkgs []string, opt *Options) ([]lint.Problem, *loader.
|
||||||
}
|
}
|
||||||
lprog, err := conf.Load()
|
lprog, err := conf.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return runner.lint(lprog), lprog, nil
|
|
||||||
|
var problems [][]lint.Problem
|
||||||
|
for _, c := range cs {
|
||||||
|
runner := &runner{
|
||||||
|
checker: c,
|
||||||
|
tags: opt.Tags,
|
||||||
|
ignores: ignores,
|
||||||
|
version: opt.GoVersion,
|
||||||
|
returnIgnored: opt.ReturnIgnored,
|
||||||
|
}
|
||||||
|
problems = append(problems, runner.lint(lprog, conf))
|
||||||
|
}
|
||||||
|
return problems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shortPath(path string) string {
|
func shortPath(path string) string {
|
||||||
|
@ -230,18 +331,19 @@ func relativePositionString(pos token.Position) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessArgs(name string, c lint.Checker, args []string) {
|
func ProcessArgs(name string, cs []CheckerConfig, args []string) {
|
||||||
flags := FlagSet(name)
|
flags := FlagSet(name)
|
||||||
flags.Parse(args)
|
flags.Parse(args)
|
||||||
|
|
||||||
ProcessFlagSet(c, flags)
|
ProcessFlagSet(cs, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runner *runner) lint(lprog *loader.Program) []lint.Problem {
|
func (runner *runner) lint(lprog *loader.Program, conf *loader.Config) []lint.Problem {
|
||||||
l := &lint.Linter{
|
l := &lint.Linter{
|
||||||
Checker: runner.checker,
|
Checker: runner.checker,
|
||||||
Ignores: runner.ignores,
|
Ignores: runner.ignores,
|
||||||
GoVersion: runner.version,
|
GoVersion: runner.version,
|
||||||
|
ReturnIgnored: runner.returnIgnored,
|
||||||
}
|
}
|
||||||
return l.Lint(lprog)
|
return l.Lint(lprog, conf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ func TestAll(t *testing.T, c lint.Checker, dir string) {
|
||||||
for version, fis := range files {
|
for version, fis := range files {
|
||||||
l := &lint.Linter{Checker: c, GoVersion: version}
|
l := &lint.Linter{Checker: c, GoVersion: version}
|
||||||
|
|
||||||
res := l.Lint(lprog)
|
res := l.Lint(lprog, conf)
|
||||||
for _, fi := range fis {
|
for _, fi := range fis {
|
||||||
name := fi.Name()
|
name := fi.Name()
|
||||||
src := sources[name]
|
src := sources[name]
|
||||||
|
@ -101,8 +101,7 @@ func TestAll(t *testing.T, c lint.Checker, dir string) {
|
||||||
for _, in := range ins {
|
for _, in := range ins {
|
||||||
ok := false
|
ok := false
|
||||||
for i, p := range res {
|
for i, p := range res {
|
||||||
pos := lprog.Fset.Position(p.Position)
|
if p.Position.Line != in.Line || filepath.Base(p.Position.Filename) != name {
|
||||||
if pos.Line != in.Line || filepath.Base(pos.Filename) != name {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if in.Match.MatchString(p.Text) {
|
if in.Match.MatchString(p.Text) {
|
||||||
|
@ -121,11 +120,10 @@ func TestAll(t *testing.T, c lint.Checker, dir string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, p := range res {
|
for _, p := range res {
|
||||||
pos := lprog.Fset.Position(p.Position)
|
name := filepath.Base(p.Position.Filename)
|
||||||
name := filepath.Base(pos.Filename)
|
|
||||||
for _, fi := range fis {
|
for _, fi := range fis {
|
||||||
if name == fi.Name() {
|
if name == fi.Name() {
|
||||||
t.Errorf("Unexpected problem at %s: %v", pos, p.Text)
|
t.Errorf("Unexpected problem at %s: %v", p.Position, p.Text)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +147,7 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
|
||||||
}
|
}
|
||||||
var ins []instruction
|
var ins []instruction
|
||||||
for _, cg := range f.Comments {
|
for _, cg := range f.Comments {
|
||||||
ln := fset.Position(cg.Pos()).Line
|
ln := fset.PositionFor(cg.Pos(), false).Line
|
||||||
raw := cg.Text()
|
raw := cg.Text()
|
||||||
for _, line := range strings.Split(raw, "\n") {
|
for _, line := range strings.Split(raw, "\n") {
|
||||||
if line == "" || strings.HasPrefix(line, "#") {
|
if line == "" || strings.HasPrefix(line, "#") {
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -29,6 +29,9 @@ func NewChecker() *Checker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*Checker) Name() string { return "gosimple" }
|
||||||
|
func (*Checker) Prefix() string { return "S" }
|
||||||
|
|
||||||
func (c *Checker) Init(prog *lint.Program) {
|
func (c *Checker) Init(prog *lint.Program) {
|
||||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
||||||
"S1023": c.LintRedundantBreak,
|
"S1023": c.LintRedundantBreak,
|
||||||
"S1024": c.LintTimeUntil,
|
"S1024": c.LintTimeUntil,
|
||||||
"S1025": c.LintRedundantSprintf,
|
"S1025": c.LintRedundantSprintf,
|
||||||
"S1026": c.LintStringCopy,
|
"S1026": nil,
|
||||||
"S1027": nil,
|
"S1027": nil,
|
||||||
"S1028": c.LintErrorsNewSprintf,
|
"S1028": c.LintErrorsNewSprintf,
|
||||||
"S1029": c.LintRangeStringRunes,
|
"S1029": c.LintRangeStringRunes,
|
||||||
|
@ -1022,7 +1025,9 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) {
|
||||||
fn := func(node ast.Node) bool {
|
fn := func(node ast.Node) bool {
|
||||||
fn1(node)
|
fn1(node)
|
||||||
fn2(node)
|
fn2(node)
|
||||||
fn3(node)
|
if j.IsGoVersion(4) {
|
||||||
|
fn3(node)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, f := range c.filterGenerated(j.Program.Files) {
|
for _, f := range c.filterGenerated(j.Program.Files) {
|
||||||
|
@ -1702,81 +1707,6 @@ func (c *Checker) LintRedundantSprintf(j *lint.Job) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checker) LintStringCopy(j *lint.Job) {
|
|
||||||
emptyStringLit := func(e ast.Expr) bool {
|
|
||||||
bl, ok := e.(*ast.BasicLit)
|
|
||||||
return ok && bl.Value == `""`
|
|
||||||
}
|
|
||||||
fn := func(node ast.Node) bool {
|
|
||||||
switch x := node.(type) {
|
|
||||||
case *ast.BinaryExpr: // "" + s, s + ""
|
|
||||||
if x.Op != token.ADD {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
l1 := j.Program.Prog.Fset.Position(x.X.Pos()).Line
|
|
||||||
l2 := j.Program.Prog.Fset.Position(x.Y.Pos()).Line
|
|
||||||
if l1 != l2 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var want ast.Expr
|
|
||||||
switch {
|
|
||||||
case emptyStringLit(x.X):
|
|
||||||
want = x.Y
|
|
||||||
case emptyStringLit(x.Y):
|
|
||||||
want = x.X
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
j.Errorf(x, "should use %s instead of %s",
|
|
||||||
j.Render(want), j.Render(x))
|
|
||||||
case *ast.CallExpr:
|
|
||||||
if j.IsCallToAST(x, "fmt.Sprint") && len(x.Args) == 1 {
|
|
||||||
// fmt.Sprint(x)
|
|
||||||
|
|
||||||
argT := j.Program.Info.TypeOf(x.Args[0])
|
|
||||||
bt, ok := argT.Underlying().(*types.Basic)
|
|
||||||
if !ok || bt.Kind() != types.String {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.Implements(j, argT, "fmt.Stringer") || c.Implements(j, argT, "error") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
j.Errorf(x, "should use %s instead of %s", j.Render(x.Args[0]), j.Render(x))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// string([]byte(s))
|
|
||||||
bt, ok := j.Program.Info.TypeOf(x.Fun).(*types.Basic)
|
|
||||||
if !ok || bt.Kind() != types.String {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
nested, ok := x.Args[0].(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
st, ok := j.Program.Info.TypeOf(nested.Fun).(*types.Slice)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
et, ok := st.Elem().(*types.Basic)
|
|
||||||
if !ok || et.Kind() != types.Byte {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
xt, ok := j.Program.Info.TypeOf(nested.Args[0]).(*types.Basic)
|
|
||||||
if !ok || xt.Kind() != types.String {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j.Errorf(x, "should use %s instead of %s",
|
|
||||||
j.Render(nested.Args[0]), j.Render(x))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, f := range c.filterGenerated(j.Program.Files) {
|
|
||||||
ast.Inspect(f, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checker) LintErrorsNewSprintf(j *lint.Job) {
|
func (c *Checker) LintErrorsNewSprintf(j *lint.Job) {
|
||||||
fn := func(node ast.Node) bool {
|
fn := func(node ast.Node) bool {
|
||||||
if !j.IsCallToAST(node, "errors.New") {
|
if !j.IsCallToAST(node, "errors.New") {
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package staticcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/lint"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildTags(f *ast.File) [][]string {
|
||||||
|
var out [][]string
|
||||||
|
for _, line := range strings.Split(lint.Preamble(f), "\n") {
|
||||||
|
if !strings.HasPrefix(line, "+build ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(strings.TrimPrefix(line, "+build "))
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
out = append(out, fields)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
|
@ -4,20 +4,20 @@ package staticcheck // import "honnef.co/go/tools/staticcheck"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
htmltemplate "html/template"
|
htmltemplate "html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
texttemplate "text/template"
|
texttemplate "text/template"
|
||||||
|
|
||||||
|
"honnef.co/go/tools/deprecated"
|
||||||
"honnef.co/go/tools/functions"
|
"honnef.co/go/tools/functions"
|
||||||
"honnef.co/go/tools/gcsizes"
|
|
||||||
"honnef.co/go/tools/internal/sharedcheck"
|
"honnef.co/go/tools/internal/sharedcheck"
|
||||||
"honnef.co/go/tools/lint"
|
"honnef.co/go/tools/lint"
|
||||||
"honnef.co/go/tools/ssa"
|
"honnef.co/go/tools/ssa"
|
||||||
|
@ -111,14 +111,12 @@ var (
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSyncPoolSizeRules = map[string]CallCheck{
|
checkSyncPoolValueRules = map[string]CallCheck{
|
||||||
"(*sync.Pool).Put": func(call *Call) {
|
"(*sync.Pool).Put": func(call *Call) {
|
||||||
// TODO(dh): allow users to pass in a custom build environment
|
|
||||||
sizes := gcsizes.ForArch(build.Default.GOARCH)
|
|
||||||
arg := call.Args[0]
|
arg := call.Args[0]
|
||||||
typ := arg.Value.Value.Type()
|
typ := arg.Value.Value.Type()
|
||||||
if !types.IsInterface(typ) && sizes.Sizeof(typ) > sizes.WordSize {
|
if !lint.IsPointerLike(typ) {
|
||||||
arg.Invalid("argument should be one word large or less to avoid allocations")
|
arg.Invalid("argument should be pointer-like to avoid allocations")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -209,6 +207,9 @@ func NewChecker() *Checker {
|
||||||
return &Checker{}
|
return &Checker{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*Checker) Name() string { return "staticcheck" }
|
||||||
|
func (*Checker) Prefix() string { return "SA" }
|
||||||
|
|
||||||
func (c *Checker) Funcs() map[string]lint.Func {
|
func (c *Checker) Funcs() map[string]lint.Func {
|
||||||
return map[string]lint.Func{
|
return map[string]lint.Func{
|
||||||
"SA1000": c.callChecker(checkRegexpRules),
|
"SA1000": c.callChecker(checkRegexpRules),
|
||||||
|
@ -265,6 +266,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
||||||
"SA4016": c.CheckSillyBitwiseOps,
|
"SA4016": c.CheckSillyBitwiseOps,
|
||||||
"SA4017": c.CheckPureFunctions,
|
"SA4017": c.CheckPureFunctions,
|
||||||
"SA4018": c.CheckSelfAssignment,
|
"SA4018": c.CheckSelfAssignment,
|
||||||
|
"SA4019": c.CheckDuplicateBuildConstraints,
|
||||||
|
|
||||||
"SA5000": c.CheckNilMaps,
|
"SA5000": c.CheckNilMaps,
|
||||||
"SA5001": c.CheckEarlyDefer,
|
"SA5001": c.CheckEarlyDefer,
|
||||||
|
@ -277,7 +279,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
||||||
|
|
||||||
"SA6000": c.callChecker(checkRegexpMatchLoopRules),
|
"SA6000": c.callChecker(checkRegexpMatchLoopRules),
|
||||||
"SA6001": c.CheckMapBytesKey,
|
"SA6001": c.CheckMapBytesKey,
|
||||||
"SA6002": c.callChecker(checkSyncPoolSizeRules),
|
"SA6002": c.callChecker(checkSyncPoolValueRules),
|
||||||
"SA6003": c.CheckRangeStringRunes,
|
"SA6003": c.CheckRangeStringRunes,
|
||||||
"SA6004": nil,
|
"SA6004": nil,
|
||||||
|
|
||||||
|
@ -301,36 +303,62 @@ func (c *Checker) filterGenerated(files []*ast.File) []*ast.File {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checker) Init(prog *lint.Program) {
|
func (c *Checker) deprecateObject(m map[types.Object]string, prog *lint.Program, obj types.Object) {
|
||||||
c.funcDescs = functions.NewDescriptions(prog.SSA)
|
if obj.Pkg() == nil {
|
||||||
c.deprecatedObjs = map[types.Object]string{}
|
return
|
||||||
c.nodeFns = map[ast.Node]*ssa.Function{}
|
|
||||||
|
|
||||||
for _, fn := range prog.AllFunctions {
|
|
||||||
if fn.Blocks != nil {
|
|
||||||
applyStdlibKnowledge(fn)
|
|
||||||
ssa.OptimizeBlocks(fn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
f := prog.File(obj)
|
||||||
|
if f == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := c.deprecationMessage(f, prog.Prog.Fset, obj)
|
||||||
|
if msg != "" {
|
||||||
|
m[obj] = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deprecated := []map[types.Object]string{}
|
func (c *Checker) Init(prog *lint.Program) {
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
for _, pkginfo := range prog.Prog.AllPackages {
|
wg.Add(3)
|
||||||
pkginfo := pkginfo
|
go func() {
|
||||||
scope := pkginfo.Pkg.Scope()
|
c.funcDescs = functions.NewDescriptions(prog.SSA)
|
||||||
names := scope.Names()
|
for _, fn := range prog.AllFunctions {
|
||||||
wg.Add(1)
|
if fn.Blocks != nil {
|
||||||
|
applyStdlibKnowledge(fn)
|
||||||
|
ssa.OptimizeBlocks(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
m := map[types.Object]string{}
|
go func() {
|
||||||
deprecated = append(deprecated, m)
|
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||||
go func(m map[types.Object]string) {
|
wg.Done()
|
||||||
for _, name := range names {
|
}()
|
||||||
obj := scope.Lookup(name)
|
|
||||||
msg := c.deprecationMessage(pkginfo.Files, prog.SSA.Fset, obj)
|
go func() {
|
||||||
if msg != "" {
|
c.deprecatedObjs = map[types.Object]string{}
|
||||||
m[obj] = msg
|
for _, ssapkg := range prog.SSA.AllPackages() {
|
||||||
|
ssapkg := ssapkg
|
||||||
|
for _, member := range ssapkg.Members {
|
||||||
|
obj := member.Object()
|
||||||
|
if obj == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.deprecateObject(c.deprecatedObjs, prog, obj)
|
||||||
|
if typ, ok := obj.Type().(*types.Named); ok {
|
||||||
|
for i := 0; i < typ.NumMethods(); i++ {
|
||||||
|
meth := typ.Method(i)
|
||||||
|
c.deprecateObject(c.deprecatedObjs, prog, meth)
|
||||||
|
}
|
||||||
|
|
||||||
|
if iface, ok := typ.Underlying().(*types.Interface); ok {
|
||||||
|
for i := 0; i < iface.NumExplicitMethods(); i++ {
|
||||||
|
meth := iface.ExplicitMethod(i)
|
||||||
|
c.deprecateObject(c.deprecatedObjs, prog, meth)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if typ, ok := obj.Type().Underlying().(*types.Struct); ok {
|
if typ, ok := obj.Type().Underlying().(*types.Struct); ok {
|
||||||
n := typ.NumFields()
|
n := typ.NumFields()
|
||||||
|
@ -338,51 +366,20 @@ func (c *Checker) Init(prog *lint.Program) {
|
||||||
// FIXME(dh): This code will not find deprecated
|
// FIXME(dh): This code will not find deprecated
|
||||||
// fields in anonymous structs.
|
// fields in anonymous structs.
|
||||||
field := typ.Field(i)
|
field := typ.Field(i)
|
||||||
msg := c.deprecationMessage(pkginfo.Files, prog.SSA.Fset, field)
|
c.deprecateObject(c.deprecatedObjs, prog, field)
|
||||||
if msg != "" {
|
|
||||||
m[field] = msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Done()
|
}
|
||||||
}(m)
|
wg.Done()
|
||||||
}
|
}()
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
for _, m := range deprecated {
|
|
||||||
for k, v := range m {
|
|
||||||
c.deprecatedObjs[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
|
func (c *Checker) deprecationMessage(file *ast.File, fset *token.FileSet, obj types.Object) (message string) {
|
||||||
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
pos := obj.Pos()
|
||||||
p := int(pos)
|
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
|
||||||
base := f.Base()
|
|
||||||
return base <= p && p < base+f.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
func pathEnclosingInterval(files []*ast.File, fset *token.FileSet, start, end token.Pos) (path []ast.Node, exact bool) {
|
|
||||||
for _, f := range files {
|
|
||||||
if f.Pos() == token.NoPos {
|
|
||||||
// This can happen if the parser saw
|
|
||||||
// too many errors and bailed out.
|
|
||||||
// (Use parser.AllErrors to prevent that.)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !tokenFileContainsPos(fset.File(f.Pos()), start) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
|
|
||||||
return path, exact
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checker) deprecationMessage(files []*ast.File, fset *token.FileSet, obj types.Object) (message string) {
|
|
||||||
path, _ := pathEnclosingInterval(files, fset, obj.Pos(), obj.Pos())
|
|
||||||
if len(path) <= 2 {
|
if len(path) <= 2 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -2065,7 +2062,7 @@ func (c *Checker) CheckCyclicFinalizer(j *lint.Job) {
|
||||||
}
|
}
|
||||||
for _, b := range mc.Bindings {
|
for _, b := range mc.Bindings {
|
||||||
if b == v {
|
if b == v {
|
||||||
pos := j.Program.SSA.Fset.Position(mc.Fn.Pos())
|
pos := j.Program.DisplayPosition(mc.Fn.Pos())
|
||||||
j.Errorf(edge.Site, "the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos)
|
j.Errorf(edge.Site, "the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2166,6 +2163,11 @@ func (c *Checker) CheckInfiniteRecursion(j *lint.Job) {
|
||||||
if edge.Callee != node {
|
if edge.Callee != node {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if _, ok := edge.Site.(*ssa.Go); ok {
|
||||||
|
// Recursively spawning goroutines doesn't consume
|
||||||
|
// stack space infinitely, so don't flag it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
block := edge.Site.Block()
|
block := edge.Site.Block()
|
||||||
canReturn := false
|
canReturn := false
|
||||||
|
@ -2437,7 +2439,7 @@ fnLoop:
|
||||||
if callee == nil {
|
if callee == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.funcDescs.Get(callee).Pure {
|
if c.funcDescs.Get(callee).Pure && !c.funcDescs.Get(callee).Stub {
|
||||||
j.Errorf(ins, "%s is a pure function but its return value is ignored", callee.Name())
|
j.Errorf(ins, "%s is a pure function but its return value is ignored", callee.Name())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -2446,22 +2448,6 @@ fnLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enclosingFunction(j *lint.Job, node ast.Node) *ast.FuncDecl {
|
|
||||||
f := j.File(node)
|
|
||||||
path, _ := astutil.PathEnclosingInterval(f, node.Pos(), node.Pos())
|
|
||||||
for _, e := range path {
|
|
||||||
fn, ok := e.(*ast.FuncDecl)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fn.Name == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) {
|
func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) {
|
||||||
obj := j.Program.Info.ObjectOf(ident)
|
obj := j.Program.Info.ObjectOf(ident)
|
||||||
if obj.Pkg() == nil {
|
if obj.Pkg() == nil {
|
||||||
|
@ -2471,19 +2457,34 @@ func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) {
|
||||||
return alt != "", alt
|
return alt != "", alt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func selectorName(j *lint.Job, expr *ast.SelectorExpr) string {
|
||||||
|
sel := j.Program.Info.Selections[expr]
|
||||||
|
if sel == nil {
|
||||||
|
if x, ok := expr.X.(*ast.Ident); ok {
|
||||||
|
return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unsupported selector: %v", expr))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Checker) enclosingFunc(sel *ast.SelectorExpr) *ssa.Function {
|
||||||
|
fn := c.nodeFns[sel]
|
||||||
|
if fn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for fn.Parent() != nil {
|
||||||
|
fn = fn.Parent()
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Checker) CheckDeprecated(j *lint.Job) {
|
func (c *Checker) CheckDeprecated(j *lint.Job) {
|
||||||
fn := func(node ast.Node) bool {
|
fn := func(node ast.Node) bool {
|
||||||
sel, ok := node.(*ast.SelectorExpr)
|
sel, ok := node.(*ast.SelectorExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if fn := enclosingFunction(j, sel); fn != nil {
|
|
||||||
if ok, _ := c.isDeprecated(j, fn.Name); ok {
|
|
||||||
// functions that are deprecated may use deprecated
|
|
||||||
// symbols
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := j.Program.Info.ObjectOf(sel.Sel)
|
obj := j.Program.Info.ObjectOf(sel.Sel)
|
||||||
if obj.Pkg() == nil {
|
if obj.Pkg() == nil {
|
||||||
|
@ -2495,6 +2496,24 @@ func (c *Checker) CheckDeprecated(j *lint.Job) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if ok, alt := c.isDeprecated(j, sel.Sel); ok {
|
if ok, alt := c.isDeprecated(j, sel.Sel); ok {
|
||||||
|
// Look for the first available alternative, not the first
|
||||||
|
// version something was deprecated in. If a function was
|
||||||
|
// deprecated in Go 1.6, an alternative has been available
|
||||||
|
// already in 1.0, and we're targetting 1.2, it still
|
||||||
|
// makes sense to use the alternative from 1.0, to be
|
||||||
|
// future-proof.
|
||||||
|
minVersion := deprecated.Stdlib[selectorName(j, sel)].AlternativeAvailableSince
|
||||||
|
if !j.IsGoVersion(minVersion) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fn := c.enclosingFunc(sel); fn != nil {
|
||||||
|
if _, ok := c.deprecatedObjs[fn.Object()]; ok {
|
||||||
|
// functions that are deprecated may use deprecated
|
||||||
|
// symbols
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
j.Errorf(sel, "%s is deprecated: %s", j.Render(sel), alt)
|
j.Errorf(sel, "%s is deprecated: %s", j.Render(sel), alt)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -2784,3 +2803,39 @@ func (c *Checker) CheckSelfAssignment(j *lint.Job) {
|
||||||
ast.Inspect(f, fn)
|
ast.Inspect(f, fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildTagsIdentical(s1, s2 []string) bool {
|
||||||
|
if len(s1) != len(s2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s1s := make([]string, len(s1))
|
||||||
|
copy(s1s, s1)
|
||||||
|
sort.Strings(s1s)
|
||||||
|
s2s := make([]string, len(s2))
|
||||||
|
copy(s2s, s2)
|
||||||
|
sort.Strings(s2s)
|
||||||
|
for i, s := range s1s {
|
||||||
|
if s != s2s[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Checker) CheckDuplicateBuildConstraints(job *lint.Job) {
|
||||||
|
for _, f := range c.filterGenerated(job.Program.Files) {
|
||||||
|
constraints := buildTags(f)
|
||||||
|
for i, constraint1 := range constraints {
|
||||||
|
for j, constraint2 := range constraints {
|
||||||
|
if i >= j {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if buildTagsIdentical(constraint1, constraint2) {
|
||||||
|
job.Errorf(f, "identical build constraints %q and %q",
|
||||||
|
strings.Join(constraint1, " "),
|
||||||
|
strings.Join(constraint2, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package structlayout
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Start int64 `json:"start"`
|
||||||
|
End int64 `json:"end"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Align int64 `json:"align"`
|
||||||
|
IsPadding bool `json:"is_padding"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) String() string {
|
||||||
|
if f.IsPadding {
|
||||||
|
return fmt.Sprintf("%s: %d-%d (size %d, align %d)",
|
||||||
|
"padding", f.Start, f.End, f.Size, f.Align)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %s: %d-%d (size %d, align %d)",
|
||||||
|
f.Name, f.Type, f.Start, f.End, f.Size, f.Align)
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2016 Dominik Honnef
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -26,6 +26,9 @@ type LintChecker struct {
|
||||||
c *Checker
|
c *Checker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*LintChecker) Name() string { return "unused" }
|
||||||
|
func (*LintChecker) Prefix() string { return "U" }
|
||||||
|
|
||||||
func (l *LintChecker) Init(*lint.Program) {}
|
func (l *LintChecker) Init(*lint.Program) {}
|
||||||
func (l *LintChecker) Funcs() map[string]lint.Func {
|
func (l *LintChecker) Funcs() map[string]lint.Func {
|
||||||
return map[string]lint.Func{
|
return map[string]lint.Func{
|
||||||
|
@ -275,6 +278,51 @@ func (c *Checker) Check(lprog *loader.Program) []Unused {
|
||||||
return unused
|
return unused
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isNoCopyType reports whether a type represents the NoCopy sentinel
|
||||||
|
// type. The NoCopy type is a named struct with no fields and exactly
|
||||||
|
// one method `func Lock()` that is empty.
|
||||||
|
//
|
||||||
|
// FIXME(dh): currently we're not checking that the function body is
|
||||||
|
// empty.
|
||||||
|
func isNoCopyType(typ types.Type) bool {
|
||||||
|
st, ok := typ.Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if st.NumFields() != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
named, ok := typ.(*types.Named)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if named.NumMethods() != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
meth := named.Method(0)
|
||||||
|
if meth.Name() != "Lock" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sig := meth.Type().(*types.Signature)
|
||||||
|
if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Checker) useNoCopyFields(typ types.Type) {
|
||||||
|
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||||
|
n := st.NumFields()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
field := st.Field(i)
|
||||||
|
if isNoCopyType(field.Type()) {
|
||||||
|
c.graph.markUsedBy(field, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Checker) useExportedFields(typ types.Type) {
|
func (c *Checker) useExportedFields(typ types.Type) {
|
||||||
if st, ok := typ.Underlying().(*types.Struct); ok {
|
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||||
n := st.NumFields()
|
n := st.NumFields()
|
||||||
|
@ -485,6 +533,7 @@ func (c *Checker) processTypes(pkg *loader.PackageInfo) {
|
||||||
interfaces = append(interfaces, obj)
|
interfaces = append(interfaces, obj)
|
||||||
}
|
}
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
|
c.useNoCopyFields(obj)
|
||||||
if pkg.Pkg.Name() != "main" && !c.WholeProgram {
|
if pkg.Pkg.Name() != "main" && !c.WholeProgram {
|
||||||
c.useExportedFields(obj)
|
c.useExportedFields(obj)
|
||||||
}
|
}
|
||||||
|
|
17
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/version/version.go
vendored
Normal file
17
vendor/src/github.com/alecthomas/gometalinter/_linters/src/honnef.co/go/tools/version/version.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Version = "2017.2"
|
||||||
|
|
||||||
|
func Print() {
|
||||||
|
if Version == "devel" {
|
||||||
|
fmt.Printf("%s (no version)\n", filepath.Base(os.Args[0]))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s %s\n", filepath.Base(os.Args[0]), Version)
|
||||||
|
}
|
||||||
|
}
|
|
@ -309,120 +309,20 @@
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"importpath": "honnef.co/go/tools/callgraph",
|
"importpath": "golang.org/x/tools/refactor/importgraph",
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
"repository": "https://go.googlesource.com/tools",
|
||||||
"vcs": "git",
|
"vcs": "git",
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
"revision": "9c477bae194915bfd4bc8c314e90e28b9ec1c831",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"path": "callgraph",
|
"path": "/refactor/importgraph",
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"importpath": "honnef.co/go/tools/cmd/gosimple",
|
"importpath": "honnef.co/go/tools",
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
"repository": "https://github.com/dominikh/go-tools",
|
||||||
"vcs": "git",
|
"vcs": "git",
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
"revision": "50914165a1ae448f1608c6c325d052313396182e",
|
||||||
"branch": "master",
|
"branch": "HEAD",
|
||||||
"path": "/cmd/gosimple",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/cmd/megacheck",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/cmd/megacheck",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/cmd/staticcheck",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/cmd/staticcheck",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/cmd/unused",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/cmd/unused",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/functions",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "functions",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/gcsizes",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "gcsizes",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/internal/sharedcheck",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/internal/sharedcheck",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/lint",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "lint",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/simple",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "simple",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/ssa",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "ssa",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/staticcheck",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "staticcheck",
|
|
||||||
"notests": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "honnef.co/go/tools/unused",
|
|
||||||
"repository": "https://github.com/dominikh/go-tools",
|
|
||||||
"vcs": "git",
|
|
||||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "unused",
|
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -450,4 +350,4 @@
|
||||||
"notests": true
|
"notests": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -235,7 +235,7 @@ var defaultLinters = map[string]LinterConfig{
|
||||||
Command: `gas -fmt=csv`,
|
Command: `gas -fmt=csv`,
|
||||||
Pattern: `^(?P<path>.*?\.go),(?P<line>\d+),(?P<message>[^,]+,[^,]+,[^,]+)`,
|
Pattern: `^(?P<path>.*?\.go),(?P<line>\d+),(?P<message>[^,]+,[^,]+,[^,]+)`,
|
||||||
InstallFrom: "github.com/GoASTScanner/gas",
|
InstallFrom: "github.com/GoASTScanner/gas",
|
||||||
PartitionStrategy: partitionPathsAsDirectories,
|
PartitionStrategy: partitionPathsAsFiles,
|
||||||
defaultEnabled: true,
|
defaultEnabled: true,
|
||||||
IsFast: true,
|
IsFast: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,7 +26,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupFlags(app *kingpin.Application) {
|
func setupFlags(app *kingpin.Application) {
|
||||||
app.Flag("config", "Load JSON configuration from file.").Action(loadConfig).String()
|
app.Flag("config", "Load JSON configuration from file.").Envar("GOMETALINTER_CONFIG").Action(loadConfig).String()
|
||||||
app.Flag("disable", "Disable previously enabled linters.").PlaceHolder("LINTER").Short('D').Action(disableAction).Strings()
|
app.Flag("disable", "Disable previously enabled linters.").PlaceHolder("LINTER").Short('D').Action(disableAction).Strings()
|
||||||
app.Flag("enable", "Enable previously disabled linters.").PlaceHolder("LINTER").Short('E').Action(enableAction).Strings()
|
app.Flag("enable", "Enable previously disabled linters.").PlaceHolder("LINTER").Short('E').Action(enableAction).Strings()
|
||||||
app.Flag("linter", "Define a linter.").PlaceHolder("NAME:COMMAND:PATTERN").Action(cliLinterOverrides).StringMap()
|
app.Flag("linter", "Define a linter.").PlaceHolder("NAME:COMMAND:PATTERN").Action(cliLinterOverrides).StringMap()
|
||||||
|
@ -156,8 +156,8 @@ func formatLinters() string {
|
||||||
if install == "()" {
|
if install == "()" {
|
||||||
install = ""
|
install = ""
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, " %s %s\n %s\n %s\n",
|
fmt.Fprintf(w, " %s: %s\n\tcommand: %s\n\tregex: %s\n\tfast: %t\n\tdefault enabled: %t\n\n",
|
||||||
linter.Name, install, linter.Command, linter.Pattern)
|
linter.Name, install, linter.Command, linter.Pattern, linter.IsFast, linter.defaultEnabled)
|
||||||
}
|
}
|
||||||
return w.String()
|
return w.String()
|
||||||
}
|
}
|
||||||
|
|
37
vendor/src/github.com/alecthomas/gometalinter/regressiontests/gas_test.go
vendored
Normal file
37
vendor/src/github.com/alecthomas/gometalinter/regressiontests/gas_test.go
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package regressiontests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gotestyourself/gotestyourself/fs"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGas(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
dir := fs.NewDir(t, "test-gas",
|
||||||
|
fs.WithFile("file.go", gasFileErrorUnhandled("root")),
|
||||||
|
fs.WithDir("sub",
|
||||||
|
fs.WithFile("file.go", gasFileErrorUnhandled("sub"))))
|
||||||
|
defer dir.Remove()
|
||||||
|
expected := Issues{
|
||||||
|
{Linter: "gas", Severity: "warning", Path: "file.go", Line: 3, Col: 0, Message: "Errors unhandled.,LOW,HIGH"},
|
||||||
|
{Linter: "gas", Severity: "warning", Path: "sub/file.go", Line: 3, Col: 0, Message: "Errors unhandled.,LOW,HIGH"},
|
||||||
|
}
|
||||||
|
actual := RunLinter(t, "gas", dir.Path())
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasFileErrorUnhandled(pkg string) string {
|
||||||
|
return fmt.Sprintf(`package %s
|
||||||
|
func badFunction() string {
|
||||||
|
u, _ := ErrorHandle()
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorHandle() (u string, err error) {
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
`, pkg)
|
||||||
|
}
|
Loading…
Reference in a new issue