mirror of
https://github.com/1f349/dendrite.git
synced 2024-12-22 16:24:10 +00:00
Update gometalinter, and disable gas (#371)
* Update gometalinter * Disable gas linter According to the gas github page: > Gas is still in alpha and accepting feedback from early adopters. We do not > consider it production ready at this time. Generally it seems to shout about a lot of things which aren't very errory, like executing subprocesses with anything other than a hardcoded commandline, and creating directories with anything other than 700 perms.
This commit is contained in:
parent
16f593f786
commit
c3cb6f8767
@ -7,7 +7,6 @@
|
||||
"deadcode",
|
||||
"gocyclo",
|
||||
"ineffassign",
|
||||
"gas",
|
||||
"misspell",
|
||||
"errcheck",
|
||||
"vet",
|
||||
|
@ -10,7 +10,6 @@
|
||||
"varcheck",
|
||||
"structcheck",
|
||||
"ineffassign",
|
||||
"gas",
|
||||
"misspell",
|
||||
"unparam",
|
||||
"errcheck",
|
||||
|
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": "b8b1f84ae8cb72e7870785840eab2d6c6355aa9f",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
|
@ -110,6 +110,21 @@ Here is an example configuration file:
|
||||
}
|
||||
```
|
||||
|
||||
#### `Format` key
|
||||
|
||||
The default `Format` key places the different fields of an `Issue` into a template. this
|
||||
corresponds to the `--format` option command-line flag.
|
||||
|
||||
Default `Format`:
|
||||
```
|
||||
Format: "{{.Path}}:{{.Line}}:{{if .Col}}{{.Col}}{{end}}:{{.Severity}}: {{.Message}} ({{.Linter}})"
|
||||
```
|
||||
|
||||
#### Format Methods
|
||||
|
||||
* `{{.Path.Relative}}` - equivalent to `{{.Path}}` which outputs a relative path to the file
|
||||
* `{{.Path.Abs}}` - outputs an absolute path to the file
|
||||
|
||||
### Adding Custom linters
|
||||
|
||||
Linters can be added and customized from the config file using the `Linters` field.
|
||||
|
@ -102,6 +102,9 @@ func (v *visitor) Visit(node ast.Node) ast.Visitor {
|
||||
for _, val := range node.Values {
|
||||
ast.Walk(v, val)
|
||||
}
|
||||
if node.Type != nil {
|
||||
ast.Walk(v, node.Type)
|
||||
}
|
||||
return nil
|
||||
|
||||
case *ast.FuncDecl:
|
||||
|
@ -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
|
||||
enabled bool
|
||||
generated bool
|
||||
exitNonZero bool
|
||||
}
|
||||
gosimple struct {
|
||||
enabled bool
|
||||
generated bool
|
||||
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,51 +130,70 @@ type Func func(*Job)
|
||||
|
||||
// Problem represents a problem in some source code.
|
||||
type Problem struct {
|
||||
Position token.Pos // position in source file
|
||||
Text string // the prose that describes the problem
|
||||
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 {
|
||||
return p.Text
|
||||
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
|
||||
}
|
||||
|
||||
// A Linter lints Go source code.
|
||||
type Linter struct {
|
||||
Checker Checker
|
||||
Ignores []Ignore
|
||||
GoVersion int
|
||||
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
|
||||
|
||||
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 {
|
||||
return true
|
||||
}
|
||||
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 {
|
||||
// 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,
|
||||
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
|
||||
}
|
||||
@ -271,7 +480,8 @@ func (l *Linter) Lint(lprog *loader.Program) []Problem {
|
||||
// Pkg represents a package being linted.
|
||||
type Pkg struct {
|
||||
*ssa.Package
|
||||
Info *loader.PackageInfo
|
||||
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)
|
||||
@ -38,13 +85,14 @@ func usage(name string, flags *flag.FlagSet) func() {
|
||||
}
|
||||
|
||||
type runner struct {
|
||||
checker lint.Checker
|
||||
tags []string
|
||||
ignores []lint.Ignore
|
||||
version int
|
||||
checker lint.Checker
|
||||
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{
|
||||
Tags: strings.Fields(tags),
|
||||
LintTests: tests,
|
||||
Ignores: ignore,
|
||||
GoVersion: version,
|
||||
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: 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 {
|
||||
os.Exit(1)
|
||||
|
||||
var f OutputFormatter
|
||||
switch format {
|
||||
case "text":
|
||||
f = TextOutput{os.Stdout}
|
||||
case "json":
|
||||
f = JSONOutput{os.Stdout}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unsupported output format %q\n", format)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
for _, p := range ps {
|
||||
f.Format(p)
|
||||
}
|
||||
for i, p := range pss {
|
||||
if len(p) != 0 && confs[i].ExitNonZero {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Tags []string
|
||||
LintTests bool
|
||||
Ignores string
|
||||
GoVersion int
|
||||
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,
|
||||
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)
|
||||
fn3(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{}
|
||||
|
||||
for _, fn := range prog.AllFunctions {
|
||||
if fn.Blocks != nil {
|
||||
applyStdlibKnowledge(fn)
|
||||
ssa.OptimizeBlocks(fn)
|
||||
}
|
||||
func (c *Checker) deprecateObject(m map[types.Object]string, prog *lint.Program, obj types.Object) {
|
||||
if obj.Pkg() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||
f := prog.File(obj)
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
msg := c.deprecationMessage(f, prog.Prog.Fset, obj)
|
||||
if msg != "" {
|
||||
m[obj] = msg
|
||||
}
|
||||
}
|
||||
|
||||
deprecated := []map[types.Object]string{}
|
||||
func (c *Checker) Init(prog *lint.Program) {
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, pkginfo := range prog.Prog.AllPackages {
|
||||
pkginfo := pkginfo
|
||||
scope := pkginfo.Pkg.Scope()
|
||||
names := scope.Names()
|
||||
wg.Add(1)
|
||||
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()
|
||||
}()
|
||||
|
||||
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
|
||||
go func() {
|
||||
c.nodeFns = lint.NodeFns(prog.Packages)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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.Done()
|
||||
}()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -109,7 +109,7 @@
|
||||
"importpath": "github.com/opennota/check",
|
||||
"repository": "https://github.com/opennota/check",
|
||||
"vcs": "git",
|
||||
"revision": "11e2eec79ec4f789607e3efbf405cdca2504d4cb",
|
||||
"revision": "0ec6d92e97559edf84eebe7c51b069953d4b522c",
|
||||
"branch": "master",
|
||||
"notests": true
|
||||
},
|
||||
@ -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
|
||||
},
|
||||
{
|
||||
@ -450,4 +350,4 @@
|
||||
"notests": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ func AggregateIssueChan(issues chan *Issue) chan *Issue {
|
||||
go func() {
|
||||
for issue := range issues {
|
||||
key := issueKey{
|
||||
path: issue.Path,
|
||||
path: issue.Path.String(),
|
||||
line: issue.Line,
|
||||
col: issue.Col,
|
||||
message: issue.Message,
|
||||
|
@ -33,14 +33,13 @@ func outputToCheckstyle(issues chan *Issue) int {
|
||||
}
|
||||
status := 0
|
||||
for issue := range issues {
|
||||
if lastFile != nil && lastFile.Name != issue.Path {
|
||||
path := issue.Path.Relative()
|
||||
if lastFile != nil && lastFile.Name != path {
|
||||
out.Files = append(out.Files, lastFile)
|
||||
lastFile = nil
|
||||
}
|
||||
if lastFile == nil {
|
||||
lastFile = &checkstyleFile{
|
||||
Name: issue.Path,
|
||||
}
|
||||
lastFile = &checkstyleFile{Name: path}
|
||||
}
|
||||
|
||||
if config.Errors && issue.Severity != Error {
|
||||
|
@ -38,6 +38,7 @@ type Config struct { // nolint: maligned
|
||||
Vendor bool
|
||||
Cyclo int
|
||||
LineLength int
|
||||
MisspellLocale string
|
||||
MinConfidence float64
|
||||
MinOccurrences int
|
||||
MinConstLength int
|
||||
@ -128,6 +129,7 @@ var config = &Config{
|
||||
Concurrency: runtime.NumCPU(),
|
||||
Cyclo: 10,
|
||||
LineLength: 80,
|
||||
MisspellLocale: "",
|
||||
MinConfidence: 0.8,
|
||||
MinOccurrences: 3,
|
||||
MinConstLength: 3,
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -67,11 +68,12 @@ func newDirectiveParser() *directiveParser {
|
||||
// IsIgnored returns true if the given linter issue is ignored by a linter directive.
|
||||
func (d *directiveParser) IsIgnored(issue *Issue) bool {
|
||||
d.lock.Lock()
|
||||
ranges, ok := d.files[issue.Path]
|
||||
path := issue.Path.Relative()
|
||||
ranges, ok := d.files[path]
|
||||
if !ok {
|
||||
ranges = d.parseFile(issue.Path)
|
||||
ranges = d.parseFile(path)
|
||||
sort.Sort(ranges)
|
||||
d.files[issue.Path] = ranges
|
||||
d.files[path] = ranges
|
||||
}
|
||||
d.lock.Unlock()
|
||||
for _, r := range ranges {
|
||||
@ -204,10 +206,16 @@ func filterIssuesViaDirectives(directives *directiveParser, issues chan *Issue)
|
||||
|
||||
func warnOnUnusedDirective(directives *directiveParser) []*Issue {
|
||||
out := []*Issue{}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
warning("failed to get working directory %s", err)
|
||||
}
|
||||
|
||||
for path, ranges := range directives.Unmatched() {
|
||||
for _, ignore := range ranges {
|
||||
issue, _ := NewIssue("nolint", config.formatTemplate)
|
||||
issue.Path = path
|
||||
issue.Path = newIssuePath(cwd, path)
|
||||
issue.Line = ignore.start
|
||||
issue.Col = ignore.col
|
||||
issue.Message = "nolint directive did not match any issue"
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -82,6 +81,7 @@ func runLinters(linters map[string]*Linter, paths []string, concurrency int, exc
|
||||
"duplthreshold": fmt.Sprintf("%d", config.DuplThreshold),
|
||||
"mincyclo": fmt.Sprintf("%d", config.Cyclo),
|
||||
"maxlinelength": fmt.Sprintf("%d", config.LineLength),
|
||||
"misspelllocale": fmt.Sprintf("%s", config.MisspellLocale),
|
||||
"min_confidence": fmt.Sprintf("%f", config.MinConfidence),
|
||||
"min_occurrences": fmt.Sprintf("%d", config.MinOccurrences),
|
||||
"min_const_length": fmt.Sprintf("%d", config.MinConstLength),
|
||||
@ -237,8 +237,10 @@ func processOutput(dbg debugFunction, state *linterState, out []byte) {
|
||||
}
|
||||
switch name {
|
||||
case "path":
|
||||
issue.Path = relativePath(cwd, part)
|
||||
|
||||
issue.Path, err = newIssuePathFromAbsPath(cwd, part)
|
||||
if err != nil {
|
||||
warning("failed to make %s a relative path: %s", part, err)
|
||||
}
|
||||
case "line":
|
||||
n, err := strconv.ParseInt(part, 10, 32)
|
||||
kingpin.FatalIfError(err, "line matched invalid integer")
|
||||
@ -273,37 +275,6 @@ func processOutput(dbg debugFunction, state *linterState, out []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func relativePath(root, path string) string {
|
||||
fallback := path
|
||||
root = resolvePath(root)
|
||||
path = resolvePath(path)
|
||||
var err error
|
||||
path, err = filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
warning("failed to make %s a relative path: %s", fallback, err)
|
||||
return fallback
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func resolvePath(path string) string {
|
||||
var err error
|
||||
fallback := path
|
||||
if !filepath.IsAbs(path) {
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
warning("failed to make %s an absolute path: %s", fallback, err)
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
path, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
warning("failed to resolve symlinks in %s: %s", fallback, err)
|
||||
return fallback
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func maybeSortIssues(issues chan *Issue) chan *Issue {
|
||||
if reflect.DeepEqual([]string{"none"}, config.Sort) {
|
||||
return issues
|
||||
|
@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -21,13 +23,59 @@ const (
|
||||
Warning Severity = "warning"
|
||||
)
|
||||
|
||||
type IssuePath struct {
|
||||
root string
|
||||
path string
|
||||
}
|
||||
|
||||
func (i IssuePath) String() string {
|
||||
return i.Relative()
|
||||
}
|
||||
|
||||
func (i IssuePath) Relative() string {
|
||||
return i.path
|
||||
}
|
||||
|
||||
func (i IssuePath) Abs() string {
|
||||
return filepath.Join(i.root, i.path)
|
||||
}
|
||||
|
||||
func (i IssuePath) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.String())
|
||||
}
|
||||
|
||||
func newIssuePath(root, path string) IssuePath {
|
||||
return IssuePath{root: root, path: path}
|
||||
}
|
||||
|
||||
// newIssuePathFromAbsPath returns a new issuePath from a path that may be
|
||||
// an absolute path. root must be an absolute path.
|
||||
func newIssuePathFromAbsPath(root, path string) (IssuePath, error) {
|
||||
resolvedRoot, err := filepath.EvalSymlinks(root)
|
||||
if err != nil {
|
||||
return newIssuePath(root, path), err
|
||||
}
|
||||
|
||||
resolvedPath, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return newIssuePath(root, path), err
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
return newIssuePath(resolvedRoot, resolvedPath), nil
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(resolvedRoot, resolvedPath)
|
||||
return newIssuePath(resolvedRoot, relPath), err
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
Linter string `json:"linter"`
|
||||
Severity Severity `json:"severity"`
|
||||
Path string `json:"path"`
|
||||
Line int `json:"line"`
|
||||
Col int `json:"col"`
|
||||
Message string `json:"message"`
|
||||
Linter string `json:"linter"`
|
||||
Severity Severity `json:"severity"`
|
||||
Path IssuePath `json:"path"`
|
||||
Line int `json:"line"`
|
||||
Col int `json:"col"`
|
||||
Message string `json:"message"`
|
||||
formatTmpl *template.Template
|
||||
}
|
||||
|
||||
@ -50,7 +98,11 @@ func (i *Issue) String() string {
|
||||
if i.Col != 0 {
|
||||
col = fmt.Sprintf("%d", i.Col)
|
||||
}
|
||||
return fmt.Sprintf("%s:%d:%s:%s: %s (%s)", strings.TrimSpace(i.Path), i.Line, col, i.Severity, strings.TrimSpace(i.Message), i.Linter)
|
||||
return fmt.Sprintf("%s:%d:%s:%s: %s (%s)",
|
||||
strings.TrimSpace(i.Path.Relative()),
|
||||
i.Line, col, i.Severity,
|
||||
strings.TrimSpace(i.Message),
|
||||
i.Linter)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_ = i.formatTmpl.Execute(buf, i)
|
||||
@ -76,7 +128,7 @@ func CompareIssue(l, r Issue, order []string) bool {
|
||||
for _, key := range order {
|
||||
switch {
|
||||
case key == "path" && l.Path != r.Path:
|
||||
return l.Path < r.Path
|
||||
return l.Path.String() < r.Path.String()
|
||||
case key == "line" && l.Line != r.Line:
|
||||
return l.Line < r.Line
|
||||
case key == "column" && l.Col != r.Col:
|
||||
|
@ -10,10 +10,10 @@ import (
|
||||
|
||||
func TestSortedIssues(t *testing.T) {
|
||||
actual := []*Issue{
|
||||
{Path: "b.go", Line: 5, Col: 1},
|
||||
{Path: "a.go", Line: 3, Col: 2},
|
||||
{Path: "b.go", Line: 1, Col: 3},
|
||||
{Path: "a.go", Line: 1, Col: 4},
|
||||
{Path: newIssuePath("", "b.go"), Line: 5, Col: 1},
|
||||
{Path: newIssuePath("", "a.go"), Line: 3, Col: 2},
|
||||
{Path: newIssuePath("", "b.go"), Line: 1, Col: 3},
|
||||
{Path: newIssuePath("", "a.go"), Line: 1, Col: 4},
|
||||
}
|
||||
issues := &sortedIssues{
|
||||
issues: actual,
|
||||
@ -21,18 +21,18 @@ func TestSortedIssues(t *testing.T) {
|
||||
}
|
||||
sort.Sort(issues)
|
||||
expected := []*Issue{
|
||||
{Path: "a.go", Line: 1, Col: 4},
|
||||
{Path: "a.go", Line: 3, Col: 2},
|
||||
{Path: "b.go", Line: 1, Col: 3},
|
||||
{Path: "b.go", Line: 5, Col: 1},
|
||||
{Path: newIssuePath("", "a.go"), Line: 1, Col: 4},
|
||||
{Path: newIssuePath("", "a.go"), Line: 3, Col: 2},
|
||||
{Path: newIssuePath( "", "b.go"), Line: 1, Col: 3},
|
||||
{Path: newIssuePath( "", "b.go"), Line: 5, Col: 1},
|
||||
}
|
||||
require.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCompareOrderWithMessage(t *testing.T) {
|
||||
order := []string{"path", "line", "column", "message"}
|
||||
issueM := Issue{Path: "file.go", Message: "message"}
|
||||
issueU := Issue{Path: "file.go", Message: "unknown"}
|
||||
issueM := Issue{Path: newIssuePath("", "file.go"), Message: "message"}
|
||||
issueU := Issue{Path: newIssuePath("", "file.go"), Message: "unknown"}
|
||||
|
||||
assert.True(t, CompareIssue(issueM, issueU, order))
|
||||
assert.False(t, CompareIssue(issueU, issueM, order))
|
||||
|
@ -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,
|
||||
},
|
||||
@ -328,7 +328,7 @@ var defaultLinters = map[string]LinterConfig{
|
||||
defaultEnabled: true,
|
||||
},
|
||||
"misspell": {
|
||||
Command: `misspell -j 1`,
|
||||
Command: `misspell -j 1 --locale "{misspelllocale}"`,
|
||||
Pattern: `PATH:LINE:COL:MESSAGE`,
|
||||
InstallFrom: "github.com/client9/misspell/cmd/misspell",
|
||||
PartitionStrategy: partitionPathsAsFiles,
|
||||
|
@ -21,12 +21,13 @@ var (
|
||||
// Locations to look for vendored linters.
|
||||
vendoredSearchPaths = [][]string{
|
||||
{"github.com", "alecthomas", "gometalinter", "_linters"},
|
||||
{"gopkg.in", "alecthomas", "gometalinter.v1", "_linters"},
|
||||
{"gopkg.in", "alecthomas", "gometalinter.v2", "_linters"},
|
||||
}
|
||||
Version = "master"
|
||||
)
|
||||
|
||||
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()
|
||||
@ -35,12 +36,12 @@ func setupFlags(app *kingpin.Application) {
|
||||
app.Flag("disable-all", "Disable all linters.").Action(disableAllAction).Bool()
|
||||
app.Flag("enable-all", "Enable all linters.").Action(enableAllAction).Bool()
|
||||
app.Flag("format", "Output format.").PlaceHolder(config.Format).StringVar(&config.Format)
|
||||
app.Flag("vendored-linters", "Use vendored linters (recommended).").BoolVar(&config.VendoredLinters)
|
||||
app.Flag("vendored-linters", "Use vendored linters (recommended) (DEPRECATED - use binary packages).").BoolVar(&config.VendoredLinters)
|
||||
app.Flag("fast", "Only run fast linters.").BoolVar(&config.Fast)
|
||||
app.Flag("install", "Attempt to install all known linters.").Short('i').BoolVar(&config.Install)
|
||||
app.Flag("update", "Pass -u to go tool when installing.").Short('u').BoolVar(&config.Update)
|
||||
app.Flag("force", "Pass -f to go tool when installing.").Short('f').BoolVar(&config.Force)
|
||||
app.Flag("download-only", "Pass -d to go tool when installing.").BoolVar(&config.DownloadOnly)
|
||||
app.Flag("install", "Attempt to install all known linters (DEPRECATED - use binary packages).").Short('i').BoolVar(&config.Install)
|
||||
app.Flag("update", "Pass -u to go tool when installing (DEPRECATED - use binary packages).").Short('u').BoolVar(&config.Update)
|
||||
app.Flag("force", "Pass -f to go tool when installing (DEPRECATED - use binary packages).").Short('f').BoolVar(&config.Force)
|
||||
app.Flag("download-only", "Pass -d to go tool when installing (DEPRECATED - use binary packages).").BoolVar(&config.DownloadOnly)
|
||||
app.Flag("debug", "Display messages for failed linters, etc.").Short('d').BoolVar(&config.Debug)
|
||||
app.Flag("concurrency", "Number of concurrent linters to run.").PlaceHolder(fmt.Sprintf("%d", runtime.NumCPU())).Short('j').IntVar(&config.Concurrency)
|
||||
app.Flag("exclude", "Exclude messages matching these regular expressions.").Short('e').PlaceHolder("REGEXP").StringsVar(&config.Exclude)
|
||||
@ -49,6 +50,7 @@ func setupFlags(app *kingpin.Application) {
|
||||
app.Flag("vendor", "Enable vendoring support (skips 'vendor' directories and sets GO15VENDOREXPERIMENT=1).").BoolVar(&config.Vendor)
|
||||
app.Flag("cyclo-over", "Report functions with cyclomatic complexity over N (using gocyclo).").PlaceHolder("10").IntVar(&config.Cyclo)
|
||||
app.Flag("line-length", "Report lines longer than N (using lll).").PlaceHolder("80").IntVar(&config.LineLength)
|
||||
app.Flag("misspell-locale", "Specify locale to use (using misspell).").PlaceHolder("").StringVar(&config.MisspellLocale)
|
||||
app.Flag("min-confidence", "Minimum confidence interval to pass to golint.").PlaceHolder(".80").FloatVar(&config.MinConfidence)
|
||||
app.Flag("min-occurrences", "Minimum occurrences to pass to goconst.").PlaceHolder("3").IntVar(&config.MinOccurrences)
|
||||
app.Flag("min-const-length", "Minimum constant length.").PlaceHolder("3").IntVar(&config.MinConstLength)
|
||||
@ -156,8 +158,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()
|
||||
}
|
||||
@ -171,6 +173,7 @@ func formatSeverity() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
kingpin.Version(Version)
|
||||
pathsArg := kingpin.Arg("path", "Directories to lint. Defaults to \".\". <path>/... will recurse.").Strings()
|
||||
app := kingpin.CommandLine
|
||||
setupFlags(app)
|
||||
|
104
vendor/src/github.com/alecthomas/gometalinter/package.sh
vendored
Normal file
104
vendor/src/github.com/alecthomas/gometalinter/package.sh
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Only build packages for tagged releases
|
||||
TAG="$(git tag -l --points-at HEAD)"
|
||||
|
||||
export CGO_ENABLED=0
|
||||
|
||||
GO_VERSION="$(go version | awk '{print $3}' | cut -d. -f1-2)"
|
||||
|
||||
if [ "$GO_VERSION" != "go1.9" ]; then
|
||||
echo "$0: not packaging; not on Go 1.9"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if echo "$TAG" | grep -q '^v[0-9]\.[0-9]\.[0-9]\(-.*\)?$' && false; then
|
||||
echo "$0: not packaging; no tag or tag not in semver format"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
LINTERS="
|
||||
github.com/alecthomas/gocyclo
|
||||
github.com/alexkohler/nakedret
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
github.com/dnephin/govet
|
||||
github.com/GoASTScanner/gas
|
||||
github.com/golang/lint/golint
|
||||
github.com/gordonklaus/ineffassign
|
||||
github.com/jgautheron/goconst/cmd/goconst
|
||||
github.com/kisielk/errcheck
|
||||
github.com/mdempsky/maligned
|
||||
github.com/mdempsky/unconvert
|
||||
github.com/mibk/dupl
|
||||
github.com/opennota/check/cmd/structcheck
|
||||
github.com/opennota/check/cmd/varcheck
|
||||
github.com/stripe/safesql
|
||||
github.com/tsenart/deadcode
|
||||
github.com/walle/lll/cmd/lll
|
||||
golang.org/x/tools/cmd/goimports
|
||||
golang.org/x/tools/cmd/gotype
|
||||
honnef.co/go/tools/cmd/gosimple
|
||||
honnef.co/go/tools/cmd/megacheck
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
honnef.co/go/tools/cmd/unused
|
||||
mvdan.cc/interfacer
|
||||
mvdan.cc/unparam
|
||||
"
|
||||
|
||||
eval "$(go env | FS='' awk '{printf "REAL_%s\n", $0}')"
|
||||
|
||||
function install_go_binary() {
|
||||
local SRC
|
||||
if [ "$GOOS" = "$REAL_GOOS" -a "$GOARCH" = "$REAL_GOARCH" ]; then
|
||||
SRC="${GOPATH}/bin"
|
||||
else
|
||||
SRC="${GOPATH}/bin/${GOOS}_${GOARCH}"
|
||||
fi
|
||||
install -m 755 "${SRC}/${1}${SUFFIX}" "${2}"
|
||||
}
|
||||
|
||||
function packager() {
|
||||
if [ "$GOOS" = "windows" ]; then
|
||||
zip -9 -r -o "${1}".zip "${1}"
|
||||
else
|
||||
tar cvfj "${1}".tar.bz2 "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
rm -rf "${PWD}/dist"
|
||||
|
||||
for GOOS in linux darwin windows; do
|
||||
SUFFIX=""
|
||||
if [ "$GOOS" = "windows" ]; then
|
||||
SUFFIX=".exe"
|
||||
fi
|
||||
|
||||
for GOARCH in 386 amd64; do
|
||||
export GOPATH="${REAL_GOPATH}"
|
||||
DEST="${PWD}/dist/gometalinter-${TAG}-${GOOS}-${GOARCH}"
|
||||
install -d -m 755 "${DEST}/linters"
|
||||
install -m 644 COPYING "${DEST}"
|
||||
cat << EOF > "${DEST}/README.txt"
|
||||
gometalinter is a tool to normalise the output of Go linters.
|
||||
See https://github.com/alecthomas/gometalinter for more information.
|
||||
|
||||
This is a binary distribution of gometalinter ${TAG}.
|
||||
|
||||
All binaries must be installed in the PATH for gometalinter to operate correctly.
|
||||
EOF
|
||||
echo "${DEST}"
|
||||
export GOOS GOARCH
|
||||
|
||||
go build -i .
|
||||
go build -o "${DEST}/gometalinter${SUFFIX}" -ldflags="-X main.Version=${TAG}" .
|
||||
|
||||
export GOPATH="$PWD/_linters"
|
||||
|
||||
go install -v ${LINTERS}
|
||||
for LINTER in ${LINTERS}; do
|
||||
install_go_binary $(basename ${LINTER}) "${DEST}/linters"
|
||||
done
|
||||
|
||||
(cd "${DEST}/.." && packager "$(basename ${DEST})")
|
||||
done
|
||||
done
|
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)
|
||||
}
|
@ -4,13 +4,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
errCommandNotSpecified = TError("command not specified")
|
||||
envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z_]+`)
|
||||
)
|
||||
|
||||
// An Application contains the definitions of flags, arguments and commands
|
||||
@ -28,7 +26,9 @@ type Application struct {
|
||||
errors io.Writer
|
||||
terminate func(status int) // See Terminate()
|
||||
noInterspersed bool // can flags be interspersed with args (or must they come first)
|
||||
envarSeparator string
|
||||
defaultEnvars bool
|
||||
resolvers []Resolver
|
||||
completion bool
|
||||
helpFlag *Clause
|
||||
helpCommand *CmdClause
|
||||
@ -38,11 +38,12 @@ type Application struct {
|
||||
// New creates a new Kingpin application instance.
|
||||
func New(name, help string) *Application {
|
||||
a := &Application{
|
||||
Name: name,
|
||||
Help: help,
|
||||
output: os.Stdout,
|
||||
errors: os.Stderr,
|
||||
terminate: os.Exit,
|
||||
Name: name,
|
||||
Help: help,
|
||||
output: os.Stdout,
|
||||
errors: os.Stderr,
|
||||
terminate: os.Exit,
|
||||
envarSeparator: string(os.PathListSeparator),
|
||||
defaultUsage: &UsageContext{
|
||||
Template: DefaultUsageTemplate,
|
||||
},
|
||||
@ -128,6 +129,26 @@ func (a *Application) DefaultEnvars() *Application {
|
||||
return a
|
||||
}
|
||||
|
||||
// EnvarSeparator sets the string that is used for separating values in environment variables.
|
||||
//
|
||||
// This defaults to the current OS's path list separator (typically : or ;).
|
||||
func (a *Application) EnvarSeparator(sep string) *Application {
|
||||
a.envarSeparator = sep
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
// Resolver adds an ordered set of flag/argument resolvers.
|
||||
//
|
||||
// Resolvers provide default flag/argument values, from environment variables, configuration files, etc. Multiple
|
||||
// resolvers may be added, and they are processed in order.
|
||||
//
|
||||
// The last Resolver to return a value always wins. Values returned from resolvers are not cumulative.
|
||||
func (a *Application) Resolver(resolvers ...Resolver) *Application {
|
||||
a.resolvers = append(a.resolvers, resolvers...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Terminate specifies the termination handler. Defaults to os.Exit(status).
|
||||
// If nil is passed, a no-op function will be used.
|
||||
func (a *Application) Terminate(terminate func(int)) *Application {
|
||||
@ -138,7 +159,7 @@ func (a *Application) Terminate(terminate func(int)) *Application {
|
||||
return a
|
||||
}
|
||||
|
||||
// Writer specifies the writer to use for usage and errors. Defaults to os.Stderr.
|
||||
// Writers specifies the writers to use for usage and errors. Defaults to os.Stderr.
|
||||
func (a *Application) Writers(out, err io.Writer) *Application {
|
||||
a.output = out
|
||||
a.errors = err
|
||||
@ -169,11 +190,27 @@ func (a *Application) parseContext(ignoreDefault bool, args []string) (*ParseCon
|
||||
if err := a.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
context := tokenize(args, ignoreDefault)
|
||||
context := tokenize(args, ignoreDefault, a.buildResolvers())
|
||||
err := parse(context, a)
|
||||
return context, err
|
||||
}
|
||||
|
||||
// Build resolvers to emulate the envar and defaults behaviour that was previously hard-coded.
|
||||
func (a *Application) buildResolvers() []Resolver {
|
||||
|
||||
// .Default() has lowest priority...
|
||||
resolvers := []Resolver{defaultsResolver()}
|
||||
// Then custom resolvers...
|
||||
resolvers = append(resolvers, a.resolvers...)
|
||||
// Finally, envars are highest priority behind direct flag parsing.
|
||||
if a.defaultEnvars {
|
||||
resolvers = append(resolvers, PrefixedEnvarResolver(a.Name+"_", a.envarSeparator))
|
||||
}
|
||||
resolvers = append(resolvers, envarResolver(a.envarSeparator))
|
||||
|
||||
return resolvers
|
||||
}
|
||||
|
||||
// Parse parses command-line arguments. It returns the selected command and an
|
||||
// error. The selected command will be a space separated subcommand, if
|
||||
// subcommands have been configured.
|
||||
@ -275,13 +312,6 @@ func (a *Application) Interspersed(interspersed bool) *Application {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Application) defaultEnvarPrefix() string {
|
||||
if a.defaultEnvars {
|
||||
return a.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *Application) init() error {
|
||||
if a.initialized {
|
||||
return nil
|
||||
@ -308,7 +338,7 @@ func (a *Application) init() error {
|
||||
a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...)
|
||||
}
|
||||
|
||||
if err := a.flagGroup.init(a.defaultEnvarPrefix()); err != nil {
|
||||
if err := a.flagGroup.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.cmdGroup.init(); err != nil {
|
||||
@ -382,7 +412,7 @@ func (a *Application) setDefaults(context *ParseContext) error {
|
||||
// Check required flags and set defaults.
|
||||
for _, flag := range context.flags.long {
|
||||
if flagElements[flag.name] == nil {
|
||||
if err := flag.setDefault(); err != nil {
|
||||
if err := flag.setDefault(context); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -392,7 +422,7 @@ func (a *Application) setDefaults(context *ParseContext) error {
|
||||
|
||||
for _, arg := range context.arguments.args {
|
||||
if argElements[arg.name] == nil {
|
||||
if err := arg.setDefault(); err != nil {
|
||||
if err := arg.setDefault(context); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -411,7 +441,7 @@ func (a *Application) validateRequired(context *ParseContext) error {
|
||||
for _, flag := range context.flags.long {
|
||||
if flagElements[flag.name] == nil {
|
||||
// Check required flags were provided.
|
||||
if flag.needsValue() {
|
||||
if flag.needsValue(context) {
|
||||
return TError("required flag --{{.Arg0}} not provided", V{"Arg0": flag.name})
|
||||
}
|
||||
}
|
||||
@ -419,7 +449,7 @@ func (a *Application) validateRequired(context *ParseContext) error {
|
||||
|
||||
for _, arg := range context.arguments.args {
|
||||
if argElements[arg.name] == nil {
|
||||
if arg.needsValue() {
|
||||
if arg.needsValue(context) {
|
||||
return TError("required argument '{{.Arg0}}' not provided", V{"Arg0": arg.name})
|
||||
}
|
||||
}
|
||||
@ -465,7 +495,10 @@ func (a *Application) setValues(context *ParseContext) (selected []string, err e
|
||||
}
|
||||
}
|
||||
|
||||
if lastCmd != nil && len(lastCmd.commands) > 0 {
|
||||
if lastCmd == nil || lastCmd.optionalSubcommands {
|
||||
return
|
||||
}
|
||||
if len(lastCmd.commands) > 0 {
|
||||
return nil, TError("must select a subcommand of '{{.Arg0}}'", V{"Arg0": lastCmd.FullCommand()})
|
||||
}
|
||||
|
||||
@ -642,7 +675,3 @@ func (a *Application) applyActions(context *ParseContext) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func envarTransform(name string) string {
|
||||
return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_"))
|
||||
}
|
||||
|
@ -2,39 +2,25 @@ package kingpin
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/alecthomas/units"
|
||||
)
|
||||
|
||||
var (
|
||||
envVarValuesSeparator = "\r?\n"
|
||||
envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$")
|
||||
envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator)
|
||||
)
|
||||
|
||||
type Settings interface {
|
||||
SetValue(value Value)
|
||||
}
|
||||
|
||||
// A Clause represents a flag or an argument passed by the user.
|
||||
type Clause struct {
|
||||
actionMixin
|
||||
completionsMixin
|
||||
|
||||
name string
|
||||
shorthand rune
|
||||
help string
|
||||
placeholder string
|
||||
hidden bool
|
||||
defaultValues []string
|
||||
value Value
|
||||
required bool
|
||||
envar string
|
||||
noEnvar bool
|
||||
hintActions []HintAction
|
||||
builtinHintActions []HintAction
|
||||
name string
|
||||
shorthand rune
|
||||
help string
|
||||
placeholder string
|
||||
hidden bool
|
||||
defaultValues []string
|
||||
value Value
|
||||
required bool
|
||||
envar string
|
||||
noEnvar bool
|
||||
}
|
||||
|
||||
func NewClause(name, help string) *Clause {
|
||||
@ -64,6 +50,11 @@ func (c *Clause) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Clause) Help(help string) *Clause {
|
||||
c.help = help
|
||||
return c
|
||||
}
|
||||
|
||||
// UsageAction adds a PreAction() that will display the given UsageContext.
|
||||
func (c *Clause) UsageAction(context *UsageContext) *Clause {
|
||||
c.PreAction(func(a *Application, e *ParseElement, c *ParseContext) error {
|
||||
@ -95,13 +86,21 @@ func (c *Clause) HintAction(action HintAction) *Clause {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Clause) addHintAction(action HintAction) {
|
||||
c.hintActions = append(c.hintActions, action)
|
||||
// Envar overrides the default value(s) for a flag from an environment variable,
|
||||
// if it is set. Several default values can be provided by using new lines to
|
||||
// separate them.
|
||||
func (c *Clause) Envar(name string) *Clause {
|
||||
c.envar = name
|
||||
c.noEnvar = false
|
||||
return c
|
||||
}
|
||||
|
||||
// Allow adding of HintActions which are added internally, ie, EnumVar
|
||||
func (c *Clause) addHintActionBuiltin(action HintAction) {
|
||||
c.builtinHintActions = append(c.builtinHintActions, action)
|
||||
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
||||
// Most useful in conjunction with PrefixedEnvarResolver.
|
||||
func (c *Clause) NoEnvar() *Clause {
|
||||
c.envar = ""
|
||||
c.noEnvar = true
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Clause) resolveCompletions() []string {
|
||||
@ -133,23 +132,6 @@ func (c *Clause) Default(values ...string) *Clause {
|
||||
return c
|
||||
}
|
||||
|
||||
// Envar overrides the default value(s) for a flag from an environment variable,
|
||||
// if it is set. Several default values can be provided by using new lines to
|
||||
// separate them.
|
||||
func (c *Clause) Envar(name string) *Clause {
|
||||
c.envar = name
|
||||
c.noEnvar = false
|
||||
return c
|
||||
}
|
||||
|
||||
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
||||
// Most useful in conjunction with app.DefaultEnvars().
|
||||
func (c *Clause) NoEnvar() *Clause {
|
||||
c.envar = ""
|
||||
c.noEnvar = true
|
||||
return c
|
||||
}
|
||||
|
||||
// PlaceHolder sets the place-holder string used for flag values in the help. The
|
||||
// default behaviour is to use the value provided by Default() if provided,
|
||||
// then fall back on the capitalized flag name.
|
||||
@ -176,9 +158,21 @@ func (c *Clause) Short(name rune) *Clause {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Clause) needsValue() bool {
|
||||
haveDefault := len(c.defaultValues) > 0
|
||||
return c.required && !(haveDefault || c.HasEnvarValue())
|
||||
func (c *Clause) needsValue(context *ParseContext) bool {
|
||||
return c.required && !c.canResolve(context)
|
||||
}
|
||||
|
||||
func (c *Clause) canResolve(context *ParseContext) bool {
|
||||
for _, resolver := range context.resolvers {
|
||||
rvalues, err := resolver.Resolve(c.name, context)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if rvalues != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Clause) reset() {
|
||||
@ -187,60 +181,32 @@ func (c *Clause) reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Clause) setDefault() error {
|
||||
if c.HasEnvarValue() {
|
||||
c.reset()
|
||||
if v, ok := c.value.(cumulativeValue); !ok || !v.IsCumulative() {
|
||||
// Use the value as-is
|
||||
return c.value.Set(c.GetEnvarValue())
|
||||
func (c *Clause) setDefault(context *ParseContext) error {
|
||||
var values []string
|
||||
for _, resolver := range context.resolvers {
|
||||
rvalues, err := resolver.Resolve(c.name, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, value := range c.GetSplitEnvarValue() {
|
||||
if rvalues != nil {
|
||||
values = rvalues
|
||||
}
|
||||
}
|
||||
|
||||
if values != nil {
|
||||
c.reset()
|
||||
for _, value := range values {
|
||||
if err := c.value.Set(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else if len(c.defaultValues) > 0 {
|
||||
c.reset()
|
||||
for _, defaultValue := range c.defaultValues {
|
||||
if err := c.value.Set(defaultValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Clause) HasEnvarValue() bool {
|
||||
return c.GetEnvarValue() != ""
|
||||
}
|
||||
|
||||
func (c *Clause) GetEnvarValue() string {
|
||||
if c.noEnvar || c.envar == "" {
|
||||
return ""
|
||||
}
|
||||
return os.Getenv(c.envar)
|
||||
}
|
||||
|
||||
func (c *Clause) GetSplitEnvarValue() []string {
|
||||
values := make([]string, 0)
|
||||
|
||||
envarValue := c.GetEnvarValue()
|
||||
if envarValue == "" {
|
||||
return values
|
||||
}
|
||||
|
||||
// Split by new line to extract multiple values, if any.
|
||||
trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "")
|
||||
values = append(values, envVarValuesSplitter.Split(trimmed, -1)...)
|
||||
return values
|
||||
}
|
||||
|
||||
func (c *Clause) SetValue(value Value) {
|
||||
c.value = value
|
||||
c.setDefault()
|
||||
}
|
||||
|
||||
// StringMap provides key=value parsing into a map.
|
||||
|
@ -130,16 +130,6 @@ func newCmdGroup(app *Application) *cmdGroup {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
|
||||
for _, cmd := range c.commandOrder {
|
||||
if len(cmd.commands) == 0 {
|
||||
out = append(out, cmd)
|
||||
}
|
||||
out = append(out, cmd.flattenedCommands()...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cmdGroup) addCommand(name, help string) *CmdClause {
|
||||
cmd := newCommand(c.app, name, help)
|
||||
c.commands[name] = cmd
|
||||
@ -187,14 +177,15 @@ type CmdClauseValidator func(*CmdClause) error
|
||||
// and either subcommands or positional arguments.
|
||||
type CmdClause struct {
|
||||
cmdMixin
|
||||
app *Application
|
||||
name string
|
||||
aliases []string
|
||||
help string
|
||||
isDefault bool
|
||||
validator CmdClauseValidator
|
||||
hidden bool
|
||||
completionAlts []string
|
||||
app *Application
|
||||
name string
|
||||
aliases []string
|
||||
help string
|
||||
isDefault bool
|
||||
validator CmdClauseValidator
|
||||
hidden bool
|
||||
completionAlts []string
|
||||
optionalSubcommands bool
|
||||
}
|
||||
|
||||
func newCommand(app *Application, name, help string) *CmdClause {
|
||||
@ -271,6 +262,12 @@ func (c *CmdClause) Command(name, help string) *CmdClause {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// OptionalSubcommands makes subcommands optional
|
||||
func (c *CmdClause) OptionalSubcommands() *CmdClause {
|
||||
c.optionalSubcommands = true
|
||||
return c
|
||||
}
|
||||
|
||||
// Default makes this command the default if commands don't match.
|
||||
func (c *CmdClause) Default() *CmdClause {
|
||||
c.isDefault = true
|
||||
@ -302,7 +299,7 @@ func (c *cmdMixin) checkArgCommandMixing() error {
|
||||
}
|
||||
|
||||
func (c *CmdClause) init() error {
|
||||
if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil {
|
||||
if err := c.flagGroup.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.checkArgCommandMixing(); err != nil {
|
||||
|
@ -105,7 +105,7 @@ func main() {
|
||||
if v.Format != "" {
|
||||
return v.Format
|
||||
}
|
||||
return "fmt.Sprintf(\"%v\", *f)"
|
||||
return "fmt.Sprintf(\"%v\", *f.v)"
|
||||
},
|
||||
"ValueName": func(v *Value) string {
|
||||
name := valueName(v)
|
||||
|
@ -31,14 +31,11 @@ func (f *flagGroup) Flag(name, help string) *Clause {
|
||||
return flag
|
||||
}
|
||||
|
||||
func (f *flagGroup) init(defaultEnvarPrefix string) error {
|
||||
func (f *flagGroup) init() error {
|
||||
if err := f.checkDuplicates(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, flag := range f.long {
|
||||
if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" {
|
||||
flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name)
|
||||
}
|
||||
if err := flag.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -13,6 +13,15 @@ type FlagGroupModel struct {
|
||||
Flags []*ClauseModel
|
||||
}
|
||||
|
||||
func (f *FlagGroupModel) FlagByName(name string) *ClauseModel {
|
||||
for _, flag := range f.Flags {
|
||||
if flag.Name == name {
|
||||
return flag
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlagGroupModel) FlagSummary() string {
|
||||
out := []string{}
|
||||
count := 0
|
||||
@ -39,7 +48,6 @@ type ClauseModel struct {
|
||||
Help string
|
||||
Short rune
|
||||
Default []string
|
||||
Envar string
|
||||
PlaceHolder string
|
||||
Required bool
|
||||
Hidden bool
|
||||
@ -106,6 +114,9 @@ type CmdGroupModel struct {
|
||||
|
||||
func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
|
||||
for _, cmd := range c.Commands {
|
||||
if cmd.OptionalSubcommands {
|
||||
out = append(out, cmd)
|
||||
}
|
||||
if len(cmd.Commands) == 0 {
|
||||
out = append(out, cmd)
|
||||
}
|
||||
@ -115,13 +126,14 @@ func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
|
||||
}
|
||||
|
||||
type CmdModel struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Help string
|
||||
Depth int
|
||||
Hidden bool
|
||||
Default bool
|
||||
Parent *CmdModel
|
||||
Name string
|
||||
Aliases []string
|
||||
Help string
|
||||
Depth int
|
||||
Hidden bool
|
||||
Default bool
|
||||
OptionalSubcommands bool
|
||||
Parent *CmdModel
|
||||
*FlagGroupModel
|
||||
*ArgGroupModel
|
||||
*CmdGroupModel
|
||||
@ -242,7 +254,6 @@ func (f *Clause) Model() *ClauseModel {
|
||||
Help: f.help,
|
||||
Short: f.shorthand,
|
||||
Default: f.defaultValues,
|
||||
Envar: f.envar,
|
||||
PlaceHolder: f.placeholder,
|
||||
Required: f.required,
|
||||
Hidden: f.hidden,
|
||||
@ -265,15 +276,16 @@ func (c *CmdClause) Model(parent *CmdModel) *CmdModel {
|
||||
depth++
|
||||
}
|
||||
cmd := &CmdModel{
|
||||
Name: c.name,
|
||||
Parent: parent,
|
||||
Aliases: c.aliases,
|
||||
Help: c.help,
|
||||
Depth: depth,
|
||||
Hidden: c.hidden,
|
||||
Default: c.isDefault,
|
||||
FlagGroupModel: c.flagGroup.Model(),
|
||||
ArgGroupModel: c.argGroup.Model(),
|
||||
Name: c.name,
|
||||
Parent: parent,
|
||||
Aliases: c.aliases,
|
||||
Help: c.help,
|
||||
Depth: depth,
|
||||
Hidden: c.hidden,
|
||||
Default: c.isDefault,
|
||||
OptionalSubcommands: c.optionalSubcommands,
|
||||
FlagGroupModel: c.flagGroup.Model(),
|
||||
ArgGroupModel: c.argGroup.Model(),
|
||||
}
|
||||
cmd.CmdGroupModel = c.cmdGroup.Model(cmd)
|
||||
return cmd
|
||||
|
@ -119,6 +119,7 @@ func (p ParseElements) ArgMap() map[string]*ParseElement {
|
||||
// any).
|
||||
type ParseContext struct {
|
||||
SelectedCommand *CmdClause
|
||||
resolvers []Resolver
|
||||
ignoreDefault bool
|
||||
argsOnly bool
|
||||
peek []*Token
|
||||
@ -132,6 +133,34 @@ type ParseContext struct {
|
||||
Elements ParseElements
|
||||
}
|
||||
|
||||
func (p *ParseContext) CombinedFlagsAndArgs() []*Clause {
|
||||
return append(p.Args(), p.Flags()...)
|
||||
}
|
||||
|
||||
func (p *ParseContext) Args() []*Clause {
|
||||
return p.arguments.args
|
||||
}
|
||||
|
||||
func (p *ParseContext) Flags() []*Clause {
|
||||
return p.flags.flagOrder
|
||||
}
|
||||
|
||||
// LastCmd returns true if the element is the last (sub)command being evaluated.
|
||||
func (p *ParseContext) LastCmd(element *ParseElement) bool {
|
||||
lastCmdIndex := -1
|
||||
eIndex := -2
|
||||
for i, e := range p.Elements {
|
||||
if element == e {
|
||||
eIndex = i
|
||||
}
|
||||
|
||||
if e.OneOf.Cmd != nil {
|
||||
lastCmdIndex = i
|
||||
}
|
||||
}
|
||||
return lastCmdIndex == eIndex
|
||||
}
|
||||
|
||||
func (p *ParseContext) nextArg() *Clause {
|
||||
if p.argumenti >= len(p.arguments.args) {
|
||||
return nil
|
||||
@ -154,13 +183,14 @@ func (p *ParseContext) HasTrailingArgs() bool {
|
||||
return len(p.args) > 0
|
||||
}
|
||||
|
||||
func tokenize(args []string, ignoreDefault bool) *ParseContext {
|
||||
func tokenize(args []string, ignoreDefault bool, resolvers []Resolver) *ParseContext {
|
||||
return &ParseContext{
|
||||
ignoreDefault: ignoreDefault,
|
||||
args: args,
|
||||
rawArgs: args,
|
||||
flags: newFlagGroup(),
|
||||
arguments: newArgGroup(),
|
||||
resolvers: resolvers,
|
||||
}
|
||||
}
|
||||
|
||||
|
157
vendor/src/github.com/alecthomas/gometalinter/vendor/gopkg.in/alecthomas/kingpin.v3-unstable/resolver.go
generated
vendored
Normal file
157
vendor/src/github.com/alecthomas/gometalinter/vendor/gopkg.in/alecthomas/kingpin.v3-unstable/resolver.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
package kingpin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z_]+`)
|
||||
)
|
||||
|
||||
// A Resolver retrieves flag values from an external source, such as a configuration file or environment variables.
|
||||
type Resolver interface {
|
||||
// Resolve key in the given parse context.
|
||||
//
|
||||
// A nil slice should be returned if the key can not be resolved.
|
||||
Resolve(key string, context *ParseContext) ([]string, error)
|
||||
}
|
||||
|
||||
// ResolverFunc is a function that is also a Resolver.
|
||||
type ResolverFunc func(key string, context *ParseContext) ([]string, error)
|
||||
|
||||
func (r ResolverFunc) Resolve(key string, context *ParseContext) ([]string, error) {
|
||||
return r(key, context)
|
||||
}
|
||||
|
||||
// A resolver that pulls values from the flag defaults. This resolver is always installed in the ParseContext.
|
||||
func defaultsResolver() Resolver {
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
for _, clause := range context.CombinedFlagsAndArgs() {
|
||||
if clause.name == key {
|
||||
return clause.defaultValues, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
|
||||
func parseEnvar(envar, sep string) []string {
|
||||
value, ok := os.LookupEnv(envar)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if sep == "" {
|
||||
return []string{value}
|
||||
}
|
||||
return strings.Split(value, sep)
|
||||
}
|
||||
|
||||
// Resolves a clause value from the envar configured on that clause, if any.
|
||||
func envarResolver(sep string) Resolver {
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
for _, clause := range context.CombinedFlagsAndArgs() {
|
||||
if key == clause.name {
|
||||
if clause.noEnvar || clause.envar == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return parseEnvar(clause.envar, sep), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
|
||||
// MapResolver resolves values from a static map.
|
||||
func MapResolver(values map[string][]string) Resolver {
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
return values[key], nil
|
||||
})
|
||||
}
|
||||
|
||||
// JSONResolver returns a Resolver that retrieves values from a JSON source.
|
||||
func JSONResolver(data []byte) (Resolver, error) {
|
||||
values := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapping := map[string][]string{}
|
||||
for key, value := range values {
|
||||
sub, err := jsonDecodeValue(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapping[key] = sub
|
||||
}
|
||||
return MapResolver(mapping), nil
|
||||
}
|
||||
|
||||
func jsonDecodeValue(value interface{}) ([]string, error) {
|
||||
switch v := value.(type) {
|
||||
case []interface{}:
|
||||
out := []string{}
|
||||
for _, sv := range v {
|
||||
next, err := jsonDecodeValue(sv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, next...)
|
||||
}
|
||||
return out, nil
|
||||
case string:
|
||||
return []string{v}, nil
|
||||
case float64:
|
||||
return []string{fmt.Sprintf("%v", v)}, nil
|
||||
case bool:
|
||||
if v {
|
||||
return []string{"true"}, nil
|
||||
}
|
||||
return []string{"false"}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported JSON value %v (of type %T)", value, value)
|
||||
}
|
||||
|
||||
// RenamingResolver creates a resolver for remapping names for a child resolver.
|
||||
//
|
||||
// This is useful if your configuration file uses a naming convention that does not map directly to
|
||||
// flag names.
|
||||
func RenamingResolver(resolver Resolver, rename func(string) string) Resolver {
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
return resolver.Resolve(rename(key), context)
|
||||
})
|
||||
}
|
||||
|
||||
// PrefixedEnvarResolver resolves any flag/argument via environment variables.
|
||||
//
|
||||
// "prefix" is the common-prefix for the environment variables. "separator", is the character used to separate
|
||||
// multiple values within a single envar (eg. ";")
|
||||
//
|
||||
// With a prefix of APP_, flags in the form --some-flag will be transformed to APP_SOME_FLAG.
|
||||
func PrefixedEnvarResolver(prefix, separator string) Resolver {
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
key = envarTransform(prefix + key)
|
||||
return parseEnvar(key, separator), nil
|
||||
})
|
||||
}
|
||||
|
||||
// DontResolve returns a Resolver that will never return values for the given keys, even if provided.
|
||||
func DontResolve(resolver Resolver, keys ...string) Resolver {
|
||||
disabled := map[string]bool{}
|
||||
for _, key := range keys {
|
||||
disabled[key] = true
|
||||
}
|
||||
return ResolverFunc(func(key string, context *ParseContext) ([]string, error) {
|
||||
if disabled[key] {
|
||||
return nil, nil
|
||||
}
|
||||
return resolver.Resolve(key, context)
|
||||
})
|
||||
}
|
||||
|
||||
func envarTransform(name string) string {
|
||||
return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_"))
|
||||
}
|
@ -174,7 +174,7 @@ func (c *cmdMixin) fromStruct(clause *CmdClause, v interface{}) error { // nolin
|
||||
case reflect.Uint:
|
||||
clause.UintsVar(ptr.(*[]uint))
|
||||
case reflect.Uint8:
|
||||
clause.Uint8ListVar(ptr.(*[]uint8))
|
||||
clause.HexBytesVar(ptr.(*[]byte))
|
||||
case reflect.Uint16:
|
||||
clause.Uint16ListVar(ptr.(*[]uint16))
|
||||
case reflect.Uint32:
|
||||
|
@ -27,7 +27,7 @@ func (f *boolValue) Set(s string) error {
|
||||
|
||||
func (f *boolValue) Get() interface{} { return (bool)(*f.v) }
|
||||
|
||||
func (f *boolValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *boolValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Bool parses the next command-line value as bool.
|
||||
func (p *Clause) Bool() (target *bool) {
|
||||
@ -113,7 +113,7 @@ func (f *uintValue) Set(s string) error {
|
||||
|
||||
func (f *uintValue) Get() interface{} { return (uint)(*f.v) }
|
||||
|
||||
func (f *uintValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *uintValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Uint parses the next command-line value as uint.
|
||||
func (p *Clause) Uint() (target *uint) {
|
||||
@ -156,7 +156,7 @@ func (f *uint8Value) Set(s string) error {
|
||||
|
||||
func (f *uint8Value) Get() interface{} { return (uint8)(*f.v) }
|
||||
|
||||
func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Uint8 parses the next command-line value as uint8.
|
||||
func (p *Clause) Uint8() (target *uint8) {
|
||||
@ -199,7 +199,7 @@ func (f *uint16Value) Set(s string) error {
|
||||
|
||||
func (f *uint16Value) Get() interface{} { return (uint16)(*f.v) }
|
||||
|
||||
func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Uint16 parses the next command-line value as uint16.
|
||||
func (p *Clause) Uint16() (target *uint16) {
|
||||
@ -242,7 +242,7 @@ func (f *uint32Value) Set(s string) error {
|
||||
|
||||
func (f *uint32Value) Get() interface{} { return (uint32)(*f.v) }
|
||||
|
||||
func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Uint32 parses the next command-line value as uint32.
|
||||
func (p *Clause) Uint32() (target *uint32) {
|
||||
@ -285,7 +285,7 @@ func (f *uint64Value) Set(s string) error {
|
||||
|
||||
func (f *uint64Value) Get() interface{} { return (uint64)(*f.v) }
|
||||
|
||||
func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Uint64 parses the next command-line value as uint64.
|
||||
func (p *Clause) Uint64() (target *uint64) {
|
||||
@ -328,7 +328,7 @@ func (f *intValue) Set(s string) error {
|
||||
|
||||
func (f *intValue) Get() interface{} { return (int)(*f.v) }
|
||||
|
||||
func (f *intValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *intValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Int parses the next command-line value as int.
|
||||
func (p *Clause) Int() (target *int) {
|
||||
@ -371,7 +371,7 @@ func (f *int8Value) Set(s string) error {
|
||||
|
||||
func (f *int8Value) Get() interface{} { return (int8)(*f.v) }
|
||||
|
||||
func (f *int8Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *int8Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Int8 parses the next command-line value as int8.
|
||||
func (p *Clause) Int8() (target *int8) {
|
||||
@ -414,7 +414,7 @@ func (f *int16Value) Set(s string) error {
|
||||
|
||||
func (f *int16Value) Get() interface{} { return (int16)(*f.v) }
|
||||
|
||||
func (f *int16Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *int16Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Int16 parses the next command-line value as int16.
|
||||
func (p *Clause) Int16() (target *int16) {
|
||||
@ -457,7 +457,7 @@ func (f *int32Value) Set(s string) error {
|
||||
|
||||
func (f *int32Value) Get() interface{} { return (int32)(*f.v) }
|
||||
|
||||
func (f *int32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *int32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Int32 parses the next command-line value as int32.
|
||||
func (p *Clause) Int32() (target *int32) {
|
||||
@ -500,7 +500,7 @@ func (f *int64Value) Set(s string) error {
|
||||
|
||||
func (f *int64Value) Get() interface{} { return (int64)(*f.v) }
|
||||
|
||||
func (f *int64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *int64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Int64 parses the next command-line value as int64.
|
||||
func (p *Clause) Int64() (target *int64) {
|
||||
@ -543,7 +543,7 @@ func (f *float64Value) Set(s string) error {
|
||||
|
||||
func (f *float64Value) Get() interface{} { return (float64)(*f.v) }
|
||||
|
||||
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Float64 parses the next command-line value as float64.
|
||||
func (p *Clause) Float64() (target *float64) {
|
||||
@ -586,7 +586,7 @@ func (f *float32Value) Set(s string) error {
|
||||
|
||||
func (f *float32Value) Get() interface{} { return (float32)(*f.v) }
|
||||
|
||||
func (f *float32Value) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *float32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Float32 parses the next command-line value as float32.
|
||||
func (p *Clause) Float32() (target *float32) {
|
||||
@ -668,7 +668,7 @@ func (f *regexpValue) Set(s string) error {
|
||||
|
||||
func (f *regexpValue) Get() interface{} { return (*regexp.Regexp)(*f.v) }
|
||||
|
||||
func (f *regexpValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *regexpValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Regexp parses the next command-line value as *regexp.Regexp.
|
||||
func (p *Clause) Regexp() (target **regexp.Regexp) {
|
||||
@ -711,7 +711,7 @@ func (f *hexBytesValue) Set(s string) error {
|
||||
|
||||
func (f *hexBytesValue) Get() interface{} { return ([]byte)(*f.v) }
|
||||
|
||||
func (f *hexBytesValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *hexBytesValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Bytes as a hex string.
|
||||
func (p *Clause) HexBytes() (target *[]byte) {
|
||||
@ -754,7 +754,7 @@ func (f *durationValue) Set(s string) error {
|
||||
|
||||
func (f *durationValue) Get() interface{} { return (time.Duration)(*f.v) }
|
||||
|
||||
func (f *durationValue) String() string { return fmt.Sprintf("%v", *f) }
|
||||
func (f *durationValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
||||
|
||||
// Time duration.
|
||||
func (p *Clause) Duration() (target *time.Duration) {
|
||||
|
@ -30,7 +30,7 @@
|
||||
"importpath": "gopkg.in/alecthomas/kingpin.v3-unstable",
|
||||
"repository": "https://gopkg.in/alecthomas/kingpin.v3-unstable",
|
||||
"vcs": "git",
|
||||
"revision": "9670b87a702e049784340892f0d31a46473041c7",
|
||||
"revision": "63abe20a23e29e80bbef8089bd3dee3ac25e5306",
|
||||
"branch": "v3-unstable",
|
||||
"notests": true
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user