Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / lint / lint.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/lint/lint.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/lint/lint.go
new file mode 100644 (file)
index 0000000..1a70e0c
--- /dev/null
@@ -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('.')
+       }
+}