diff --git a/Gopkg.lock b/Gopkg.lock index 6228d1cc..56932356 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -72,7 +72,7 @@ "unused", "version" ] - revision = "38c5f4a0efc6bd0efc88bdd3890e5e64e698bdc2" + revision = "f557f368b7f3d00a54ed44eb57b9eca59e85cee1" [[projects]] branch = "master" @@ -136,6 +136,15 @@ revision = "7a3d63cfc6fbd9e46f2e551f9b8d1e9c69bffab1" source = "github.com/golangci/errcheck" +[[projects]] + name = "github.com/kisielk/gotool" + packages = [ + ".", + "internal/load" + ] + revision = "80517062f582ea3340cd4baf70e86d539ae7d84d" + version = "v1.0.0" + [[projects]] name = "github.com/mattn/go-colorable" packages = ["."] @@ -243,6 +252,20 @@ ] revision = "87723262609ca8fd55d449c027454c29cadefd68" +[[projects]] + branch = "master" + name = "mvdan.cc/interfacer" + packages = ["check"] + revision = "72c3fb5d5e5e0ca07e9a7c90bcd150b049920b3b" + source = "github.com/golangci/interfacer" + +[[projects]] + branch = "master" + name = "mvdan.cc/lint" + packages = ["."] + revision = "8ff1696d5934157cea033c4b97cf5ea23fdb7a32" + source = "github.com/golangci/lint" + [[projects]] branch = "master" name = "sourcegraph.com/sourcegraph/go-diff" @@ -258,6 +281,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "aa0c6cd4a6cd5957ffdeeb0f06753a92f33f5393045845e45bc63a10ead24a85" + inputs-digest = "6e4437a7adb255525e8d1cfda07716617c1f28acaa721ac4f910f52f49ecea2f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 53fc9e9a..91874be6 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -55,6 +55,16 @@ branch = "master" source = "github.com/golangci/dupl" +[[constraint]] + name = "mvdan.cc/interfacer" + branch = "master" + source = "github.com/golangci/interfacer" + +[[override]] + name = "mvdan.cc/lint" + branch = "master" + source = "github.com/golangci/lint" + [prune] go-tests = true unused-packages = true diff --git a/internal/commands/run.go b/internal/commands/run.go index 508f2a51..77408071 100644 --- a/internal/commands/run.go +++ b/internal/commands/run.go @@ -10,6 +10,8 @@ import ( "time" "github.com/fatih/color" + "github.com/golangci/go-tools/ssa" + "github.com/golangci/go-tools/ssa/ssautil" "github.com/golangci/golangci-lint/pkg" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/fsutils" @@ -84,6 +86,17 @@ func isFullImportNeeded(linters []pkg.Linter) bool { return false } +func isSSAReprNeeded(linters []pkg.Linter) bool { + for _, linter := range linters { + lc := pkg.GetLinterConfig(linter.Name()) + if lc.NeedsSSARepr { + return true + } + } + + return false +} + func loadWholeAppIfNeeded(ctx context.Context, linters []pkg.Linter, cfg *config.Run, paths *fsutils.ProjectPaths) (*loader.Program, *loader.Config, error) { if !isFullImportNeeded(linters) { return nil, nil, nil @@ -117,6 +130,17 @@ func loadWholeAppIfNeeded(ctx context.Context, linters []pkg.Linter, cfg *config return prog, loadcfg, nil } +func buildSSAProgram(ctx context.Context, lprog *loader.Program) *ssa.Program { + startedAt := time.Now() + defer func() { + analytics.Log(ctx).Infof("SSA repr building took %s", time.Since(startedAt)) + }() + + ssaProg := ssautil.CreateProgram(lprog, ssa.GlobalDebug) + ssaProg.Build() + return ssaProg +} + func buildLintCtx(ctx context.Context, linters []pkg.Linter, cfg *config.Config) (*golinters.Context, error) { args := cfg.Run.Args if len(args) == 0 { @@ -133,10 +157,16 @@ func buildLintCtx(ctx context.Context, linters []pkg.Linter, cfg *config.Config) return nil, err } + var ssaProg *ssa.Program + if prog != nil && isSSAReprNeeded(linters) { + ssaProg = buildSSAProgram(ctx, prog) + } + return &golinters.Context{ Paths: paths, Cfg: cfg, Program: prog, + SSAProgram: ssaProg, LoaderConfig: loaderConfig, }, nil } diff --git a/pkg/enabled_linters.go b/pkg/enabled_linters.go index db96f7ef..6563830c 100644 --- a/pkg/enabled_linters.go +++ b/pkg/enabled_linters.go @@ -15,6 +15,7 @@ type LinterConfig struct { Linter Linter EnabledByDefault bool DoesFullImport bool + NeedsSSARepr bool } var nameToLC map[string]LinterConfig @@ -36,40 +37,43 @@ func GetLinterConfig(name string) *LinterConfig { return &lc } -func enabledByDefault(linter Linter, desc string, doesFullImport bool) LinterConfig { +func enabledByDefault(linter Linter, desc string, doesFullImport, needsSSARepr bool) LinterConfig { return LinterConfig{ EnabledByDefault: true, Linter: linter, Desc: desc, DoesFullImport: doesFullImport, + NeedsSSARepr: needsSSARepr, } } -func disabledByDefault(linter Linter, desc string, doesFullImport bool) LinterConfig { +func disabledByDefault(linter Linter, desc string, doesFullImport, needsSSARepr bool) LinterConfig { return LinterConfig{ EnabledByDefault: false, Linter: linter, Desc: desc, DoesFullImport: doesFullImport, + NeedsSSARepr: needsSSARepr, } } func GetAllSupportedLinterConfigs() []LinterConfig { return []LinterConfig{ - enabledByDefault(golinters.Govet{}, "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string", false), - enabledByDefault(golinters.Errcheck{}, "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases", true), - enabledByDefault(golinters.Golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes", false), - enabledByDefault(golinters.Deadcode{}, "Finds unused code", true), - enabledByDefault(golinters.Gocyclo{}, "Computes and checks the cyclomatic complexity of functions", false), - enabledByDefault(golinters.Structcheck{}, "Finds unused struct fields", true), - enabledByDefault(golinters.Varcheck{}, "Finds unused global variables and constants", true), - enabledByDefault(golinters.Megacheck{}, "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused", true), - enabledByDefault(golinters.Dupl{}, "Tool for code clone detection", false), - enabledByDefault(golinters.Ineffassign{}, "Detects when assignments to existing variables are not used", false), + enabledByDefault(golinters.Govet{}, "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string", false, false), + enabledByDefault(golinters.Errcheck{}, "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases", true, false), + enabledByDefault(golinters.Golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes", false, false), + enabledByDefault(golinters.Deadcode{}, "Finds unused code", true, false), + enabledByDefault(golinters.Gocyclo{}, "Computes and checks the cyclomatic complexity of functions", false, false), + enabledByDefault(golinters.Structcheck{}, "Finds unused struct fields", true, false), + enabledByDefault(golinters.Varcheck{}, "Finds unused global variables and constants", true, false), + enabledByDefault(golinters.Megacheck{}, "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused", true, true), + enabledByDefault(golinters.Dupl{}, "Tool for code clone detection", false, false), + enabledByDefault(golinters.Ineffassign{}, "Detects when assignments to existing variables are not used", false, false), + enabledByDefault(golinters.Interfacer{}, "Linter that suggests narrower interface types", true, true), - disabledByDefault(golinters.Gofmt{}, "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification", false), - disabledByDefault(golinters.Gofmt{UseGoimports: true}, "Goimports does everything that gofmt does. Additionally it checks unused imports", false), - disabledByDefault(golinters.Maligned{}, "Tool to detect Go structs that would take less memory if their fields were sorted", true), + disabledByDefault(golinters.Gofmt{}, "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification", false, false), + disabledByDefault(golinters.Gofmt{UseGoimports: true}, "Goimports does everything that gofmt does. Additionally it checks unused imports", false, false), + disabledByDefault(golinters.Maligned{}, "Tool to detect Go structs that would take less memory if their fields were sorted", true, false), } } diff --git a/pkg/golinters/context.go b/pkg/golinters/context.go index dbc18797..2d692721 100644 --- a/pkg/golinters/context.go +++ b/pkg/golinters/context.go @@ -1,6 +1,7 @@ package golinters import ( + "github.com/golangci/go-tools/ssa" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/fsutils" "golang.org/x/tools/go/loader" @@ -10,6 +11,7 @@ type Context struct { Paths *fsutils.ProjectPaths Cfg *config.Config Program *loader.Program + SSAProgram *ssa.Program LoaderConfig *loader.Config } diff --git a/pkg/golinters/interfacer.go b/pkg/golinters/interfacer.go new file mode 100644 index 00000000..887d0941 --- /dev/null +++ b/pkg/golinters/interfacer.go @@ -0,0 +1,39 @@ +package golinters + +import ( + "context" + + "mvdan.cc/interfacer/check" + + "github.com/golangci/golangci-lint/pkg/result" +) + +type Interfacer struct{} + +func (Interfacer) Name() string { + return "interfacer" +} + +func (lint Interfacer) Run(ctx context.Context, lintCtx *Context) (*result.Result, error) { + c := new(check.Checker) + c.Program(lintCtx.Program) + c.ProgramSSA(lintCtx.SSAProgram) + + issues, err := c.Check() + if err != nil { + return nil, err + } + + res := &result.Result{} + for _, i := range issues { + pos := lintCtx.SSAProgram.Fset.Position(i.Pos()) + res.Issues = append(res.Issues, result.Issue{ + File: pos.Filename, + LineNumber: pos.Line, + Text: i.Message(), + FromLinter: lint.Name(), + }) + } + + return res, nil +} diff --git a/pkg/golinters/megacheck.go b/pkg/golinters/megacheck.go index 7ab24dd0..c88d7aee 100644 --- a/pkg/golinters/megacheck.go +++ b/pkg/golinters/megacheck.go @@ -15,7 +15,7 @@ func (Megacheck) Name() string { func (m Megacheck) Run(ctx context.Context, lintCtx *Context) (*result.Result, error) { c := lintCtx.RunCfg().Megacheck - issues := megacheckAPI.Run(lintCtx.Program, lintCtx.LoaderConfig, c.EnableStaticcheck, c.EnableGosimple, c.EnableUnused) + issues := megacheckAPI.Run(lintCtx.Program, lintCtx.LoaderConfig, lintCtx.SSAProgram, c.EnableStaticcheck, c.EnableGosimple, c.EnableUnused) res := &result.Result{} for _, i := range issues { diff --git a/pkg/golinters/testdata/interfacer.go b/pkg/golinters/testdata/interfacer.go new file mode 100644 index 00000000..36d05742 --- /dev/null +++ b/pkg/golinters/testdata/interfacer.go @@ -0,0 +1,7 @@ +package testdata + +import "io" + +func InterfacerCheck(f io.ReadCloser) { // ERROR "XXX" + f.Close() +} diff --git a/vendor/github.com/golangci/go-tools/cmd/megacheck/megacheck.go b/vendor/github.com/golangci/go-tools/cmd/megacheck/megacheck.go index a7834418..45b540d9 100644 --- a/vendor/github.com/golangci/go-tools/cmd/megacheck/megacheck.go +++ b/vendor/github.com/golangci/go-tools/cmd/megacheck/megacheck.go @@ -5,12 +5,13 @@ import ( "github.com/golangci/go-tools/lint" "github.com/golangci/go-tools/lint/lintutil" "github.com/golangci/go-tools/simple" + "github.com/golangci/go-tools/ssa" "github.com/golangci/go-tools/staticcheck" "github.com/golangci/go-tools/unused" "golang.org/x/tools/go/loader" ) -func Run(program *loader.Program, conf *loader.Config, enableStaticcheck, enableGosimple, enableUnused bool) []lint.Problem { +func Run(program *loader.Program, conf *loader.Config, ssaProg *ssa.Program, enableStaticcheck, enableGosimple, enableUnused bool) []lint.Problem { var flags struct { staticcheck struct { enabled bool @@ -120,5 +121,5 @@ func Run(program *loader.Program, conf *loader.Config, enableStaticcheck, enable } - return lintutil.ProcessFlagSet(checkers, fs, program, conf) + return lintutil.ProcessFlagSet(checkers, fs, program, conf, ssaProg) } diff --git a/vendor/github.com/golangci/go-tools/lint/lint.go b/vendor/github.com/golangci/go-tools/lint/lint.go index aad75b84..2fb7983f 100644 --- a/vendor/github.com/golangci/go-tools/lint/lint.go +++ b/vendor/github.com/golangci/go-tools/lint/lint.go @@ -23,10 +23,10 @@ import ( "sync" "unicode" - "github.com/golangci/go-tools/ssa" - "github.com/golangci/go-tools/ssa/ssautil" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/loader" + "github.com/golangci/go-tools/ssa" + "github.com/golangci/go-tools/ssa/ssautil" ) type Job struct { @@ -235,9 +235,7 @@ func parseDirective(s string) (cmd string, args []string) { return fields[0], fields[1:] } -func (l *Linter) Lint(lprog *loader.Program, conf *loader.Config) []Problem { - ssaprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - ssaprog.Build() +func (l *Linter) Lint(lprog *loader.Program, conf *loader.Config, ssaprog *ssa.Program) []Problem { pkgMap := map[*ssa.Package]*Pkg{} var pkgs []*Pkg for _, pkginfo := range lprog.InitialPackages() { @@ -836,9 +834,6 @@ func (v *fnVisitor) Visit(node ast.Node) ast.Visitor { switch node := node.(type) { case *ast.FuncDecl: var ssafn *ssa.Function - if v.pkg == nil || v.pkg.Prog == nil { - return nil // partially loaded - } ssafn = v.pkg.Prog.FuncValue(v.pkg.Info.ObjectOf(node.Name).(*types.Func)) v.m[node] = ssafn if ssafn == nil { diff --git a/vendor/github.com/golangci/go-tools/lint/lintutil/util.go b/vendor/github.com/golangci/go-tools/lint/lintutil/util.go index ec32be0e..bd8dc62a 100644 --- a/vendor/github.com/golangci/go-tools/lint/lintutil/util.go +++ b/vendor/github.com/golangci/go-tools/lint/lintutil/util.go @@ -23,6 +23,7 @@ import ( "github.com/golangci/go-tools/lint" "github.com/golangci/go-tools/version" + "github.com/golangci/go-tools/ssa" "golang.org/x/tools/go/loader" ) @@ -182,7 +183,7 @@ type CheckerConfig struct { ExitNonZero bool } -func ProcessFlagSet(confs []CheckerConfig, fs *flag.FlagSet, lprog *loader.Program, conf *loader.Config) []lint.Problem { +func ProcessFlagSet(confs []CheckerConfig, fs *flag.FlagSet, lprog *loader.Program, conf *loader.Config, ssaProg *ssa.Program) []lint.Problem { 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) @@ -199,7 +200,7 @@ func ProcessFlagSet(confs []CheckerConfig, fs *flag.FlagSet, lprog *loader.Progr for _, conf := range confs { cs = append(cs, conf.Checker) } - pss, err := Lint(cs, lprog, conf, &Options{ + pss, err := Lint(cs, lprog, conf, ssaProg, &Options{ Tags: strings.Fields(tags), LintTests: tests, Ignores: ignore, @@ -226,7 +227,7 @@ type Options struct { ReturnIgnored bool } -func Lint(cs []lint.Checker, lprog *loader.Program, conf *loader.Config, opt *Options) ([][]lint.Problem, error) { +func Lint(cs []lint.Checker, lprog *loader.Program, conf *loader.Config, ssaProg *ssa.Program, opt *Options) ([][]lint.Problem, error) { if opt == nil { opt = &Options{} } @@ -244,7 +245,7 @@ func Lint(cs []lint.Checker, lprog *loader.Program, conf *loader.Config, opt *Op version: opt.GoVersion, returnIgnored: opt.ReturnIgnored, } - problems = append(problems, runner.lint(lprog, conf)) + problems = append(problems, runner.lint(lprog, conf, ssaProg)) } return problems, nil } @@ -278,15 +279,15 @@ func ProcessArgs(name string, cs []CheckerConfig, args []string) { flags := FlagSet(name) flags.Parse(args) - ProcessFlagSet(cs, flags, nil, nil) + ProcessFlagSet(cs, flags, nil, nil, nil) } -func (runner *runner) lint(lprog *loader.Program, conf *loader.Config) []lint.Problem { +func (runner *runner) lint(lprog *loader.Program, conf *loader.Config, ssaProg *ssa.Program) []lint.Problem { l := &lint.Linter{ Checker: runner.checker, Ignores: runner.ignores, GoVersion: runner.version, ReturnIgnored: runner.returnIgnored, } - return l.Lint(lprog, conf) + return l.Lint(lprog, conf, ssaProg) } diff --git a/vendor/github.com/kisielk/gotool/.travis.yml b/vendor/github.com/kisielk/gotool/.travis.yml new file mode 100644 index 00000000..d1784e1e --- /dev/null +++ b/vendor/github.com/kisielk/gotool/.travis.yml @@ -0,0 +1,23 @@ +sudo: false +language: go +go: + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - master +matrix: + allow_failures: + - go: master + fast_finish: true +install: + - # Skip. +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/kisielk/gotool/LEGAL b/vendor/github.com/kisielk/gotool/LEGAL new file mode 100644 index 00000000..72b859cd --- /dev/null +++ b/vendor/github.com/kisielk/gotool/LEGAL @@ -0,0 +1,32 @@ +All the files in this distribution are covered under either the MIT +license (see the file LICENSE) except some files mentioned below. + +match.go, match_test.go: + + 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. diff --git a/vendor/github.com/kisielk/gotool/LICENSE b/vendor/github.com/kisielk/gotool/LICENSE new file mode 100644 index 00000000..1cbf651e --- /dev/null +++ b/vendor/github.com/kisielk/gotool/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Kamil Kisiel + +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. diff --git a/vendor/github.com/kisielk/gotool/README.md b/vendor/github.com/kisielk/gotool/README.md new file mode 100644 index 00000000..6e4e92b2 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/README.md @@ -0,0 +1,6 @@ +gotool +====== +[![GoDoc](https://godoc.org/github.com/kisielk/gotool?status.svg)](https://godoc.org/github.com/kisielk/gotool) +[![Build Status](https://travis-ci.org/kisielk/gotool.svg?branch=master)](https://travis-ci.org/kisielk/gotool) + +Package gotool contains utility functions used to implement the standard "cmd/go" tool, provided as a convenience to developers who want to write tools with similar semantics. diff --git a/vendor/github.com/kisielk/gotool/go.mod b/vendor/github.com/kisielk/gotool/go.mod new file mode 100644 index 00000000..503b37c6 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/go.mod @@ -0,0 +1 @@ +module "github.com/kisielk/gotool" diff --git a/vendor/github.com/kisielk/gotool/go13.go b/vendor/github.com/kisielk/gotool/go13.go new file mode 100644 index 00000000..2dd9b3fd --- /dev/null +++ b/vendor/github.com/kisielk/gotool/go13.go @@ -0,0 +1,15 @@ +// +build !go1.4 + +package gotool + +import ( + "go/build" + "path/filepath" + "runtime" +) + +var gorootSrc = filepath.Join(runtime.GOROOT(), "src", "pkg") + +func shouldIgnoreImport(p *build.Package) bool { + return true +} diff --git a/vendor/github.com/kisielk/gotool/go14-15.go b/vendor/github.com/kisielk/gotool/go14-15.go new file mode 100644 index 00000000..aa99a322 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/go14-15.go @@ -0,0 +1,15 @@ +// +build go1.4,!go1.6 + +package gotool + +import ( + "go/build" + "path/filepath" + "runtime" +) + +var gorootSrc = filepath.Join(runtime.GOROOT(), "src") + +func shouldIgnoreImport(p *build.Package) bool { + return true +} diff --git a/vendor/github.com/kisielk/gotool/go16-18.go b/vendor/github.com/kisielk/gotool/go16-18.go new file mode 100644 index 00000000..f25cec14 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/go16-18.go @@ -0,0 +1,15 @@ +// +build go1.6,!go1.9 + +package gotool + +import ( + "go/build" + "path/filepath" + "runtime" +) + +var gorootSrc = filepath.Join(runtime.GOROOT(), "src") + +func shouldIgnoreImport(p *build.Package) bool { + return p == nil || len(p.InvalidGoFiles) == 0 +} diff --git a/vendor/github.com/kisielk/gotool/internal/load/path.go b/vendor/github.com/kisielk/gotool/internal/load/path.go new file mode 100644 index 00000000..74e15b9d --- /dev/null +++ b/vendor/github.com/kisielk/gotool/internal/load/path.go @@ -0,0 +1,27 @@ +// Copyright 2017 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. + +// +build go1.9 + +package load + +import ( + "strings" +) + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} diff --git a/vendor/github.com/kisielk/gotool/internal/load/pkg.go b/vendor/github.com/kisielk/gotool/internal/load/pkg.go new file mode 100644 index 00000000..b937ede7 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/internal/load/pkg.go @@ -0,0 +1,25 @@ +// Copyright 2011 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. + +// +build go1.9 + +// Package load loads packages. +package load + +import ( + "strings" +) + +// isStandardImportPath reports whether $GOROOT/src/path should be considered +// part of the standard distribution. For historical reasons we allow people to add +// their own code to $GOROOT instead of using $GOPATH, but we assume that +// code will start with a domain name (dot in the first element). +func isStandardImportPath(path string) bool { + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + elem := path[:i] + return !strings.Contains(elem, ".") +} diff --git a/vendor/github.com/kisielk/gotool/internal/load/search.go b/vendor/github.com/kisielk/gotool/internal/load/search.go new file mode 100644 index 00000000..17ed62dd --- /dev/null +++ b/vendor/github.com/kisielk/gotool/internal/load/search.go @@ -0,0 +1,354 @@ +// Copyright 2017 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. + +// +build go1.9 + +package load + +import ( + "fmt" + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "strings" +) + +// Context specifies values for operation of ImportPaths that would +// otherwise come from cmd/go/internal/cfg package. +// +// This is a construct added for gotool purposes and doesn't have +// an equivalent upstream in cmd/go. +type Context struct { + // BuildContext is the build context to use. + BuildContext build.Context + + // GOROOTsrc is the location of the src directory in GOROOT. + // At this time, it's used only in MatchPackages to skip + // GOOROOT/src entry from BuildContext.SrcDirs output. + GOROOTsrc string +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages), +// "cmd" (standard commands), or a path including "...". +func (c *Context) allPackages(pattern string) []string { + pkgs := c.MatchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +func (c *Context) allPackagesInFS(pattern string) []string { + pkgs := c.MatchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// MatchPackages returns a list of package paths matching pattern +// (see go help packages for pattern syntax). +func (c *Context) MatchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !IsMetaPackage(pattern) { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !c.BuildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + for _, src := range c.BuildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != c.GOROOTsrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || path == src { + return nil + } + + want := true + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + want = false + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + want = false + } + if !treeCanMatch(name) { + want = false + } + + if !fi.IsDir() { + if fi.Mode()&os.ModeSymlink != 0 && want { + if target, err := os.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + if !want { + return filepath.SkipDir + } + + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + pkg, err := c.BuildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + + // If we are expanding "cmd", skip main + // packages under cmd/vendor. At least as of + // March, 2017, there is one there for the + // vendored pprof tool. + if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" { + return nil + } + + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// MatchPackagesInFS returns a list of package paths matching pattern, +// which must begin with ./ or ../ +// (see go help packages for pattern syntax). +func (c *Context) MatchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + + // We keep the directory if we can import it, or if we can't import it + // due to invalid Go source files. This means that directories containing + // parse errors will be built (and fail) instead of being silently skipped + // as not matching the pattern. Go 1.5 and earlier skipped, but that + // behavior means people miss serious mistakes. + // See golang.org/issue/11407. + if p, err := c.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +// Unfortunately, there are two special cases. Quoting "go help packages": +// +// First, /... at the end of the pattern can match an empty string, +// so that net/... matches both net and packages in its subdirectories, like net/http. +// Second, any slash-separted pattern element containing a wildcard never +// participates in a match of the "vendor" element in the path of a vendored +// package, so that ./... does not match packages in subdirectories of +// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. +// Note, however, that a directory named vendor that itself contains code +// is not a vendored package: cmd/vendor would be a command named vendor, +// and the pattern cmd/... matches it. +func matchPattern(pattern string) func(name string) bool { + // Convert pattern to regular expression. + // The strategy for the trailing /... is to nest it in an explicit ? expression. + // The strategy for the vendor exclusion is to change the unmatchable + // vendor strings to a disallowed code point (vendorChar) and to use + // "(anything but that codepoint)*" as the implementation of the ... wildcard. + // This is a bit complicated but the obvious alternative, + // namely a hand-written search like in most shell glob matchers, + // is too easy to make accidentally exponential. + // Using package regexp guarantees linear-time matching. + + const vendorChar = "\x00" + + if strings.Contains(pattern, vendorChar) { + return func(name string) bool { return false } + } + + re := regexp.QuoteMeta(pattern) + re = replaceVendor(re, vendorChar) + switch { + case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): + re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` + case re == vendorChar+`/\.\.\.`: + re = `(/vendor|/` + vendorChar + `/\.\.\.)` + case strings.HasSuffix(re, `/\.\.\.`): + re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` + } + re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1) + + reg := regexp.MustCompile(`^` + re + `$`) + + return func(name string) bool { + if strings.Contains(name, vendorChar) { + return false + } + return reg.MatchString(replaceVendor(name, vendorChar)) + } +} + +// replaceVendor returns the result of replacing +// non-trailing vendor path elements in x with repl. +func replaceVendor(x, repl string) string { + if !strings.Contains(x, "vendor") { + return x + } + elem := strings.Split(x, "/") + for i := 0; i < len(elem)-1; i++ { + if elem[i] == "vendor" { + elem[i] = repl + } + } + return strings.Join(elem, "/") +} + +// ImportPaths returns the import paths to use for the given command line. +func (c *Context) ImportPaths(args []string) []string { + args = c.ImportPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, c.allPackagesInFS(a)...) + } else { + out = append(out, c.allPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// ImportPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func (c *Context) ImportPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if IsMetaPackage(a) { + out = append(out, c.allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// IsMetaPackage checks if name is a reserved package name that expands to multiple packages. +func IsMetaPackage(name string) bool { + return name == "std" || name == "cmd" || name == "all" +} diff --git a/vendor/github.com/kisielk/gotool/match.go b/vendor/github.com/kisielk/gotool/match.go new file mode 100644 index 00000000..4dbdbff4 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/match.go @@ -0,0 +1,56 @@ +// 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. + +// +build go1.9 + +package gotool + +import ( + "path/filepath" + + "github.com/kisielk/gotool/internal/load" +) + +// importPaths returns the import paths to use for the given command line. +func (c *Context) importPaths(args []string) []string { + lctx := load.Context{ + BuildContext: c.BuildContext, + GOROOTsrc: c.joinPath(c.BuildContext.GOROOT, "src"), + } + return lctx.ImportPaths(args) +} + +// joinPath calls c.BuildContext.JoinPath (if not nil) or else filepath.Join. +// +// It's a copy of the unexported build.Context.joinPath helper. +func (c *Context) joinPath(elem ...string) string { + if f := c.BuildContext.JoinPath; f != nil { + return f(elem...) + } + return filepath.Join(elem...) +} diff --git a/vendor/github.com/kisielk/gotool/match18.go b/vendor/github.com/kisielk/gotool/match18.go new file mode 100644 index 00000000..6d6b1368 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/match18.go @@ -0,0 +1,317 @@ +// 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. + +// +build !go1.9 + +package gotool + +import ( + "fmt" + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "strings" +) + +// This file contains code from the Go distribution. + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return reg.MatchString +} + +// matchPackages returns a list of package paths matching pattern +// (see go help packages for pattern syntax). +func (c *Context) matchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if !isMetaPackage(pattern) { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !c.BuildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + for _, src := range c.BuildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != gorootSrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + return filepath.SkipDir + } + if !treeCanMatch(name) { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = c.BuildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func (c *Context) importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if isMetaPackage(a) { + out = append(out, c.allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func (c *Context) importPaths(args []string) []string { + args = c.importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, c.allPackagesInFS(a)...) + } else { + out = append(out, c.allPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages), +// "cmd" (standard commands), or a path including "...". +func (c *Context) allPackages(pattern string) []string { + pkgs := c.matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +func (c *Context) allPackagesInFS(pattern string) []string { + pkgs := c.matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +// matchPackagesInFS returns a list of package paths matching pattern, +// which must begin with ./ or ../ +// (see go help packages for pattern syntax). +func (c *Context) matchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + + // We keep the directory if we can import it, or if we can't import it + // due to invalid Go source files. This means that directories containing + // parse errors will be built (and fail) instead of being silently skipped + // as not matching the pattern. Go 1.5 and earlier skipped, but that + // behavior means people miss serious mistakes. + // See golang.org/issue/11407. + if p, err := c.BuildContext.ImportDir(path, 0); err != nil && shouldIgnoreImport(p) { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} + +// isMetaPackage checks if name is a reserved package name that expands to multiple packages. +func isMetaPackage(name string) bool { + return name == "std" || name == "cmd" || name == "all" +} + +// isStandardImportPath reports whether $GOROOT/src/path should be considered +// part of the standard distribution. For historical reasons we allow people to add +// their own code to $GOROOT instead of using $GOPATH, but we assume that +// code will start with a domain name (dot in the first element). +func isStandardImportPath(path string) bool { + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + elem := path[:i] + return !strings.Contains(elem, ".") +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} diff --git a/vendor/github.com/kisielk/gotool/tool.go b/vendor/github.com/kisielk/gotool/tool.go new file mode 100644 index 00000000..c7409e11 --- /dev/null +++ b/vendor/github.com/kisielk/gotool/tool.go @@ -0,0 +1,48 @@ +// Package gotool contains utility functions used to implement the standard +// "cmd/go" tool, provided as a convenience to developers who want to write +// tools with similar semantics. +package gotool + +import "go/build" + +// Export functions here to make it easier to keep the implementations up to date with upstream. + +// DefaultContext is the default context that uses build.Default. +var DefaultContext = Context{ + BuildContext: build.Default, +} + +// A Context specifies the supporting context. +type Context struct { + // BuildContext is the build.Context that is used when computing import paths. + BuildContext build.Context +} + +// ImportPaths returns the import paths to use for the given command line. +// +// The path "all" is expanded to all packages in $GOPATH and $GOROOT. +// The path "std" is expanded to all packages in the Go standard library. +// The path "cmd" is expanded to all Go standard commands. +// The string "..." is treated as a wildcard within a path. +// When matching recursively, directories are ignored if they are prefixed with +// a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata". +// Relative import paths are not converted to full import paths. +// If args is empty, a single element "." is returned. +func (c *Context) ImportPaths(args []string) []string { + return c.importPaths(args) +} + +// ImportPaths returns the import paths to use for the given command line +// using default context. +// +// The path "all" is expanded to all packages in $GOPATH and $GOROOT. +// The path "std" is expanded to all packages in the Go standard library. +// The path "cmd" is expanded to all Go standard commands. +// The string "..." is treated as a wildcard within a path. +// When matching recursively, directories are ignored if they are prefixed with +// a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata". +// Relative import paths are not converted to full import paths. +// If args is empty, a single element "." is returned. +func ImportPaths(args []string) []string { + return DefaultContext.importPaths(args) +} diff --git a/vendor/mvdan.cc/interfacer/LICENSE b/vendor/mvdan.cc/interfacer/LICENSE new file mode 100644 index 00000000..7d71d51a --- /dev/null +++ b/vendor/mvdan.cc/interfacer/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, Daniel Martí. 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 the copyright holder 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. diff --git a/vendor/mvdan.cc/interfacer/check/cache.go b/vendor/mvdan.cc/interfacer/check/cache.go new file mode 100644 index 00000000..757eca55 --- /dev/null +++ b/vendor/mvdan.cc/interfacer/check/cache.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015, Daniel Martí +// See LICENSE for licensing information + +package check + +import ( + "go/ast" + "go/types" +) + +type pkgTypes struct { + ifaces map[string]string + funcSigns map[string]bool +} + +func (p *pkgTypes) getTypes(pkg *types.Package) { + p.ifaces = make(map[string]string) + p.funcSigns = make(map[string]bool) + done := make(map[*types.Package]bool) + addTypes := func(pkg *types.Package, top bool) { + if done[pkg] { + return + } + done[pkg] = true + ifs, funs := fromScope(pkg.Scope()) + fullName := func(name string) string { + if !top { + return pkg.Path() + "." + name + } + return name + } + for iftype, name := range ifs { + // only suggest exported interfaces + if ast.IsExported(name) { + p.ifaces[iftype] = fullName(name) + } + } + for ftype := range funs { + // ignore non-exported func signatures too + p.funcSigns[ftype] = true + } + } + for _, imp := range pkg.Imports() { + addTypes(imp, false) + for _, imp2 := range imp.Imports() { + addTypes(imp2, false) + } + } + addTypes(pkg, true) +} diff --git a/vendor/mvdan.cc/interfacer/check/check.go b/vendor/mvdan.cc/interfacer/check/check.go new file mode 100644 index 00000000..5d930e35 --- /dev/null +++ b/vendor/mvdan.cc/interfacer/check/check.go @@ -0,0 +1,462 @@ +// Copyright (c) 2015, Daniel Martí +// See LICENSE for licensing information + +package check // import "mvdan.cc/interfacer/check" + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "os" + "strings" + + "github.com/golangci/go-tools/ssa" + "github.com/golangci/go-tools/ssa/ssautil" + "golang.org/x/tools/go/loader" + + "github.com/kisielk/gotool" + "mvdan.cc/lint" +) + +func toDiscard(usage *varUsage) bool { + if usage.discard { + return true + } + for to := range usage.assigned { + if toDiscard(to) { + return true + } + } + return false +} + +func allCalls(usage *varUsage, all, ftypes map[string]string) { + for fname := range usage.calls { + all[fname] = ftypes[fname] + } + for to := range usage.assigned { + allCalls(to, all, ftypes) + } +} + +func (c *Checker) interfaceMatching(param *types.Var, usage *varUsage) (string, string) { + if toDiscard(usage) { + return "", "" + } + ftypes := typeFuncMap(param.Type()) + called := make(map[string]string, len(usage.calls)) + allCalls(usage, called, ftypes) + s := funcMapString(called) + return c.ifaces[s], s +} + +type varUsage struct { + calls map[string]struct{} + discard bool + + assigned map[*varUsage]struct{} +} + +type funcDecl struct { + astDecl *ast.FuncDecl + ssaFn *ssa.Function +} + +// CheckArgs checks the packages specified by their import paths in +// args. +func CheckArgs(args []string) ([]string, error) { + paths := gotool.ImportPaths(args) + conf := loader.Config{} + conf.AllowErrors = true + rest, err := conf.FromArgs(paths, false) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, fmt.Errorf("unwanted extra args: %v", rest) + } + lprog, err := conf.Load() + if err != nil { + return nil, err + } + prog := ssautil.CreateProgram(lprog, 0) + prog.Build() + c := new(Checker) + c.Program(lprog) + c.ProgramSSA(prog) + issues, err := c.Check() + if err != nil { + return nil, err + } + wd, err := os.Getwd() + if err != nil { + return nil, err + } + lines := make([]string, len(issues)) + for i, issue := range issues { + fpos := prog.Fset.Position(issue.Pos()).String() + if strings.HasPrefix(fpos, wd) { + fpos = fpos[len(wd)+1:] + } + lines[i] = fmt.Sprintf("%s: %s", fpos, issue.Message()) + } + return lines, nil +} + +type Checker struct { + lprog *loader.Program + prog *ssa.Program + + pkgTypes + *loader.PackageInfo + + funcs []*funcDecl + + ssaByPos map[token.Pos]*ssa.Function + + discardFuncs map[*types.Signature]struct{} + + vars map[*types.Var]*varUsage +} + +var ( + _ lint.Checker = (*Checker)(nil) + _ lint.WithSSA = (*Checker)(nil) +) + +func (c *Checker) Program(lprog *loader.Program) { + c.lprog = lprog +} + +func (c *Checker) ProgramSSA(prog *ssa.Program) { + c.prog = prog +} + +func (c *Checker) Check() ([]lint.Issue, error) { + var total []lint.Issue + c.ssaByPos = make(map[token.Pos]*ssa.Function) + wantPkg := make(map[*types.Package]bool) + for _, pinfo := range c.lprog.InitialPackages() { + wantPkg[pinfo.Pkg] = true + } + for fn := range ssautil.AllFunctions(c.prog) { + if fn.Pkg == nil { // builtin? + continue + } + if len(fn.Blocks) == 0 { // stub + continue + } + if !wantPkg[fn.Pkg.Pkg] { // not part of given pkgs + continue + } + c.ssaByPos[fn.Pos()] = fn + } + for _, pinfo := range c.lprog.InitialPackages() { + pkg := pinfo.Pkg + c.getTypes(pkg) + c.PackageInfo = c.lprog.AllPackages[pkg] + total = append(total, c.checkPkg()...) + } + return total, nil +} + +func (c *Checker) checkPkg() []lint.Issue { + c.discardFuncs = make(map[*types.Signature]struct{}) + c.vars = make(map[*types.Var]*varUsage) + c.funcs = c.funcs[:0] + findFuncs := func(node ast.Node) bool { + decl, ok := node.(*ast.FuncDecl) + if !ok { + return true + } + ssaFn := c.ssaByPos[decl.Name.Pos()] + if ssaFn == nil { + return true + } + fd := &funcDecl{ + astDecl: decl, + ssaFn: ssaFn, + } + if c.funcSigns[signString(fd.ssaFn.Signature)] { + // implements interface + return true + } + c.funcs = append(c.funcs, fd) + ast.Walk(c, decl.Body) + return true + } + for _, f := range c.Files { + ast.Inspect(f, findFuncs) + } + return c.packageIssues() +} + +func paramVarAndType(sign *types.Signature, i int) (*types.Var, types.Type) { + params := sign.Params() + extra := sign.Variadic() && i >= params.Len()-1 + if !extra { + if i >= params.Len() { + // builtins with multiple signatures + return nil, nil + } + vr := params.At(i) + return vr, vr.Type() + } + last := params.At(params.Len() - 1) + switch x := last.Type().(type) { + case *types.Slice: + return nil, x.Elem() + default: + return nil, x + } +} + +func (c *Checker) varUsage(e ast.Expr) *varUsage { + id, ok := e.(*ast.Ident) + if !ok { + return nil + } + param, ok := c.ObjectOf(id).(*types.Var) + if !ok { + // not a variable + return nil + } + if usage, e := c.vars[param]; e { + return usage + } + if !interesting(param.Type()) { + return nil + } + usage := &varUsage{ + calls: make(map[string]struct{}), + assigned: make(map[*varUsage]struct{}), + } + c.vars[param] = usage + return usage +} + +func (c *Checker) addUsed(e ast.Expr, as types.Type) { + if as == nil { + return + } + if usage := c.varUsage(e); usage != nil { + // using variable + iface, ok := as.Underlying().(*types.Interface) + if !ok { + usage.discard = true + return + } + for i := 0; i < iface.NumMethods(); i++ { + m := iface.Method(i) + usage.calls[m.Name()] = struct{}{} + } + } else if t, ok := c.TypeOf(e).(*types.Signature); ok { + // using func + c.discardFuncs[t] = struct{}{} + } +} + +func (c *Checker) addAssign(to, from ast.Expr) { + pto := c.varUsage(to) + pfrom := c.varUsage(from) + if pto == nil || pfrom == nil { + // either isn't interesting + return + } + pfrom.assigned[pto] = struct{}{} +} + +func (c *Checker) discard(e ast.Expr) { + if usage := c.varUsage(e); usage != nil { + usage.discard = true + } +} + +func (c *Checker) comparedWith(e, with ast.Expr) { + if _, ok := with.(*ast.BasicLit); ok { + c.discard(e) + } +} + +func (c *Checker) Visit(node ast.Node) ast.Visitor { + switch x := node.(type) { + case *ast.SelectorExpr: + if _, ok := c.TypeOf(x.Sel).(*types.Signature); !ok { + c.discard(x.X) + } + case *ast.StarExpr: + c.discard(x.X) + case *ast.UnaryExpr: + c.discard(x.X) + case *ast.IndexExpr: + c.discard(x.X) + case *ast.IncDecStmt: + c.discard(x.X) + case *ast.BinaryExpr: + switch x.Op { + case token.EQL, token.NEQ: + c.comparedWith(x.X, x.Y) + c.comparedWith(x.Y, x.X) + default: + c.discard(x.X) + c.discard(x.Y) + } + case *ast.ValueSpec: + for _, val := range x.Values { + c.addUsed(val, c.TypeOf(x.Type)) + } + case *ast.AssignStmt: + for i, val := range x.Rhs { + left := x.Lhs[i] + if x.Tok == token.ASSIGN { + c.addUsed(val, c.TypeOf(left)) + } + c.addAssign(left, val) + } + case *ast.CompositeLit: + for i, e := range x.Elts { + switch y := e.(type) { + case *ast.KeyValueExpr: + c.addUsed(y.Key, c.TypeOf(y.Value)) + c.addUsed(y.Value, c.TypeOf(y.Key)) + case *ast.Ident: + c.addUsed(y, compositeIdentType(c.TypeOf(x), i)) + } + } + case *ast.CallExpr: + switch y := c.TypeOf(x.Fun).Underlying().(type) { + case *types.Signature: + c.onMethodCall(x, y) + default: + // type conversion + if len(x.Args) == 1 { + c.addUsed(x.Args[0], y) + } + } + } + return c +} + +func compositeIdentType(t types.Type, i int) types.Type { + switch x := t.(type) { + case *types.Named: + return compositeIdentType(x.Underlying(), i) + case *types.Struct: + return x.Field(i).Type() + case *types.Array: + return x.Elem() + case *types.Slice: + return x.Elem() + } + return nil +} + +func (c *Checker) onMethodCall(ce *ast.CallExpr, sign *types.Signature) { + for i, e := range ce.Args { + paramObj, t := paramVarAndType(sign, i) + // Don't if this is a parameter being re-used as itself + // in a recursive call + if id, ok := e.(*ast.Ident); ok { + if paramObj == c.ObjectOf(id) { + continue + } + } + c.addUsed(e, t) + } + sel, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return + } + // receiver func call on the left side + if usage := c.varUsage(sel.X); usage != nil { + usage.calls[sel.Sel.Name] = struct{}{} + } +} + +func (fd *funcDecl) paramGroups() [][]*types.Var { + astList := fd.astDecl.Type.Params.List + groups := make([][]*types.Var, len(astList)) + signIndex := 0 + for i, field := range astList { + group := make([]*types.Var, len(field.Names)) + for j := range field.Names { + group[j] = fd.ssaFn.Signature.Params().At(signIndex) + signIndex++ + } + groups[i] = group + } + return groups +} + +func (c *Checker) packageIssues() []lint.Issue { + var issues []lint.Issue + for _, fd := range c.funcs { + if _, e := c.discardFuncs[fd.ssaFn.Signature]; e { + continue + } + for _, group := range fd.paramGroups() { + issues = append(issues, c.groupIssues(fd, group)...) + } + } + return issues +} + +type Issue struct { + pos token.Pos + msg string +} + +func (i Issue) Pos() token.Pos { return i.pos } +func (i Issue) Message() string { return i.msg } + +func (c *Checker) groupIssues(fd *funcDecl, group []*types.Var) []lint.Issue { + var issues []lint.Issue + for _, param := range group { + usage := c.vars[param] + if usage == nil { + return nil + } + newType := c.paramNewType(fd.astDecl.Name.Name, param, usage) + if newType == "" { + return nil + } + issues = append(issues, Issue{ + pos: param.Pos(), + msg: fmt.Sprintf("%s can be %s", param.Name(), newType), + }) + } + return issues +} + +func willAddAllocation(t types.Type) bool { + switch t.Underlying().(type) { + case *types.Pointer, *types.Interface: + return false + } + return true +} + +func (c *Checker) paramNewType(funcName string, param *types.Var, usage *varUsage) string { + t := param.Type() + if !ast.IsExported(funcName) && willAddAllocation(t) { + return "" + } + if named := typeNamed(t); named != nil { + tname := named.Obj().Name() + vname := param.Name() + if mentionsName(funcName, tname) || mentionsName(funcName, vname) { + return "" + } + } + ifname, iftype := c.interfaceMatching(param, usage) + if ifname == "" { + return "" + } + if types.IsInterface(t.Underlying()) { + if have := funcMapString(typeFuncMap(t)); have == iftype { + return "" + } + } + return ifname +} diff --git a/vendor/mvdan.cc/interfacer/check/types.go b/vendor/mvdan.cc/interfacer/check/types.go new file mode 100644 index 00000000..393bb0b9 --- /dev/null +++ b/vendor/mvdan.cc/interfacer/check/types.go @@ -0,0 +1,170 @@ +// Copyright (c) 2015, Daniel Martí +// See LICENSE for licensing information + +package check + +import ( + "bytes" + "fmt" + "go/types" + "sort" + "strings" +) + +type methoder interface { + NumMethods() int + Method(int) *types.Func +} + +func methoderFuncMap(m methoder, skip bool) map[string]string { + ifuncs := make(map[string]string, m.NumMethods()) + for i := 0; i < m.NumMethods(); i++ { + f := m.Method(i) + if !f.Exported() { + if skip { + continue + } + return nil + } + sign := f.Type().(*types.Signature) + ifuncs[f.Name()] = signString(sign) + } + return ifuncs +} + +func typeFuncMap(t types.Type) map[string]string { + switch x := t.(type) { + case *types.Pointer: + return typeFuncMap(x.Elem()) + case *types.Named: + u := x.Underlying() + if types.IsInterface(u) { + return typeFuncMap(u) + } + return methoderFuncMap(x, true) + case *types.Interface: + return methoderFuncMap(x, false) + default: + return nil + } +} + +func funcMapString(iface map[string]string) string { + fnames := make([]string, 0, len(iface)) + for fname := range iface { + fnames = append(fnames, fname) + } + sort.Strings(fnames) + var b bytes.Buffer + for i, fname := range fnames { + if i > 0 { + fmt.Fprint(&b, "; ") + } + fmt.Fprint(&b, fname, iface[fname]) + } + return b.String() +} + +func tupleJoin(buf *bytes.Buffer, t *types.Tuple) { + buf.WriteByte('(') + for i := 0; i < t.Len(); i++ { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(t.At(i).Type().String()) + } + buf.WriteByte(')') +} + +// signString is similar to Signature.String(), but it ignores +// param/result names. +func signString(sign *types.Signature) string { + var buf bytes.Buffer + tupleJoin(&buf, sign.Params()) + tupleJoin(&buf, sign.Results()) + return buf.String() +} + +func interesting(t types.Type) bool { + switch x := t.(type) { + case *types.Interface: + return x.NumMethods() > 1 + case *types.Named: + if u := x.Underlying(); types.IsInterface(u) { + return interesting(u) + } + return x.NumMethods() >= 1 + case *types.Pointer: + return interesting(x.Elem()) + default: + return false + } +} + +func anyInteresting(params *types.Tuple) bool { + for i := 0; i < params.Len(); i++ { + t := params.At(i).Type() + if interesting(t) { + return true + } + } + return false +} + +func fromScope(scope *types.Scope) (ifaces map[string]string, funcs map[string]bool) { + ifaces = make(map[string]string) + funcs = make(map[string]bool) + for _, name := range scope.Names() { + tn, ok := scope.Lookup(name).(*types.TypeName) + if !ok { + continue + } + switch x := tn.Type().Underlying().(type) { + case *types.Interface: + iface := methoderFuncMap(x, false) + if len(iface) == 0 { + continue + } + for i := 0; i < x.NumMethods(); i++ { + f := x.Method(i) + sign := f.Type().(*types.Signature) + if !anyInteresting(sign.Params()) { + continue + } + funcs[signString(sign)] = true + } + s := funcMapString(iface) + if _, e := ifaces[s]; !e { + ifaces[s] = tn.Name() + } + case *types.Signature: + if !anyInteresting(x.Params()) { + continue + } + funcs[signString(x)] = true + } + } + return ifaces, funcs +} + +func mentionsName(fname, name string) bool { + if len(name) < 2 { + return false + } + capit := strings.ToUpper(name[:1]) + name[1:] + lower := strings.ToLower(name) + return strings.Contains(fname, capit) || strings.HasPrefix(fname, lower) +} + +func typeNamed(t types.Type) *types.Named { + for { + switch x := t.(type) { + case *types.Named: + return x + case *types.Pointer: + t = x.Elem() + default: + return nil + } + } +} diff --git a/vendor/mvdan.cc/lint/.travis.yml b/vendor/mvdan.cc/lint/.travis.yml new file mode 100644 index 00000000..2ccdeab9 --- /dev/null +++ b/vendor/mvdan.cc/lint/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.8.x + - 1.9.x + +go_import_path: mvdan.cc/lint diff --git a/vendor/mvdan.cc/lint/LICENSE b/vendor/mvdan.cc/lint/LICENSE new file mode 100644 index 00000000..a06c5ebf --- /dev/null +++ b/vendor/mvdan.cc/lint/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017, Daniel Martí. 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 the copyright holder 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. diff --git a/vendor/mvdan.cc/lint/README.md b/vendor/mvdan.cc/lint/README.md new file mode 100644 index 00000000..8a9c8b51 --- /dev/null +++ b/vendor/mvdan.cc/lint/README.md @@ -0,0 +1,27 @@ +# lint + +[![GoDoc](https://godoc.org/mvdan.cc/lint?status.svg)](https://godoc.org/mvdan.cc/lint) +[![Build Status](https://travis-ci.org/mvdan/lint.svg?branch=master)](https://travis-ci.org/mvdan/lint) + +Work in progress. Its API might change before the 1.0 release. + +This package intends to define simple interfaces that Go code checkers +can implement. This would simplify calling them from Go code, as well as +running multiple linters while sharing initial loading work. + +### metalint + + go get -u mvdan.cc/lint/cmd/metalint + +The start of a linter that runs many linters leveraging the common +interface. Not stable yet. + +Linters included: + +* [unparam](https://mvdan.cc/unparam) +* [interfacer](https://github.com/mvdan/interfacer) + +### Related projects + +* [golinters](https://github.com/thomasheller/golinters) - Report on + linter support diff --git a/vendor/mvdan.cc/lint/lint.go b/vendor/mvdan.cc/lint/lint.go new file mode 100644 index 00000000..153d04ca --- /dev/null +++ b/vendor/mvdan.cc/lint/lint.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +// Package lint defines common interfaces for Go code checkers. +package lint // import "mvdan.cc/lint" + +import ( + "go/token" + + "github.com/golangci/go-tools/ssa" + "golang.org/x/tools/go/loader" +) + +// A Checker points out issues in a program. +type Checker interface { + Program(*loader.Program) + Check() ([]Issue, error) +} + +type WithSSA interface { + ProgramSSA(*ssa.Program) +} + +// Issue represents an issue somewhere in a source code file. +type Issue interface { + Pos() token.Pos + Message() string +}