aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/lint/lintutil/util.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/honnef.co/go/tools/lint/lintutil/util.go')
-rw-r--r--vendor/honnef.co/go/tools/lint/lintutil/util.go349
1 files changed, 349 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/lint/lintutil/util.go b/vendor/honnef.co/go/tools/lint/lintutil/util.go
new file mode 100644
index 0000000..0bb1426
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintutil/util.go
@@ -0,0 +1,349 @@
+// 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 (
+ "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)
+ 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 runner struct {
+ checker lint.Checker
+ tags []string
+ ignores []lint.Ignore
+ version int
+ returnIgnored bool
+}
+
+func resolveRelative(importPaths []string, tags []string) (goFiles bool, err error) {
+ if len(importPaths) == 0 {
+ return false, nil
+ }
+ if strings.HasSuffix(importPaths[0], ".go") {
+ // User is specifying a package in terms of .go files, don't resolve
+ return true, nil
+ }
+ wd, err := os.Getwd()
+ if err != nil {
+ return false, err
+ }
+ ctx := build.Default
+ ctx.BuildTags = tags
+ for i, path := range importPaths {
+ bpkg, err := ctx.Import(path, wd, build.FindOnly)
+ if err != nil {
+ return false, fmt.Errorf("can't load package %q: %v", path, err)
+ }
+ importPaths[i] = bpkg.ImportPath
+ }
+ return false, nil
+}
+
+func parseIgnore(s string) ([]lint.Ignore, error) {
+ var out []lint.Ignore
+ if len(s) == 0 {
+ return nil, nil
+ }
+ for _, part := range strings.Fields(s) {
+ p := strings.Split(part, ":")
+ if len(p) != 2 {
+ return nil, errors.New("malformed ignore string")
+ }
+ path := p[0]
+ checks := strings.Split(p[1], ",")
+ out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
+ }
+ return out, nil
+}
+
+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 FlagSet(name string) *flag.FlagSet {
+ flags := flag.NewFlagSet("", flag.ExitOnError)
+ flags.Usage = usage(name, flags)
+ flags.Float64("min_confidence", 0, "Deprecated; use -ignore instead")
+ 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:]
+ 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
+}
+
+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)
+ 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)
+
+ 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)
+ }
+
+ var ps []lint.Problem
+ for _, p := range pss {
+ ps = append(ps, p...)
+ }
+
+ var f OutputFormatter
+ switch format {
+ case "text":
+ f = TextOutput{os.Stdout}
+ case "json":
+ f = JSONOutput{os.Stdout}
+ default:
+ fmt.Fprintf(os.Stderr, "unsupported output format %q\n", format)
+ os.Exit(2)
+ }
+
+ for _, p := range ps {
+ f.Format(p)
+ }
+ for i, p := range pss {
+ if len(p) != 0 && confs[i].ExitNonZero {
+ os.Exit(1)
+ }
+ }
+}
+
+type Options struct {
+ Tags []string
+ LintTests bool
+ Ignores string
+ GoVersion int
+ ReturnIgnored bool
+}
+
+func Lint(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, err
+ }
+ paths := gotool.ImportPaths(pkgs)
+ goFiles, err := resolveRelative(paths, opt.Tags)
+ if err != nil {
+ return nil, err
+ }
+ ctx := build.Default
+ 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...)
+ } else {
+ for _, path := range paths {
+ conf.ImportPkgs[path] = opt.LintTests
+ }
+ }
+ lprog, err := conf.Load()
+ if err != nil {
+ return nil, err
+ }
+
+ 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 {
+ 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
+}
+
+func ProcessArgs(name string, cs []CheckerConfig, args []string) {
+ flags := FlagSet(name)
+ flags.Parse(args)
+
+ ProcessFlagSet(cs, flags)
+}
+
+func (runner *runner) lint(lprog *loader.Program, conf *loader.Config) []lint.Problem {
+ l := &lint.Linter{
+ Checker: runner.checker,
+ Ignores: runner.ignores,
+ GoVersion: runner.version,
+ ReturnIgnored: runner.returnIgnored,
+ }
+ return l.Lint(lprog, conf)
+}