28
vendor/honnef.co/go/tools/lint/LICENSE
vendored
Normal file
28
vendor/honnef.co/go/tools/lint/LICENSE
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2013 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2016 Dominik Honnef. 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.
|
||||
539
vendor/honnef.co/go/tools/lint/lint.go
vendored
Normal file
539
vendor/honnef.co/go/tools/lint/lint.go
vendored
Normal file
@@ -0,0 +1,539 @@
|
||||
// Package lint provides the foundation for tools like staticcheck
|
||||
package lint // import "honnef.co/go/tools/lint"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"honnef.co/go/tools/config"
|
||||
"honnef.co/go/tools/internal/cache"
|
||||
)
|
||||
|
||||
type Documentation struct {
|
||||
Title string
|
||||
Text string
|
||||
Since string
|
||||
NonDefault bool
|
||||
Options []string
|
||||
}
|
||||
|
||||
func (doc *Documentation) String() string {
|
||||
b := &strings.Builder{}
|
||||
fmt.Fprintf(b, "%s\n\n", doc.Title)
|
||||
if doc.Text != "" {
|
||||
fmt.Fprintf(b, "%s\n\n", doc.Text)
|
||||
}
|
||||
fmt.Fprint(b, "Available since\n ")
|
||||
if doc.Since == "" {
|
||||
fmt.Fprint(b, "unreleased")
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s", doc.Since)
|
||||
}
|
||||
if doc.NonDefault {
|
||||
fmt.Fprint(b, ", non-default")
|
||||
}
|
||||
fmt.Fprint(b, "\n")
|
||||
if len(doc.Options) > 0 {
|
||||
fmt.Fprintf(b, "\nOptions\n")
|
||||
for _, opt := range doc.Options {
|
||||
fmt.Fprintf(b, " %s", opt)
|
||||
}
|
||||
fmt.Fprint(b, "\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type Ignore interface {
|
||||
Match(p Problem) bool
|
||||
}
|
||||
|
||||
type LineIgnore struct {
|
||||
File string
|
||||
Line int
|
||||
Checks []string
|
||||
Matched bool
|
||||
Pos token.Position
|
||||
}
|
||||
|
||||
func (li *LineIgnore) Match(p Problem) bool {
|
||||
pos := p.Pos
|
||||
if pos.Filename != li.File || pos.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.Pos.Filename != fi.File {
|
||||
return false
|
||||
}
|
||||
for _, c := range fi.Checks {
|
||||
if m, _ := filepath.Match(c, p.Check); m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Severity uint8
|
||||
|
||||
const (
|
||||
Error Severity = iota
|
||||
Warning
|
||||
Ignored
|
||||
)
|
||||
|
||||
// Problem represents a problem in some source code.
|
||||
type Problem struct {
|
||||
Pos token.Position
|
||||
End token.Position
|
||||
Message string
|
||||
Check string
|
||||
Severity Severity
|
||||
Related []Related
|
||||
}
|
||||
|
||||
type Related struct {
|
||||
Pos token.Position
|
||||
End token.Position
|
||||
Message string
|
||||
}
|
||||
|
||||
func (p Problem) Equal(o Problem) bool {
|
||||
return p.Pos == o.Pos &&
|
||||
p.End == o.End &&
|
||||
p.Message == o.Message &&
|
||||
p.Check == o.Check &&
|
||||
p.Severity == o.Severity
|
||||
}
|
||||
|
||||
func (p *Problem) String() string {
|
||||
return fmt.Sprintf("%s (%s)", p.Message, p.Check)
|
||||
}
|
||||
|
||||
// A Linter lints Go source code.
|
||||
type Linter struct {
|
||||
Checkers []*analysis.Analyzer
|
||||
CumulativeCheckers []CumulativeChecker
|
||||
GoVersion int
|
||||
Config config.Config
|
||||
Stats Stats
|
||||
RepeatAnalyzers uint
|
||||
}
|
||||
|
||||
type CumulativeChecker interface {
|
||||
Analyzer() *analysis.Analyzer
|
||||
Result() []types.Object
|
||||
ProblemObject(*token.FileSet, types.Object) Problem
|
||||
}
|
||||
|
||||
func (l *Linter) Lint(cfg *packages.Config, patterns []string) ([]Problem, error) {
|
||||
var allAnalyzers []*analysis.Analyzer
|
||||
allAnalyzers = append(allAnalyzers, l.Checkers...)
|
||||
for _, cum := range l.CumulativeCheckers {
|
||||
allAnalyzers = append(allAnalyzers, cum.Analyzer())
|
||||
}
|
||||
|
||||
// The -checks command line flag overrules all configuration
|
||||
// files, which means that for `-checks="foo"`, no check other
|
||||
// than foo can ever be reported to the user. Make use of this
|
||||
// fact to cull the list of analyses we need to run.
|
||||
|
||||
// replace "inherit" with "all", as we don't want to base the
|
||||
// list of all checks on the default configuration, which
|
||||
// disables certain checks.
|
||||
checks := make([]string, len(l.Config.Checks))
|
||||
copy(checks, l.Config.Checks)
|
||||
for i, c := range checks {
|
||||
if c == "inherit" {
|
||||
checks[i] = "all"
|
||||
}
|
||||
}
|
||||
|
||||
allowed := FilterChecks(allAnalyzers, checks)
|
||||
var allowedAnalyzers []*analysis.Analyzer
|
||||
for _, c := range l.Checkers {
|
||||
if allowed[c.Name] {
|
||||
allowedAnalyzers = append(allowedAnalyzers, c)
|
||||
}
|
||||
}
|
||||
hasCumulative := false
|
||||
for _, cum := range l.CumulativeCheckers {
|
||||
a := cum.Analyzer()
|
||||
if allowed[a.Name] {
|
||||
hasCumulative = true
|
||||
allowedAnalyzers = append(allowedAnalyzers, a)
|
||||
}
|
||||
}
|
||||
|
||||
r, err := NewRunner(&l.Stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.goVersion = l.GoVersion
|
||||
r.repeatAnalyzers = l.RepeatAnalyzers
|
||||
|
||||
pkgs, err := r.Run(cfg, patterns, allowedAnalyzers, hasCumulative)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tpkgToPkg := map[*types.Package]*Package{}
|
||||
for _, pkg := range pkgs {
|
||||
tpkgToPkg[pkg.Types] = pkg
|
||||
|
||||
for _, e := range pkg.errs {
|
||||
switch e := e.(type) {
|
||||
case types.Error:
|
||||
p := Problem{
|
||||
Pos: e.Fset.PositionFor(e.Pos, false),
|
||||
Message: e.Msg,
|
||||
Severity: Error,
|
||||
Check: "compile",
|
||||
}
|
||||
pkg.problems = append(pkg.problems, p)
|
||||
case packages.Error:
|
||||
msg := e.Msg
|
||||
if len(msg) != 0 && msg[0] == '\n' {
|
||||
// TODO(dh): See https://github.com/golang/go/issues/32363
|
||||
msg = msg[1:]
|
||||
}
|
||||
|
||||
var pos token.Position
|
||||
if e.Pos == "" {
|
||||
// Under certain conditions (malformed package
|
||||
// declarations, multiple packages in the same
|
||||
// directory), go list emits an error on stderr
|
||||
// instead of JSON. Those errors do not have
|
||||
// associated position information in
|
||||
// go/packages.Error, even though the output on
|
||||
// stderr may contain it.
|
||||
if p, n, err := parsePos(msg); err == nil {
|
||||
if abs, err := filepath.Abs(p.Filename); err == nil {
|
||||
p.Filename = abs
|
||||
}
|
||||
pos = p
|
||||
msg = msg[n+2:]
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
pos, _, err = parsePos(e.Pos)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("internal error: %s", e))
|
||||
}
|
||||
}
|
||||
p := Problem{
|
||||
Pos: pos,
|
||||
Message: msg,
|
||||
Severity: Error,
|
||||
Check: "compile",
|
||||
}
|
||||
pkg.problems = append(pkg.problems, p)
|
||||
case scanner.ErrorList:
|
||||
for _, e := range e {
|
||||
p := Problem{
|
||||
Pos: e.Pos,
|
||||
Message: e.Msg,
|
||||
Severity: Error,
|
||||
Check: "compile",
|
||||
}
|
||||
pkg.problems = append(pkg.problems, p)
|
||||
}
|
||||
case error:
|
||||
p := Problem{
|
||||
Pos: token.Position{},
|
||||
Message: e.Error(),
|
||||
Severity: Error,
|
||||
Check: "compile",
|
||||
}
|
||||
pkg.problems = append(pkg.problems, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&r.stats.State, StateCumulative)
|
||||
for _, cum := range l.CumulativeCheckers {
|
||||
for _, res := range cum.Result() {
|
||||
pkg := tpkgToPkg[res.Pkg()]
|
||||
if pkg == nil {
|
||||
panic(fmt.Sprintf("analyzer %s flagged object %s in package %s, a package that we aren't tracking", cum.Analyzer(), res, res.Pkg()))
|
||||
}
|
||||
allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks)
|
||||
if allowedChecks[cum.Analyzer().Name] {
|
||||
pos := DisplayPosition(pkg.Fset, res.Pos())
|
||||
// FIXME(dh): why are we ignoring generated files
|
||||
// here? Surely this is specific to 'unused', not all
|
||||
// cumulative checkers
|
||||
if _, ok := pkg.gen[pos.Filename]; ok {
|
||||
continue
|
||||
}
|
||||
p := cum.ProblemObject(pkg.Fset, res)
|
||||
pkg.problems = append(pkg.problems, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
if !pkg.fromSource {
|
||||
// Don't cache packages that we loaded from the cache
|
||||
continue
|
||||
}
|
||||
cpkg := cachedPackage{
|
||||
Problems: pkg.problems,
|
||||
Ignores: pkg.ignores,
|
||||
Config: pkg.cfg,
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
if err := gob.NewEncoder(buf).Encode(cpkg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id := cache.Subkey(pkg.actionID, "data "+r.problemsCacheKey)
|
||||
if err := r.cache.PutBytes(id, buf.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var problems []Problem
|
||||
// Deduplicate line ignores. When U1000 processes a package and
|
||||
// its test variant, it will only emit a single problem for an
|
||||
// unused object, not two problems. We will, however, have two
|
||||
// line ignores, one per package. Without deduplication, one line
|
||||
// ignore will be marked as matched, while the other one won't,
|
||||
// subsequently reporting a "this linter directive didn't match
|
||||
// anything" error.
|
||||
ignores := map[token.Position]Ignore{}
|
||||
for _, pkg := range pkgs {
|
||||
for _, ig := range pkg.ignores {
|
||||
if lig, ok := ig.(*LineIgnore); ok {
|
||||
ig = ignores[lig.Pos]
|
||||
if ig == nil {
|
||||
ignores[lig.Pos] = lig
|
||||
ig = lig
|
||||
}
|
||||
}
|
||||
for i := range pkg.problems {
|
||||
p := &pkg.problems[i]
|
||||
if ig.Match(*p) {
|
||||
p.Severity = Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pkg.cfg == nil {
|
||||
// The package failed to load, otherwise we would have a
|
||||
// valid config. Pass through all errors.
|
||||
problems = append(problems, pkg.problems...)
|
||||
} else {
|
||||
for _, p := range pkg.problems {
|
||||
allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks)
|
||||
allowedChecks["compile"] = true
|
||||
if allowedChecks[p.Check] {
|
||||
problems = append(problems, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ig := range pkg.ignores {
|
||||
ig, ok := ig.(*LineIgnore)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ig = ignores[ig.Pos].(*LineIgnore)
|
||||
if ig.Matched {
|
||||
continue
|
||||
}
|
||||
|
||||
couldveMatched := false
|
||||
allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks)
|
||||
for _, c := range ig.Checks {
|
||||
if !allowedChecks[c] {
|
||||
continue
|
||||
}
|
||||
couldveMatched = true
|
||||
break
|
||||
}
|
||||
|
||||
if !couldveMatched {
|
||||
// The ignored checks were disabled for the containing package.
|
||||
// Don't flag the ignore for not having matched.
|
||||
continue
|
||||
}
|
||||
p := Problem{
|
||||
Pos: ig.Pos,
|
||||
Message: "this linter directive didn't match anything; should it be removed?",
|
||||
Check: "",
|
||||
}
|
||||
problems = append(problems, p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(problems) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sort.Slice(problems, func(i, j int) bool {
|
||||
pi := problems[i].Pos
|
||||
pj := problems[j].Pos
|
||||
|
||||
if pi.Filename != pj.Filename {
|
||||
return pi.Filename < pj.Filename
|
||||
}
|
||||
if pi.Line != pj.Line {
|
||||
return pi.Line < pj.Line
|
||||
}
|
||||
if pi.Column != pj.Column {
|
||||
return pi.Column < pj.Column
|
||||
}
|
||||
|
||||
return problems[i].Message < problems[j].Message
|
||||
})
|
||||
|
||||
var out []Problem
|
||||
out = append(out, problems[0])
|
||||
for i, p := range problems[1:] {
|
||||
// We may encounter duplicate problems because one file
|
||||
// can be part of many packages.
|
||||
if !problems[i].Equal(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func FilterChecks(allChecks []*analysis.Analyzer, checks []string) map[string]bool {
|
||||
// OPT(dh): this entire computation could be cached per package
|
||||
allowedChecks := map[string]bool{}
|
||||
|
||||
for _, check := range checks {
|
||||
b := true
|
||||
if len(check) > 1 && check[0] == '-' {
|
||||
b = false
|
||||
check = check[1:]
|
||||
}
|
||||
if check == "*" || check == "all" {
|
||||
// Match all
|
||||
for _, c := range allChecks {
|
||||
allowedChecks[c.Name] = b
|
||||
}
|
||||
} else if strings.HasSuffix(check, "*") {
|
||||
// Glob
|
||||
prefix := check[:len(check)-1]
|
||||
isCat := strings.IndexFunc(prefix, func(r rune) bool { return unicode.IsNumber(r) }) == -1
|
||||
|
||||
for _, c := range allChecks {
|
||||
idx := strings.IndexFunc(c.Name, func(r rune) bool { return unicode.IsNumber(r) })
|
||||
if isCat {
|
||||
// Glob is S*, which should match S1000 but not SA1000
|
||||
cat := c.Name[:idx]
|
||||
if prefix == cat {
|
||||
allowedChecks[c.Name] = b
|
||||
}
|
||||
} else {
|
||||
// Glob is S1*
|
||||
if strings.HasPrefix(c.Name, prefix) {
|
||||
allowedChecks[c.Name] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Literal check name
|
||||
allowedChecks[check] = b
|
||||
}
|
||||
}
|
||||
return allowedChecks
|
||||
}
|
||||
|
||||
func DisplayPosition(fset *token.FileSet, p token.Pos) token.Position {
|
||||
if p == token.NoPos {
|
||||
return token.Position{}
|
||||
}
|
||||
|
||||
// Only use the adjusted position if it points to another Go file.
|
||||
// This means we'll point to the original file for cgo files, but
|
||||
// we won't point to a YACC grammar file.
|
||||
pos := fset.PositionFor(p, false)
|
||||
adjPos := fset.PositionFor(p, true)
|
||||
|
||||
if filepath.Ext(adjPos.Filename) == ".go" {
|
||||
return adjPos
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
var bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(64)
|
||||
return buf
|
||||
},
|
||||
}
|
||||
|
||||
func FuncName(f *types.Func) string {
|
||||
buf := bufferPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
if f.Type() != nil {
|
||||
sig := f.Type().(*types.Signature)
|
||||
if recv := sig.Recv(); recv != nil {
|
||||
buf.WriteByte('(')
|
||||
if _, ok := recv.Type().(*types.Interface); ok {
|
||||
// gcimporter creates abstract methods of
|
||||
// named interfaces using the interface type
|
||||
// (not the named type) as the receiver.
|
||||
// Don't print it in full.
|
||||
buf.WriteString("interface")
|
||||
} else {
|
||||
types.WriteType(buf, recv.Type(), nil)
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
buf.WriteByte('.')
|
||||
} else if f.Pkg() != nil {
|
||||
writePackage(buf, f.Pkg())
|
||||
}
|
||||
}
|
||||
buf.WriteString(f.Name())
|
||||
s := buf.String()
|
||||
bufferPool.Put(buf)
|
||||
return s
|
||||
}
|
||||
|
||||
func writePackage(buf *bytes.Buffer, pkg *types.Package) {
|
||||
if pkg == nil {
|
||||
return
|
||||
}
|
||||
s := pkg.Path()
|
||||
if s != "" {
|
||||
buf.WriteString(s)
|
||||
buf.WriteByte('.')
|
||||
}
|
||||
}
|
||||
58
vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go
vendored
Normal file
58
vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Package lintdsl provides helpers for implementing static analysis
|
||||
// checks. Dot-importing this package is encouraged.
|
||||
package lintdsl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"honnef.co/go/tools/pattern"
|
||||
)
|
||||
|
||||
func Inspect(node ast.Node, fn func(node ast.Node) bool) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
ast.Inspect(node, fn)
|
||||
}
|
||||
|
||||
func Match(pass *analysis.Pass, q pattern.Pattern, node ast.Node) (*pattern.Matcher, bool) {
|
||||
// Note that we ignore q.Relevant – callers of Match usually use
|
||||
// AST inspectors that already filter on nodes we're interested
|
||||
// in.
|
||||
m := &pattern.Matcher{TypesInfo: pass.TypesInfo}
|
||||
ok := m.Match(q.Root, node)
|
||||
return m, ok
|
||||
}
|
||||
|
||||
func MatchAndEdit(pass *analysis.Pass, before, after pattern.Pattern, node ast.Node) (*pattern.Matcher, []analysis.TextEdit, bool) {
|
||||
m, ok := Match(pass, before, node)
|
||||
if !ok {
|
||||
return m, nil, false
|
||||
}
|
||||
r := pattern.NodeToAST(after.Root, m.State)
|
||||
buf := &bytes.Buffer{}
|
||||
format.Node(buf, pass.Fset, r)
|
||||
edit := []analysis.TextEdit{{
|
||||
Pos: node.Pos(),
|
||||
End: node.End(),
|
||||
NewText: buf.Bytes(),
|
||||
}}
|
||||
return m, edit, true
|
||||
}
|
||||
|
||||
func Selector(x, sel string) *ast.SelectorExpr {
|
||||
return &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: x},
|
||||
Sel: &ast.Ident{Name: sel},
|
||||
}
|
||||
}
|
||||
|
||||
// ExhaustiveTypeSwitch panics when called. It can be used to ensure
|
||||
// that type switches are exhaustive.
|
||||
func ExhaustiveTypeSwitch(v interface{}) {
|
||||
panic(fmt.Sprintf("internal error: unhandled case %T", v))
|
||||
}
|
||||
162
vendor/honnef.co/go/tools/lint/lintutil/format/format.go
vendored
Normal file
162
vendor/honnef.co/go/tools/lint/lintutil/format/format.go
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Package format provides formatters for linter problems.
|
||||
package format
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/tabwriter"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
)
|
||||
|
||||
func shortPath(path string) string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
|
||||
return rel
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func relativePositionString(pos token.Position) string {
|
||||
s := shortPath(pos.Filename)
|
||||
if pos.IsValid() {
|
||||
if s != "" {
|
||||
s += ":"
|
||||
}
|
||||
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
|
||||
}
|
||||
if s == "" {
|
||||
s = "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type Statter interface {
|
||||
Stats(total, errors, warnings, ignored int)
|
||||
}
|
||||
|
||||
type Formatter interface {
|
||||
Format(p lint.Problem)
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
W io.Writer
|
||||
}
|
||||
|
||||
func (o Text) Format(p lint.Problem) {
|
||||
fmt.Fprintf(o.W, "%s: %s\n", relativePositionString(p.Pos), p.String())
|
||||
for _, r := range p.Related {
|
||||
fmt.Fprintf(o.W, "\t%s: %s\n", relativePositionString(r.Pos), r.Message)
|
||||
}
|
||||
}
|
||||
|
||||
type JSON struct {
|
||||
W io.Writer
|
||||
}
|
||||
|
||||
func severity(s lint.Severity) string {
|
||||
switch s {
|
||||
case lint.Error:
|
||||
return "error"
|
||||
case lint.Warning:
|
||||
return "warning"
|
||||
case lint.Ignored:
|
||||
return "ignored"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (o JSON) Format(p lint.Problem) {
|
||||
type location struct {
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
Column int `json:"column"`
|
||||
}
|
||||
type related struct {
|
||||
Location location `json:"location"`
|
||||
End location `json:"end"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
jp := struct {
|
||||
Code string `json:"code"`
|
||||
Severity string `json:"severity,omitempty"`
|
||||
Location location `json:"location"`
|
||||
End location `json:"end"`
|
||||
Message string `json:"message"`
|
||||
Related []related `json:"related,omitempty"`
|
||||
}{
|
||||
Code: p.Check,
|
||||
Severity: severity(p.Severity),
|
||||
Location: location{
|
||||
File: p.Pos.Filename,
|
||||
Line: p.Pos.Line,
|
||||
Column: p.Pos.Column,
|
||||
},
|
||||
End: location{
|
||||
File: p.End.Filename,
|
||||
Line: p.End.Line,
|
||||
Column: p.End.Column,
|
||||
},
|
||||
Message: p.Message,
|
||||
}
|
||||
for _, r := range p.Related {
|
||||
jp.Related = append(jp.Related, related{
|
||||
Location: location{
|
||||
File: r.Pos.Filename,
|
||||
Line: r.Pos.Line,
|
||||
Column: r.Pos.Column,
|
||||
},
|
||||
End: location{
|
||||
File: r.End.Filename,
|
||||
Line: r.End.Line,
|
||||
Column: r.End.Column,
|
||||
},
|
||||
Message: r.Message,
|
||||
})
|
||||
}
|
||||
_ = json.NewEncoder(o.W).Encode(jp)
|
||||
}
|
||||
|
||||
type Stylish struct {
|
||||
W io.Writer
|
||||
|
||||
prevFile string
|
||||
tw *tabwriter.Writer
|
||||
}
|
||||
|
||||
func (o *Stylish) Format(p lint.Problem) {
|
||||
pos := p.Pos
|
||||
if pos.Filename == "" {
|
||||
pos.Filename = "-"
|
||||
}
|
||||
|
||||
if pos.Filename != o.prevFile {
|
||||
if o.prevFile != "" {
|
||||
o.tw.Flush()
|
||||
fmt.Fprintln(o.W)
|
||||
}
|
||||
fmt.Fprintln(o.W, pos.Filename)
|
||||
o.prevFile = pos.Filename
|
||||
o.tw = tabwriter.NewWriter(o.W, 0, 4, 2, ' ', 0)
|
||||
}
|
||||
fmt.Fprintf(o.tw, " (%d, %d)\t%s\t%s\n", pos.Line, pos.Column, p.Check, p.Message)
|
||||
for _, r := range p.Related {
|
||||
fmt.Fprintf(o.tw, " (%d, %d)\t\t %s\n", r.Pos.Line, r.Pos.Column, r.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Stylish) Stats(total, errors, warnings, ignored int) {
|
||||
if o.tw != nil {
|
||||
o.tw.Flush()
|
||||
fmt.Fprintln(o.W)
|
||||
}
|
||||
fmt.Fprintf(o.W, " ✖ %d problems (%d errors, %d warnings, %d ignored)\n",
|
||||
total, errors, warnings, ignored)
|
||||
}
|
||||
7
vendor/honnef.co/go/tools/lint/lintutil/stats.go
vendored
Normal file
7
vendor/honnef.co/go/tools/lint/lintutil/stats.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build !aix,!android,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package lintutil
|
||||
|
||||
import "os"
|
||||
|
||||
var infoSignals = []os.Signal{}
|
||||
10
vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go
vendored
Normal file
10
vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package lintutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var infoSignals = []os.Signal{syscall.SIGINFO}
|
||||
10
vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go
vendored
Normal file
10
vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build aix android linux solaris
|
||||
|
||||
package lintutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var infoSignals = []os.Signal{syscall.SIGUSR1}
|
||||
444
vendor/honnef.co/go/tools/lint/lintutil/util.go
vendored
Normal file
444
vendor/honnef.co/go/tools/lint/lintutil/util.go
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||
// Copyright (c) 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// Package lintutil provides helpers for writing linter command lines.
|
||||
package lintutil // import "honnef.co/go/tools/lint/lintutil"
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"honnef.co/go/tools/config"
|
||||
"honnef.co/go/tools/internal/cache"
|
||||
"honnef.co/go/tools/lint"
|
||||
"honnef.co/go/tools/lint/lintutil/format"
|
||||
"honnef.co/go/tools/version"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func NewVersionFlag() flag.Getter {
|
||||
tags := build.Default.ReleaseTags
|
||||
v := tags[len(tags)-1][2:]
|
||||
version := new(VersionFlag)
|
||||
if err := version.Set(v); err != nil {
|
||||
panic(fmt.Sprintf("internal error: %s", err))
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
type VersionFlag int
|
||||
|
||||
func (v *VersionFlag) String() string {
|
||||
return fmt.Sprintf("1.%d", *v)
|
||||
|
||||
}
|
||||
|
||||
func (v *VersionFlag) Set(s string) error {
|
||||
if len(s) < 3 {
|
||||
return errors.New("invalid Go version")
|
||||
}
|
||||
if s[0] != '1' {
|
||||
return errors.New("invalid Go version")
|
||||
}
|
||||
if s[1] != '.' {
|
||||
return errors.New("invalid Go version")
|
||||
}
|
||||
i, err := strconv.Atoi(s[2:])
|
||||
*v = VersionFlag(i)
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *VersionFlag) Get() interface{} {
|
||||
return int(*v)
|
||||
}
|
||||
|
||||
func usage(name string, flags *flag.FlagSet) func() {
|
||||
return func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
|
||||
fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
|
||||
fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
|
||||
fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
|
||||
fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
|
||||
fmt.Fprintf(os.Stderr, "Flags:\n")
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
type list []string
|
||||
|
||||
func (list *list) String() string {
|
||||
return `"` + strings.Join(*list, ",") + `"`
|
||||
}
|
||||
|
||||
func (list *list) Set(s string) error {
|
||||
if s == "" {
|
||||
*list = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
*list = strings.Split(s, ",")
|
||||
return nil
|
||||
}
|
||||
|
||||
func FlagSet(name string) *flag.FlagSet {
|
||||
flags := flag.NewFlagSet("", flag.ExitOnError)
|
||||
flags.Usage = usage(name, flags)
|
||||
flags.String("tags", "", "List of `build tags`")
|
||||
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 'stylish', 'text' and 'json')")
|
||||
flags.String("explain", "", "Print description of `check`")
|
||||
|
||||
flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")
|
||||
flags.String("debug.memprofile", "", "Write memory profile to `file`")
|
||||
flags.Bool("debug.version", false, "Print detailed version information about this program")
|
||||
flags.Bool("debug.no-compile-errors", false, "Don't print compile errors")
|
||||
flags.String("debug.measure-analyzers", "", "Write analysis measurements to `file`. `file` will be opened for appending if it already exists.")
|
||||
flags.Uint("debug.repeat-analyzers", 0, "Run analyzers `num` times")
|
||||
|
||||
checks := list{"inherit"}
|
||||
fail := list{"all"}
|
||||
flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
|
||||
flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")
|
||||
|
||||
tags := build.Default.ReleaseTags
|
||||
v := tags[len(tags)-1][2:]
|
||||
version := new(VersionFlag)
|
||||
if err := version.Set(v); err != nil {
|
||||
panic(fmt.Sprintf("internal error: %s", err))
|
||||
}
|
||||
|
||||
flags.Var(version, "go", "Target Go `version` in the format '1.x'")
|
||||
return flags
|
||||
}
|
||||
|
||||
func findCheck(cs []*analysis.Analyzer, check string) (*analysis.Analyzer, bool) {
|
||||
for _, c := range cs {
|
||||
if c.Name == check {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func ProcessFlagSet(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, fs *flag.FlagSet) {
|
||||
tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
|
||||
tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
|
||||
goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
|
||||
formatter := 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)
|
||||
explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string)
|
||||
|
||||
cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)
|
||||
memProfile := fs.Lookup("debug.memprofile").Value.(flag.Getter).Get().(string)
|
||||
debugVersion := fs.Lookup("debug.version").Value.(flag.Getter).Get().(bool)
|
||||
debugNoCompile := fs.Lookup("debug.no-compile-errors").Value.(flag.Getter).Get().(bool)
|
||||
debugRepeat := fs.Lookup("debug.repeat-analyzers").Value.(flag.Getter).Get().(uint)
|
||||
|
||||
var measureAnalyzers func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration)
|
||||
if path := fs.Lookup("debug.measure-analyzers").Value.(flag.Getter).Get().(string); path != "" {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
mu := &sync.Mutex{}
|
||||
measureAnalyzers = func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if _, err := fmt.Fprintf(f, "%s\t%s\t%d\n", analysis.Name, pkg.ID, d.Nanoseconds()); err != nil {
|
||||
log.Println("error writing analysis measurements:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg := config.Config{}
|
||||
cfg.Checks = *fs.Lookup("checks").Value.(*list)
|
||||
|
||||
exit := func(code int) {
|
||||
if cpuProfile != "" {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
if memProfile != "" {
|
||||
f, err := os.Create(memProfile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
runtime.GC()
|
||||
pprof.WriteHeapProfile(f)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
if cpuProfile != "" {
|
||||
f, err := os.Create(cpuProfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
}
|
||||
|
||||
if debugVersion {
|
||||
version.Verbose()
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if printVersion {
|
||||
version.Print()
|
||||
exit(0)
|
||||
}
|
||||
|
||||
// Validate that the tags argument is well-formed. go/packages
|
||||
// doesn't detect malformed build flags and returns unhelpful
|
||||
// errors.
|
||||
tf := buildutil.TagsFlag{}
|
||||
if err := tf.Set(tags); err != nil {
|
||||
fmt.Fprintln(os.Stderr, fmt.Errorf("invalid value %q for flag -tags: %s", tags, err))
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if explain != "" {
|
||||
var haystack []*analysis.Analyzer
|
||||
haystack = append(haystack, cs...)
|
||||
for _, cum := range cums {
|
||||
haystack = append(haystack, cum.Analyzer())
|
||||
}
|
||||
check, ok := findCheck(haystack, explain)
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "Couldn't find check", explain)
|
||||
exit(1)
|
||||
}
|
||||
if check.Doc == "" {
|
||||
fmt.Fprintln(os.Stderr, explain, "has no documentation")
|
||||
exit(1)
|
||||
}
|
||||
fmt.Println(check.Doc)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
ps, err := Lint(cs, cums, fs.Args(), &Options{
|
||||
Tags: tags,
|
||||
LintTests: tests,
|
||||
GoVersion: goVersion,
|
||||
Config: cfg,
|
||||
PrintAnalyzerMeasurement: measureAnalyzers,
|
||||
RepeatAnalyzers: debugRepeat,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
var f format.Formatter
|
||||
switch formatter {
|
||||
case "text":
|
||||
f = format.Text{W: os.Stdout}
|
||||
case "stylish":
|
||||
f = &format.Stylish{W: os.Stdout}
|
||||
case "json":
|
||||
f = format.JSON{W: os.Stdout}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
|
||||
exit(2)
|
||||
}
|
||||
|
||||
var (
|
||||
total int
|
||||
errors int
|
||||
warnings int
|
||||
ignored int
|
||||
)
|
||||
|
||||
fail := *fs.Lookup("fail").Value.(*list)
|
||||
analyzers := make([]*analysis.Analyzer, len(cs), len(cs)+len(cums))
|
||||
copy(analyzers, cs)
|
||||
for _, cum := range cums {
|
||||
analyzers = append(analyzers, cum.Analyzer())
|
||||
}
|
||||
shouldExit := lint.FilterChecks(analyzers, fail)
|
||||
shouldExit["compile"] = true
|
||||
|
||||
total = len(ps)
|
||||
for _, p := range ps {
|
||||
if p.Check == "compile" && debugNoCompile {
|
||||
continue
|
||||
}
|
||||
if p.Severity == lint.Ignored && !showIgnored {
|
||||
ignored++
|
||||
continue
|
||||
}
|
||||
if shouldExit[p.Check] {
|
||||
errors++
|
||||
} else {
|
||||
p.Severity = lint.Warning
|
||||
warnings++
|
||||
}
|
||||
f.Format(p)
|
||||
}
|
||||
if f, ok := f.(format.Statter); ok {
|
||||
f.Stats(total, errors, warnings, ignored)
|
||||
}
|
||||
if errors > 0 {
|
||||
exit(1)
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Config config.Config
|
||||
|
||||
Tags string
|
||||
LintTests bool
|
||||
GoVersion int
|
||||
PrintAnalyzerMeasurement func(analysis *analysis.Analyzer, pkg *lint.Package, d time.Duration)
|
||||
RepeatAnalyzers uint
|
||||
}
|
||||
|
||||
func computeSalt() ([]byte, error) {
|
||||
if version.Version != "devel" {
|
||||
return []byte(version.Version), nil
|
||||
}
|
||||
p, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func Lint(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, paths []string, opt *Options) ([]lint.Problem, error) {
|
||||
salt, err := computeSalt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not compute salt for cache: %s", err)
|
||||
}
|
||||
cache.SetSalt(salt)
|
||||
|
||||
if opt == nil {
|
||||
opt = &Options{}
|
||||
}
|
||||
|
||||
l := &lint.Linter{
|
||||
Checkers: cs,
|
||||
CumulativeCheckers: cums,
|
||||
GoVersion: opt.GoVersion,
|
||||
Config: opt.Config,
|
||||
RepeatAnalyzers: opt.RepeatAnalyzers,
|
||||
}
|
||||
l.Stats.PrintAnalyzerMeasurement = opt.PrintAnalyzerMeasurement
|
||||
cfg := &packages.Config{}
|
||||
if opt.LintTests {
|
||||
cfg.Tests = true
|
||||
}
|
||||
if opt.Tags != "" {
|
||||
cfg.BuildFlags = append(cfg.BuildFlags, "-tags", opt.Tags)
|
||||
}
|
||||
|
||||
printStats := func() {
|
||||
// Individual stats are read atomically, but overall there
|
||||
// is no synchronisation. For printing rough progress
|
||||
// information, this doesn't matter.
|
||||
switch atomic.LoadUint32(&l.Stats.State) {
|
||||
case lint.StateInitializing:
|
||||
fmt.Fprintln(os.Stderr, "Status: initializing")
|
||||
case lint.StateGraph:
|
||||
fmt.Fprintln(os.Stderr, "Status: loading package graph")
|
||||
case lint.StateProcessing:
|
||||
fmt.Fprintf(os.Stderr, "Packages: %d/%d initial, %d/%d total; Workers: %d/%d; Problems: %d\n",
|
||||
atomic.LoadUint32(&l.Stats.ProcessedInitialPackages),
|
||||
atomic.LoadUint32(&l.Stats.InitialPackages),
|
||||
atomic.LoadUint32(&l.Stats.ProcessedPackages),
|
||||
atomic.LoadUint32(&l.Stats.TotalPackages),
|
||||
atomic.LoadUint32(&l.Stats.ActiveWorkers),
|
||||
atomic.LoadUint32(&l.Stats.TotalWorkers),
|
||||
atomic.LoadUint32(&l.Stats.Problems),
|
||||
)
|
||||
case lint.StateCumulative:
|
||||
fmt.Fprintln(os.Stderr, "Status: processing cumulative checkers")
|
||||
}
|
||||
}
|
||||
if len(infoSignals) > 0 {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, infoSignals...)
|
||||
defer signal.Stop(ch)
|
||||
go func() {
|
||||
for range ch {
|
||||
printStats()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
ps, err := l.Lint(cfg, paths)
|
||||
return ps, err
|
||||
}
|
||||
|
||||
var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?$`)
|
||||
|
||||
func parsePos(pos string) token.Position {
|
||||
if pos == "-" || pos == "" {
|
||||
return token.Position{}
|
||||
}
|
||||
parts := posRe.FindStringSubmatch(pos)
|
||||
if parts == nil {
|
||||
panic(fmt.Sprintf("internal error: malformed position %q", pos))
|
||||
}
|
||||
file := parts[1]
|
||||
line, _ := strconv.Atoi(parts[2])
|
||||
col, _ := strconv.Atoi(parts[3])
|
||||
return token.Position{
|
||||
Filename: file,
|
||||
Line: line,
|
||||
Column: col,
|
||||
}
|
||||
}
|
||||
|
||||
func InitializeAnalyzers(docs map[string]*lint.Documentation, analyzers map[string]*analysis.Analyzer) map[string]*analysis.Analyzer {
|
||||
out := make(map[string]*analysis.Analyzer, len(analyzers))
|
||||
for k, v := range analyzers {
|
||||
vc := *v
|
||||
out[k] = &vc
|
||||
|
||||
vc.Name = k
|
||||
doc, ok := docs[k]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("missing documentation for check %s", k))
|
||||
}
|
||||
vc.Doc = doc.String()
|
||||
if vc.Flags.Usage == nil {
|
||||
fs := flag.NewFlagSet("", flag.PanicOnError)
|
||||
fs.Var(NewVersionFlag(), "go", "Target Go version")
|
||||
vc.Flags = *fs
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
1114
vendor/honnef.co/go/tools/lint/runner.go
vendored
Normal file
1114
vendor/honnef.co/go/tools/lint/runner.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
38
vendor/honnef.co/go/tools/lint/stats.go
vendored
Normal file
38
vendor/honnef.co/go/tools/lint/stats.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package lint
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const (
|
||||
StateInitializing = 0
|
||||
StateGraph = 1
|
||||
StateProcessing = 2
|
||||
StateCumulative = 3
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
State uint32
|
||||
|
||||
InitialPackages uint32
|
||||
TotalPackages uint32
|
||||
ProcessedPackages uint32
|
||||
ProcessedInitialPackages uint32
|
||||
Problems uint32
|
||||
ActiveWorkers uint32
|
||||
TotalWorkers uint32
|
||||
PrintAnalyzerMeasurement func(*analysis.Analyzer, *Package, time.Duration)
|
||||
}
|
||||
|
||||
type AnalysisMeasurementKey struct {
|
||||
Analysis string
|
||||
Pkg string
|
||||
}
|
||||
|
||||
func (s *Stats) MeasureAnalyzer(analysis *analysis.Analyzer, pkg *Package, d time.Duration) {
|
||||
if s.PrintAnalyzerMeasurement != nil {
|
||||
s.PrintAnalyzerMeasurement(analysis, pkg, d)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user