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",
|
||||
"repository": "https://github.com/alecthomas/gometalinter",
|
||||
"revision": "0262fb20957a4c2d3bb7c834a6a125ae3884a2c6",
|
||||
"revision": "212b1b91e362ea0b0e441c9b53ce31e81405c240",
|
||||
"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:])
|
||||
c := simple.NewChecker()
|
||||
c.CheckGenerated = *gen
|
||||
|
||||
lintutil.ProcessFlagSet(c, fs)
|
||||
cfg := lintutil.CheckerConfig{
|
||||
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 (
|
||||
"os"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
"honnef.co/go/tools/lint/lintutil"
|
||||
"honnef.co/go/tools/simple"
|
||||
"honnef.co/go/tools/staticcheck"
|
||||
"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() {
|
||||
var flags struct {
|
||||
staticcheck struct {
|
||||
enabled bool
|
||||
generated bool
|
||||
exitNonZero bool
|
||||
}
|
||||
gosimple struct {
|
||||
enabled bool
|
||||
generated bool
|
||||
exitNonZero bool
|
||||
}
|
||||
unused struct {
|
||||
enabled bool
|
||||
|
@ -51,6 +32,7 @@ func main() {
|
|||
debug string
|
||||
wholeProgram bool
|
||||
reflection bool
|
||||
exitNonZero bool
|
||||
}
|
||||
}
|
||||
fs := lintutil.FlagSet("megacheck")
|
||||
|
@ -58,11 +40,15 @@ func main() {
|
|||
"simple.enabled", true, "Run gosimple")
|
||||
fs.BoolVar(&flags.gosimple.generated,
|
||||
"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,
|
||||
"staticcheck.enabled", true, "Run staticcheck")
|
||||
fs.BoolVar(&flags.staticcheck.generated,
|
||||
"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,
|
||||
"unused.enabled", true, "Run unused")
|
||||
|
@ -78,22 +64,31 @@ func main() {
|
|||
"unused.vars", true, "Report unused variables")
|
||||
fs.BoolVar(&flags.unused.wholeProgram,
|
||||
"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:])
|
||||
|
||||
c := &Checker{}
|
||||
var checkers []lintutil.CheckerConfig
|
||||
|
||||
if flags.staticcheck.enabled {
|
||||
sac := staticcheck.NewChecker()
|
||||
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 {
|
||||
sc := simple.NewChecker()
|
||||
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 {
|
||||
|
@ -116,8 +111,12 @@ func main() {
|
|||
uc := unused.NewChecker(mode)
|
||||
uc.WholeProgram = flags.unused.wholeProgram
|
||||
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:])
|
||||
c := staticcheck.NewChecker()
|
||||
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)
|
||||
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 {
|
||||
// The function is known to be pure
|
||||
Pure bool
|
||||
// The function is known to be a stub
|
||||
Stub bool
|
||||
// The function is known to never return (panics notwithstanding)
|
||||
Infinite bool
|
||||
// Variable ranges
|
||||
|
@ -90,6 +92,7 @@ func (d *Descriptions) Get(fn *ssa.Function) Description {
|
|||
{
|
||||
fd.result = stdlibDescs[fn.RelString(nil)]
|
||||
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.Ranges = vrp.BuildGraph(fn).Solve()
|
||||
fd.result.Loops = findLoops(fn)
|
||||
|
|
|
@ -5,9 +5,41 @@ import (
|
|||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/callgraph"
|
||||
"honnef.co/go/tools/lint"
|
||||
"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 {
|
||||
if fn.Signature.Results().Len() == 0 {
|
||||
// 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"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
|
@ -20,6 +21,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
|
@ -30,15 +32,85 @@ import (
|
|||
type Job struct {
|
||||
Program *Program
|
||||
|
||||
checker string
|
||||
check string
|
||||
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
|
||||
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 {
|
||||
SSA *ssa.Program
|
||||
Prog *loader.Program
|
||||
|
@ -58,15 +130,25 @@ type Func func(*Job)
|
|||
|
||||
// Problem represents a problem in some source code.
|
||||
type Problem struct {
|
||||
Position token.Pos // position in source file
|
||||
pos token.Pos
|
||||
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 {
|
||||
if p.Check == "" {
|
||||
return p.Text
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", p.Text, p.Check)
|
||||
}
|
||||
|
||||
type Checker interface {
|
||||
Name() string
|
||||
Prefix() string
|
||||
Init(*Program)
|
||||
Funcs() map[string]Func
|
||||
}
|
||||
|
@ -76,33 +158,42 @@ type Linter struct {
|
|||
Checker Checker
|
||||
Ignores []Ignore
|
||||
GoVersion int
|
||||
ReturnIgnored bool
|
||||
|
||||
automaticIgnores []Ignore
|
||||
}
|
||||
|
||||
func (l *Linter) ignore(j *Job, p Problem) bool {
|
||||
tf := j.Program.SSA.Fset.File(p.Position)
|
||||
f := j.Program.tokenFileMap[tf]
|
||||
pkg := j.Program.astFileMap[f].Pkg
|
||||
|
||||
func (l *Linter) ignore(p Problem) bool {
|
||||
ignored := false
|
||||
for _, ig := range l.automaticIgnores {
|
||||
// We cannot short-circuit these, as we want to record, for
|
||||
// each ignore, whether it matched or not.
|
||||
if ig.Match(p) {
|
||||
ignored = true
|
||||
}
|
||||
}
|
||||
if ignored {
|
||||
// no need to execute other ignores if we've already had a
|
||||
// match.
|
||||
return true
|
||||
}
|
||||
for _, ig := range l.Ignores {
|
||||
pkgpath := pkg.Path()
|
||||
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 {
|
||||
// We can short-circuit here, as we aren't tracking any
|
||||
// information.
|
||||
if ig.Match(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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.
|
||||
|
@ -116,7 +207,7 @@ func (ps byPosition) Len() int {
|
|||
}
|
||||
|
||||
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 {
|
||||
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]
|
||||
}
|
||||
|
||||
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.Build()
|
||||
pkgMap := map[*ssa.Package]*Pkg{}
|
||||
var pkgs []*Pkg
|
||||
for _, pkginfo := range lprog.InitialPackages() {
|
||||
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{
|
||||
Package: ssapkg,
|
||||
Info: pkginfo,
|
||||
BuildPkg: bp,
|
||||
}
|
||||
pkgMap[ssapkg] = pkg
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
@ -158,6 +273,7 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
|||
tokenFileMap: map[*token.File]*ast.File{},
|
||||
astFileMap: map[*ast.File]*Pkg{},
|
||||
}
|
||||
|
||||
initial := map[*types.Package]struct{}{}
|
||||
for _, pkg := range pkgs {
|
||||
initial[pkg.Info.Pkg] = struct{}{}
|
||||
|
@ -176,9 +292,69 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
|||
|
||||
ssapkg := ssaprog.Package(pkg.Info.Pkg)
|
||||
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())
|
||||
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 {
|
||||
j := &Job{
|
||||
Program: prog,
|
||||
checker: l.Checker.Name(),
|
||||
check: k,
|
||||
}
|
||||
jobs = append(jobs, j)
|
||||
|
@ -255,15 +432,47 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
|||
}
|
||||
wg.Wait()
|
||||
|
||||
var out []Problem
|
||||
for _, j := range jobs {
|
||||
for _, p := range j.problems {
|
||||
if !l.ignore(j, p) {
|
||||
p.Ignored = l.ignore(p)
|
||||
if l.ReturnIgnored || !p.Ignored {
|
||||
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})
|
||||
return out
|
||||
}
|
||||
|
@ -272,6 +481,7 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
|||
type Pkg struct {
|
||||
*ssa.Package
|
||||
Info *loader.PackageInfo
|
||||
BuildPkg *build.Package
|
||||
}
|
||||
|
||||
type packager interface {
|
||||
|
@ -309,10 +519,55 @@ type Positioner interface {
|
|||
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 {
|
||||
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{
|
||||
Position: n.Pos(),
|
||||
Text: fmt.Sprintf(format, args...) + fmt.Sprintf(" (%s)", j.check),
|
||||
pos: n.Pos(),
|
||||
Position: pos,
|
||||
Text: fmt.Sprintf(format, args...),
|
||||
Check: j.check,
|
||||
Checker: j.checker,
|
||||
Package: pkg,
|
||||
}
|
||||
j.problems = append(j.problems, problem)
|
||||
return &j.problems[len(j.problems)-1]
|
||||
|
@ -422,6 +677,31 @@ func IsGenerated(f *ast.File) bool {
|
|||
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 {
|
||||
return j.Program.GoVersion >= minor
|
||||
}
|
||||
|
|
|
@ -8,23 +8,70 @@
|
|||
package lintutil // import "honnef.co/go/tools/lint/lintutil"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
"honnef.co/go/tools/version"
|
||||
|
||||
"github.com/kisielk/gotool"
|
||||
"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() {
|
||||
return func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
|
||||
|
@ -42,9 +89,10 @@ type runner struct {
|
|||
tags []string
|
||||
ignores []lint.Ignore
|
||||
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 {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -57,7 +105,7 @@ func (runner runner) resolveRelative(importPaths []string) (goFiles bool, err er
|
|||
return false, err
|
||||
}
|
||||
ctx := build.Default
|
||||
ctx.BuildTags = runner.tags
|
||||
ctx.BuildTags = tags
|
||||
for i, path := range importPaths {
|
||||
bpkg, err := ctx.Import(path, wd, build.FindOnly)
|
||||
if err != nil {
|
||||
|
@ -80,7 +128,7 @@ func parseIgnore(s string) ([]lint.Ignore, error) {
|
|||
}
|
||||
path := p[0]
|
||||
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
|
||||
}
|
||||
|
@ -117,6 +165,9 @@ func FlagSet(name string) *flag.FlagSet {
|
|||
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.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
|
||||
v := tags[len(tags)-1][2:]
|
||||
|
@ -129,67 +180,105 @@ func FlagSet(name string) *flag.FlagSet {
|
|||
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)
|
||||
ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
|
||||
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 {
|
||||
version.Print()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
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: version,
|
||||
GoVersion: goVersion,
|
||||
ReturnIgnored: showIgnored,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
unclean := false
|
||||
for _, p := range ps {
|
||||
unclean = true
|
||||
pos := lprog.Fset.Position(p.Position)
|
||||
fmt.Printf("%v: %s\n", relativePositionString(pos), p.Text)
|
||||
|
||||
var ps []lint.Problem
|
||||
for _, p := range pss {
|
||||
ps = append(ps, p...)
|
||||
}
|
||||
if unclean {
|
||||
|
||||
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 {
|
||||
Tags []string
|
||||
LintTests bool
|
||||
Ignores string
|
||||
GoVersion int
|
||||
ReturnIgnored bool
|
||||
}
|
||||
|
||||
func Lint(c lint.Checker, pkgs []string, opt *Options) ([]lint.Problem, *loader.Program, error) {
|
||||
// TODO(dh): Instead of returning the loader.Program, we should
|
||||
// store token.Position instead of token.Pos in lint.Problem.
|
||||
func Lint(cs []lint.Checker, pkgs []string, opt *Options) ([][]lint.Problem, error) {
|
||||
if opt == nil {
|
||||
opt = &Options{}
|
||||
}
|
||||
ignores, err := parseIgnore(opt.Ignores)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
runner := &runner{
|
||||
checker: c,
|
||||
tags: opt.Tags,
|
||||
ignores: ignores,
|
||||
version: opt.GoVersion,
|
||||
return nil, err
|
||||
}
|
||||
paths := gotool.ImportPaths(pkgs)
|
||||
goFiles, err := runner.resolveRelative(paths)
|
||||
goFiles, err := resolveRelative(paths, opt.Tags)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
ctx := build.Default
|
||||
ctx.BuildTags = runner.tags
|
||||
ctx.BuildTags = opt.Tags
|
||||
hadError := false
|
||||
conf := &loader.Config{
|
||||
Build: &ctx,
|
||||
ParserMode: parser.ParseComments,
|
||||
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 {
|
||||
conf.CreateFromFilenames("adhoc", paths...)
|
||||
|
@ -200,9 +289,21 @@ func Lint(c lint.Checker, pkgs []string, opt *Options) ([]lint.Problem, *loader.
|
|||
}
|
||||
lprog, err := conf.Load()
|
||||
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 {
|
||||
|
@ -230,18 +331,19 @@ func relativePositionString(pos token.Position) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func ProcessArgs(name string, c lint.Checker, args []string) {
|
||||
func ProcessArgs(name string, cs []CheckerConfig, args []string) {
|
||||
flags := FlagSet(name)
|
||||
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{
|
||||
Checker: runner.checker,
|
||||
Ignores: runner.ignores,
|
||||
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 {
|
||||
l := &lint.Linter{Checker: c, GoVersion: version}
|
||||
|
||||
res := l.Lint(lprog)
|
||||
res := l.Lint(lprog, conf)
|
||||
for _, fi := range fis {
|
||||
name := fi.Name()
|
||||
src := sources[name]
|
||||
|
@ -101,8 +101,7 @@ func TestAll(t *testing.T, c lint.Checker, dir string) {
|
|||
for _, in := range ins {
|
||||
ok := false
|
||||
for i, p := range res {
|
||||
pos := lprog.Fset.Position(p.Position)
|
||||
if pos.Line != in.Line || filepath.Base(pos.Filename) != name {
|
||||
if p.Position.Line != in.Line || filepath.Base(p.Position.Filename) != name {
|
||||
continue
|
||||
}
|
||||
if in.Match.MatchString(p.Text) {
|
||||
|
@ -121,11 +120,10 @@ func TestAll(t *testing.T, c lint.Checker, dir string) {
|
|||
}
|
||||
}
|
||||
for _, p := range res {
|
||||
pos := lprog.Fset.Position(p.Position)
|
||||
name := filepath.Base(pos.Filename)
|
||||
name := filepath.Base(p.Position.Filename)
|
||||
for _, fi := range fis {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +147,7 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
|
|||
}
|
||||
var ins []instruction
|
||||
for _, cg := range f.Comments {
|
||||
ln := fset.Position(cg.Pos()).Line
|
||||
ln := fset.PositionFor(cg.Pos(), false).Line
|
||||
raw := cg.Text()
|
||||
for _, line := range strings.Split(raw, "\n") {
|
||||
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) {
|
||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||
}
|
||||
|
@ -61,7 +64,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
|||
"S1023": c.LintRedundantBreak,
|
||||
"S1024": c.LintTimeUntil,
|
||||
"S1025": c.LintRedundantSprintf,
|
||||
"S1026": c.LintStringCopy,
|
||||
"S1026": nil,
|
||||
"S1027": nil,
|
||||
"S1028": c.LintErrorsNewSprintf,
|
||||
"S1029": c.LintRangeStringRunes,
|
||||
|
@ -1022,7 +1025,9 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) {
|
|||
fn := func(node ast.Node) bool {
|
||||
fn1(node)
|
||||
fn2(node)
|
||||
if j.IsGoVersion(4) {
|
||||
fn3(node)
|
||||
}
|
||||
return true
|
||||
}
|
||||
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) {
|
||||
fn := func(node ast.Node) bool {
|
||||
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 (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
htmltemplate "html/template"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
texttemplate "text/template"
|
||||
|
||||
"honnef.co/go/tools/deprecated"
|
||||
"honnef.co/go/tools/functions"
|
||||
"honnef.co/go/tools/gcsizes"
|
||||
"honnef.co/go/tools/internal/sharedcheck"
|
||||
"honnef.co/go/tools/lint"
|
||||
"honnef.co/go/tools/ssa"
|
||||
|
@ -111,14 +111,12 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
checkSyncPoolSizeRules = map[string]CallCheck{
|
||||
checkSyncPoolValueRules = map[string]CallCheck{
|
||||
"(*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]
|
||||
typ := arg.Value.Value.Type()
|
||||
if !types.IsInterface(typ) && sizes.Sizeof(typ) > sizes.WordSize {
|
||||
arg.Invalid("argument should be one word large or less to avoid allocations")
|
||||
if !lint.IsPointerLike(typ) {
|
||||
arg.Invalid("argument should be pointer-like to avoid allocations")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -209,6 +207,9 @@ func NewChecker() *Checker {
|
|||
return &Checker{}
|
||||
}
|
||||
|
||||
func (*Checker) Name() string { return "staticcheck" }
|
||||
func (*Checker) Prefix() string { return "SA" }
|
||||
|
||||
func (c *Checker) Funcs() map[string]lint.Func {
|
||||
return map[string]lint.Func{
|
||||
"SA1000": c.callChecker(checkRegexpRules),
|
||||
|
@ -265,6 +266,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
|||
"SA4016": c.CheckSillyBitwiseOps,
|
||||
"SA4017": c.CheckPureFunctions,
|
||||
"SA4018": c.CheckSelfAssignment,
|
||||
"SA4019": c.CheckDuplicateBuildConstraints,
|
||||
|
||||
"SA5000": c.CheckNilMaps,
|
||||
"SA5001": c.CheckEarlyDefer,
|
||||
|
@ -277,7 +279,7 @@ func (c *Checker) Funcs() map[string]lint.Func {
|
|||
|
||||
"SA6000": c.callChecker(checkRegexpMatchLoopRules),
|
||||
"SA6001": c.CheckMapBytesKey,
|
||||
"SA6002": c.callChecker(checkSyncPoolSizeRules),
|
||||
"SA6002": c.callChecker(checkSyncPoolValueRules),
|
||||
"SA6003": c.CheckRangeStringRunes,
|
||||
"SA6004": nil,
|
||||
|
||||
|
@ -301,36 +303,62 @@ func (c *Checker) filterGenerated(files []*ast.File) []*ast.File {
|
|||
return out
|
||||
}
|
||||
|
||||
func (c *Checker) Init(prog *lint.Program) {
|
||||
c.funcDescs = functions.NewDescriptions(prog.SSA)
|
||||
c.deprecatedObjs = map[types.Object]string{}
|
||||
c.nodeFns = map[ast.Node]*ssa.Function{}
|
||||
func (c *Checker) deprecateObject(m map[types.Object]string, prog *lint.Program, obj types.Object) {
|
||||
if obj.Pkg() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
f := prog.File(obj)
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
msg := c.deprecationMessage(f, prog.Prog.Fset, obj)
|
||||
if msg != "" {
|
||||
m[obj] = msg
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Checker) Init(prog *lint.Program) {
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(3)
|
||||
go func() {
|
||||
c.funcDescs = functions.NewDescriptions(prog.SSA)
|
||||
for _, fn := range prog.AllFunctions {
|
||||
if fn.Blocks != nil {
|
||||
applyStdlibKnowledge(fn)
|
||||
ssa.OptimizeBlocks(fn)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
deprecated := []map[types.Object]string{}
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, pkginfo := range prog.Prog.AllPackages {
|
||||
pkginfo := pkginfo
|
||||
scope := pkginfo.Pkg.Scope()
|
||||
names := scope.Names()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
c.deprecatedObjs = map[types.Object]string{}
|
||||
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)
|
||||
}
|
||||
|
||||
m := map[types.Object]string{}
|
||||
deprecated = append(deprecated, m)
|
||||
go func(m map[types.Object]string) {
|
||||
for _, name := range names {
|
||||
obj := scope.Lookup(name)
|
||||
msg := c.deprecationMessage(pkginfo.Files, prog.SSA.Fset, obj)
|
||||
if msg != "" {
|
||||
m[obj] = msg
|
||||
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 {
|
||||
n := typ.NumFields()
|
||||
|
@ -338,51 +366,20 @@ func (c *Checker) Init(prog *lint.Program) {
|
|||
// FIXME(dh): This code will not find deprecated
|
||||
// fields in anonymous structs.
|
||||
field := typ.Field(i)
|
||||
msg := c.deprecationMessage(pkginfo.Files, prog.SSA.Fset, field)
|
||||
if msg != "" {
|
||||
m[field] = msg
|
||||
c.deprecateObject(c.deprecatedObjs, prog, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(m)
|
||||
}
|
||||
}()
|
||||
|
||||
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 tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
||||
p := int(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())
|
||||
func (c *Checker) deprecationMessage(file *ast.File, fset *token.FileSet, obj types.Object) (message string) {
|
||||
pos := obj.Pos()
|
||||
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
|
||||
if len(path) <= 2 {
|
||||
return ""
|
||||
}
|
||||
|
@ -2065,7 +2062,7 @@ func (c *Checker) CheckCyclicFinalizer(j *lint.Job) {
|
|||
}
|
||||
for _, b := range mc.Bindings {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -2166,6 +2163,11 @@ func (c *Checker) CheckInfiniteRecursion(j *lint.Job) {
|
|||
if edge.Callee != node {
|
||||
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()
|
||||
canReturn := false
|
||||
|
@ -2437,7 +2439,7 @@ fnLoop:
|
|||
if callee == nil {
|
||||
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())
|
||||
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) {
|
||||
obj := j.Program.Info.ObjectOf(ident)
|
||||
if obj.Pkg() == nil {
|
||||
|
@ -2471,19 +2457,34 @@ func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) {
|
|||
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) {
|
||||
fn := func(node ast.Node) bool {
|
||||
sel, ok := node.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
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)
|
||||
if obj.Pkg() == nil {
|
||||
|
@ -2495,6 +2496,24 @@ func (c *Checker) CheckDeprecated(j *lint.Job) {
|
|||
return true
|
||||
}
|
||||
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)
|
||||
return true
|
||||
}
|
||||
|
@ -2784,3 +2803,39 @@ func (c *Checker) CheckSelfAssignment(j *lint.Job) {
|
|||
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
|
||||
}
|
||||
|
||||
func (*LintChecker) Name() string { return "unused" }
|
||||
func (*LintChecker) Prefix() string { return "U" }
|
||||
|
||||
func (l *LintChecker) Init(*lint.Program) {}
|
||||
func (l *LintChecker) Funcs() map[string]lint.Func {
|
||||
return map[string]lint.Func{
|
||||
|
@ -275,6 +278,51 @@ func (c *Checker) Check(lprog *loader.Program) []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) {
|
||||
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||
n := st.NumFields()
|
||||
|
@ -485,6 +533,7 @@ func (c *Checker) processTypes(pkg *loader.PackageInfo) {
|
|||
interfaces = append(interfaces, obj)
|
||||
}
|
||||
case *types.Struct:
|
||||
c.useNoCopyFields(obj)
|
||||
if pkg.Pkg.Name() != "main" && !c.WholeProgram {
|
||||
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
|
||||
},
|
||||
{
|
||||
"importpath": "honnef.co/go/tools/callgraph",
|
||||
"repository": "https://github.com/dominikh/go-tools",
|
||||
"importpath": "golang.org/x/tools/refactor/importgraph",
|
||||
"repository": "https://go.googlesource.com/tools",
|
||||
"vcs": "git",
|
||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
||||
"revision": "9c477bae194915bfd4bc8c314e90e28b9ec1c831",
|
||||
"branch": "master",
|
||||
"path": "callgraph",
|
||||
"path": "/refactor/importgraph",
|
||||
"notests": true
|
||||
},
|
||||
{
|
||||
"importpath": "honnef.co/go/tools/cmd/gosimple",
|
||||
"importpath": "honnef.co/go/tools",
|
||||
"repository": "https://github.com/dominikh/go-tools",
|
||||
"vcs": "git",
|
||||
"revision": "49f44f893d933fd08cd7d67d65ccefa5d7c23329",
|
||||
"branch": "master",
|
||||
"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",
|
||||
"revision": "50914165a1ae448f1608c6c325d052313396182e",
|
||||
"branch": "HEAD",
|
||||
"notests": true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -235,7 +235,7 @@ var defaultLinters = map[string]LinterConfig{
|
|||
Command: `gas -fmt=csv`,
|
||||
Pattern: `^(?P<path>.*?\.go),(?P<line>\d+),(?P<message>[^,]+,[^,]+,[^,]+)`,
|
||||
InstallFrom: "github.com/GoASTScanner/gas",
|
||||
PartitionStrategy: partitionPathsAsDirectories,
|
||||
PartitionStrategy: partitionPathsAsFiles,
|
||||
defaultEnabled: true,
|
||||
IsFast: true,
|
||||
},
|
||||
|
|
|
@ -26,7 +26,7 @@ var (
|
|||
)
|
||||
|
||||
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("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()
|
||||
|
@ -156,8 +156,8 @@ func formatLinters() string {
|
|||
if install == "()" {
|
||||
install = ""
|
||||
}
|
||||
fmt.Fprintf(w, " %s %s\n %s\n %s\n",
|
||||
linter.Name, install, linter.Command, linter.Pattern)
|
||||
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.IsFast, linter.defaultEnabled)
|
||||
}
|
||||
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