You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-08 04:42:07 +03:00
Update vendored tools/.
This commit is contained in:
787
tools/vendor/github.com/alecthomas/gometalinter/main.go
generated
vendored
787
tools/vendor/github.com/alecthomas/gometalinter/main.go
generated
vendored
@@ -5,32 +5,18 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"gopkg.in/alecthomas/kingpin.v3-unstable"
|
||||
)
|
||||
|
||||
// Severity of linter message.
|
||||
type Severity string
|
||||
|
||||
// Linter message severity levels.
|
||||
const ( // nolint
|
||||
Warning Severity = "warning"
|
||||
Error Severity = "error"
|
||||
)
|
||||
|
||||
var (
|
||||
// Locations to look for vendored linters.
|
||||
vendoredSearchPaths = [][]string{
|
||||
@@ -39,129 +25,43 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
type Linter struct {
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
CompositeCommand string `json:"composite_command,omitempty"`
|
||||
Pattern string `json:"pattern"`
|
||||
InstallFrom string `json:"install_from"`
|
||||
SeverityOverride Severity `json:"severity,omitempty"`
|
||||
MessageOverride string `json:"message_override,omitempty"`
|
||||
|
||||
regex *regexp.Regexp
|
||||
}
|
||||
|
||||
func (l *Linter) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.Name)
|
||||
}
|
||||
|
||||
func (l *Linter) String() string {
|
||||
return l.Name
|
||||
}
|
||||
|
||||
func LinterFromName(name string) *Linter {
|
||||
s := linterDefinitions[name]
|
||||
parts := strings.SplitN(s, ":", 2)
|
||||
if len(parts) < 2 {
|
||||
kingpin.Fatalf("invalid linter: %q", name)
|
||||
}
|
||||
|
||||
pattern := parts[1]
|
||||
if p, ok := predefinedPatterns[pattern]; ok {
|
||||
pattern = p
|
||||
}
|
||||
re, err := regexp.Compile("(?m:" + pattern + ")")
|
||||
kingpin.FatalIfError(err, "invalid regex for %q", name)
|
||||
return &Linter{
|
||||
Name: name,
|
||||
Command: s[0:strings.Index(s, ":")],
|
||||
Pattern: pattern,
|
||||
InstallFrom: installMap[name],
|
||||
SeverityOverride: Severity(config.Severity[name]),
|
||||
MessageOverride: config.MessageOverride[name],
|
||||
regex: re,
|
||||
}
|
||||
}
|
||||
|
||||
type sortedIssues struct {
|
||||
issues []*Issue
|
||||
order []string
|
||||
}
|
||||
|
||||
func (s *sortedIssues) Len() int { return len(s.issues) }
|
||||
func (s *sortedIssues) Swap(i, j int) { s.issues[i], s.issues[j] = s.issues[j], s.issues[i] }
|
||||
|
||||
// nolint: gocyclo
|
||||
func (s *sortedIssues) Less(i, j int) bool {
|
||||
l, r := s.issues[i], s.issues[j]
|
||||
for _, key := range s.order {
|
||||
switch key {
|
||||
case "path":
|
||||
if l.Path > r.Path {
|
||||
return false
|
||||
}
|
||||
case "line":
|
||||
if l.Line > r.Line {
|
||||
return false
|
||||
}
|
||||
case "column":
|
||||
if l.Col > r.Col {
|
||||
return false
|
||||
}
|
||||
case "severity":
|
||||
if l.Severity > r.Severity {
|
||||
return false
|
||||
}
|
||||
case "message":
|
||||
if l.Message > r.Message {
|
||||
return false
|
||||
}
|
||||
case "linter":
|
||||
if l.Linter.Name > r.Linter.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
kingpin.Flag("config", "Load JSON configuration from file.").Action(loadConfig).String()
|
||||
kingpin.Flag("disable", "Disable previously enabled linters.").PlaceHolder("LINTER").Short('D').Action(disableAction).Strings()
|
||||
kingpin.Flag("enable", "Enable previously disabled linters.").PlaceHolder("LINTER").Short('E').Action(enableAction).Strings()
|
||||
kingpin.Flag("linter", "Define a linter.").PlaceHolder("NAME:COMMAND:PATTERN").StringMapVar(&config.Linters)
|
||||
kingpin.Flag("message-overrides", "Override message from linter. {message} will be expanded to the original message.").PlaceHolder("LINTER:MESSAGE").StringMapVar(&config.MessageOverride)
|
||||
kingpin.Flag("severity", "Map of linter severities.").PlaceHolder("LINTER:SEVERITY").StringMapVar(&config.Severity)
|
||||
kingpin.Flag("disable-all", "Disable all linters.").Action(disableAllAction).Bool()
|
||||
kingpin.Flag("enable-all", "Enable all linters.").Action(enableAllAction).Bool()
|
||||
kingpin.Flag("format", "Output format.").PlaceHolder(config.Format).StringVar(&config.Format)
|
||||
kingpin.Flag("vendored-linters", "Use vendored linters (recommended).").BoolVar(&config.VendoredLinters)
|
||||
kingpin.Flag("fast", "Only run fast linters.").BoolVar(&config.Fast)
|
||||
kingpin.Flag("install", "Attempt to install all known linters.").Short('i').BoolVar(&config.Install)
|
||||
kingpin.Flag("update", "Pass -u to go tool when installing.").Short('u').BoolVar(&config.Update)
|
||||
kingpin.Flag("force", "Pass -f to go tool when installing.").Short('f').BoolVar(&config.Force)
|
||||
kingpin.Flag("download-only", "Pass -d to go tool when installing.").BoolVar(&config.DownloadOnly)
|
||||
kingpin.Flag("debug", "Display messages for failed linters, etc.").Short('d').BoolVar(&config.Debug)
|
||||
kingpin.Flag("concurrency", "Number of concurrent linters to run.").PlaceHolder(fmt.Sprintf("%d", runtime.NumCPU())).Short('j').IntVar(&config.Concurrency)
|
||||
kingpin.Flag("exclude", "Exclude messages matching these regular expressions.").Short('e').PlaceHolder("REGEXP").StringsVar(&config.Exclude)
|
||||
kingpin.Flag("include", "Include messages matching these regular expressions.").Short('I').PlaceHolder("REGEXP").StringsVar(&config.Include)
|
||||
kingpin.Flag("skip", "Skip directories with this name when expanding '...'.").Short('s').PlaceHolder("DIR...").StringsVar(&config.Skip)
|
||||
kingpin.Flag("vendor", "Enable vendoring support (skips 'vendor' directories and sets GO15VENDOREXPERIMENT=1).").BoolVar(&config.Vendor)
|
||||
kingpin.Flag("cyclo-over", "Report functions with cyclomatic complexity over N (using gocyclo).").PlaceHolder("10").IntVar(&config.Cyclo)
|
||||
kingpin.Flag("line-length", "Report lines longer than N (using lll).").PlaceHolder("80").IntVar(&config.LineLength)
|
||||
kingpin.Flag("min-confidence", "Minimum confidence interval to pass to golint.").PlaceHolder(".80").FloatVar(&config.MinConfidence)
|
||||
kingpin.Flag("min-occurrences", "Minimum occurrences to pass to goconst.").PlaceHolder("3").IntVar(&config.MinOccurrences)
|
||||
kingpin.Flag("min-const-length", "Minimumum constant length.").PlaceHolder("3").IntVar(&config.MinConstLength)
|
||||
kingpin.Flag("dupl-threshold", "Minimum token sequence as a clone for dupl.").PlaceHolder("50").IntVar(&config.DuplThreshold)
|
||||
kingpin.Flag("sort", fmt.Sprintf("Sort output by any of %s.", strings.Join(sortKeys, ", "))).PlaceHolder("none").EnumsVar(&config.Sort, sortKeys...)
|
||||
kingpin.Flag("tests", "Include test files for linters that support this option").Short('t').BoolVar(&config.Test)
|
||||
kingpin.Flag("deadline", "Cancel linters if they have not completed within this duration.").PlaceHolder("30s").DurationVar(&config.Deadline)
|
||||
kingpin.Flag("errors", "Only show errors.").BoolVar(&config.Errors)
|
||||
kingpin.Flag("json", "Generate structured JSON rather than standard line-based output.").BoolVar(&config.JSON)
|
||||
kingpin.Flag("checkstyle", "Generate checkstyle XML rather than standard line-based output.").BoolVar(&config.Checkstyle)
|
||||
kingpin.Flag("enable-gc", "Enable GC for linters (useful on large repositories).").BoolVar(&config.EnableGC)
|
||||
kingpin.Flag("aggregate", "Aggregate issues reported by several linters.").BoolVar(&config.Aggregate)
|
||||
kingpin.CommandLine.GetFlag("help").Short('h')
|
||||
func setupFlags(app *kingpin.Application) {
|
||||
app.Flag("config", "Load JSON configuration from file.").Action(loadConfig).String()
|
||||
app.Flag("disable", "Disable previously enabled linters.").PlaceHolder("LINTER").Short('D').Action(disableAction).Strings()
|
||||
app.Flag("enable", "Enable previously disabled linters.").PlaceHolder("LINTER").Short('E').Action(enableAction).Strings()
|
||||
app.Flag("linter", "Define a linter.").PlaceHolder("NAME:COMMAND:PATTERN").StringMapVar(&config.Linters)
|
||||
app.Flag("message-overrides", "Override message from linter. {message} will be expanded to the original message.").PlaceHolder("LINTER:MESSAGE").StringMapVar(&config.MessageOverride)
|
||||
app.Flag("severity", "Map of linter severities.").PlaceHolder("LINTER:SEVERITY").StringMapVar(&config.Severity)
|
||||
app.Flag("disable-all", "Disable all linters.").Action(disableAllAction).Bool()
|
||||
app.Flag("enable-all", "Enable all linters.").Action(enableAllAction).Bool()
|
||||
app.Flag("format", "Output format.").PlaceHolder(config.Format).StringVar(&config.Format)
|
||||
app.Flag("vendored-linters", "Use vendored linters (recommended).").BoolVar(&config.VendoredLinters)
|
||||
app.Flag("fast", "Only run fast linters.").BoolVar(&config.Fast)
|
||||
app.Flag("install", "Attempt to install all known linters.").Short('i').BoolVar(&config.Install)
|
||||
app.Flag("update", "Pass -u to go tool when installing.").Short('u').BoolVar(&config.Update)
|
||||
app.Flag("force", "Pass -f to go tool when installing.").Short('f').BoolVar(&config.Force)
|
||||
app.Flag("download-only", "Pass -d to go tool when installing.").BoolVar(&config.DownloadOnly)
|
||||
app.Flag("debug", "Display messages for failed linters, etc.").Short('d').BoolVar(&config.Debug)
|
||||
app.Flag("concurrency", "Number of concurrent linters to run.").PlaceHolder(fmt.Sprintf("%d", runtime.NumCPU())).Short('j').IntVar(&config.Concurrency)
|
||||
app.Flag("exclude", "Exclude messages matching these regular expressions.").Short('e').PlaceHolder("REGEXP").StringsVar(&config.Exclude)
|
||||
app.Flag("include", "Include messages matching these regular expressions.").Short('I').PlaceHolder("REGEXP").StringsVar(&config.Include)
|
||||
app.Flag("skip", "Skip directories with this name when expanding '...'.").Short('s').PlaceHolder("DIR...").StringsVar(&config.Skip)
|
||||
app.Flag("vendor", "Enable vendoring support (skips 'vendor' directories and sets GO15VENDOREXPERIMENT=1).").BoolVar(&config.Vendor)
|
||||
app.Flag("cyclo-over", "Report functions with cyclomatic complexity over N (using gocyclo).").PlaceHolder("10").IntVar(&config.Cyclo)
|
||||
app.Flag("line-length", "Report lines longer than N (using lll).").PlaceHolder("80").IntVar(&config.LineLength)
|
||||
app.Flag("min-confidence", "Minimum confidence interval to pass to golint.").PlaceHolder(".80").FloatVar(&config.MinConfidence)
|
||||
app.Flag("min-occurrences", "Minimum occurrences to pass to goconst.").PlaceHolder("3").IntVar(&config.MinOccurrences)
|
||||
app.Flag("min-const-length", "Minimumum constant length.").PlaceHolder("3").IntVar(&config.MinConstLength)
|
||||
app.Flag("dupl-threshold", "Minimum token sequence as a clone for dupl.").PlaceHolder("50").IntVar(&config.DuplThreshold)
|
||||
app.Flag("sort", fmt.Sprintf("Sort output by any of %s.", strings.Join(sortKeys, ", "))).PlaceHolder("none").EnumsVar(&config.Sort, sortKeys...)
|
||||
app.Flag("tests", "Include test files for linters that support this option").Short('t').BoolVar(&config.Test)
|
||||
app.Flag("deadline", "Cancel linters if they have not completed within this duration.").PlaceHolder("30s").DurationVar((*time.Duration)(&config.Deadline))
|
||||
app.Flag("errors", "Only show errors.").BoolVar(&config.Errors)
|
||||
app.Flag("json", "Generate structured JSON rather than standard line-based output.").BoolVar(&config.JSON)
|
||||
app.Flag("checkstyle", "Generate checkstyle XML rather than standard line-based output.").BoolVar(&config.Checkstyle)
|
||||
app.Flag("enable-gc", "Enable GC for linters (useful on large repositories).").BoolVar(&config.EnableGC)
|
||||
app.Flag("aggregate", "Aggregate issues reported by several linters.").BoolVar(&config.Aggregate)
|
||||
app.GetFlag("help").Short('h')
|
||||
}
|
||||
|
||||
func loadConfig(app *kingpin.Application, element *kingpin.ParseElement, ctx *kingpin.ParseContext) error {
|
||||
@@ -174,9 +74,6 @@ func loadConfig(app *kingpin.Application, element *kingpin.ParseElement, ctx *ki
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.DeadlineJSONCrutch != "" {
|
||||
config.Deadline, err = time.ParseDuration(config.DeadlineJSONCrutch)
|
||||
}
|
||||
for _, disable := range config.Disable {
|
||||
for i, enable := range config.Enable {
|
||||
if enable == disable {
|
||||
@@ -210,28 +107,13 @@ func disableAllAction(app *kingpin.Application, element *kingpin.ParseElement, c
|
||||
}
|
||||
|
||||
func enableAllAction(app *kingpin.Application, element *kingpin.ParseElement, ctx *kingpin.ParseContext) error {
|
||||
for linter := range linterDefinitions {
|
||||
for linter := range defaultLinters {
|
||||
config.Enable = append(config.Enable, linter)
|
||||
}
|
||||
config.EnableAll = true
|
||||
return nil
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
Linter *Linter `json:"linter"`
|
||||
Severity Severity `json:"severity"`
|
||||
Path string `json:"path"`
|
||||
Line int `json:"line"`
|
||||
Col int `json:"col"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (i *Issue) String() string {
|
||||
buf := new(bytes.Buffer)
|
||||
err := formatTemplate.Execute(buf, i)
|
||||
kingpin.FatalIfError(err, "Invalid output format")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func debug(format string, args ...interface{}) {
|
||||
if config.Debug {
|
||||
fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
|
||||
@@ -244,13 +126,13 @@ func warning(format string, args ...interface{}) {
|
||||
|
||||
func formatLinters() string {
|
||||
w := bytes.NewBuffer(nil)
|
||||
for name := range linterDefinitions {
|
||||
linter := LinterFromName(name)
|
||||
for _, linter := range getDefaultLinters() {
|
||||
install := "(" + linter.InstallFrom + ")"
|
||||
if install == "()" {
|
||||
install = ""
|
||||
}
|
||||
fmt.Fprintf(w, " %s %s\n %s\n %s\n", name, install, linter.Command, linter.Pattern)
|
||||
fmt.Fprintf(w, " %s %s\n %s\n %s\n",
|
||||
linter.Name, install, linter.Command, linter.Pattern)
|
||||
}
|
||||
return w.String()
|
||||
}
|
||||
@@ -263,33 +145,11 @@ func formatSeverity() string {
|
||||
return w.String()
|
||||
}
|
||||
|
||||
type Vars map[string]string
|
||||
|
||||
func (v Vars) Copy() Vars {
|
||||
out := Vars{}
|
||||
for k, v := range v {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v Vars) Replace(s string) string {
|
||||
for k, v := range v {
|
||||
prefix := regexp.MustCompile(fmt.Sprintf("{%s=([^}]*)}", k))
|
||||
if v != "" {
|
||||
s = prefix.ReplaceAllString(s, "$1")
|
||||
} else {
|
||||
s = prefix.ReplaceAllString(s, "")
|
||||
}
|
||||
s = strings.Replace(s, fmt.Sprintf("{%s}", k), v, -1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Linters are by their very nature, short lived, so disable GC.
|
||||
// Reduced (user) linting time on kingpin from 0.97s to 0.64s.
|
||||
kingpin.CommandLine.Help = fmt.Sprintf(`Aggregate and normalise the output of a whole bunch of Go linters.
|
||||
pathsArg := kingpin.Arg("path", "Directories to lint. Defaults to \".\". <path>/... will recurse.").Strings()
|
||||
app := kingpin.CommandLine
|
||||
setupFlags(app)
|
||||
app.Help = fmt.Sprintf(`Aggregate and normalise the output of a whole bunch of Go linters.
|
||||
|
||||
PlaceHolder linters:
|
||||
|
||||
@@ -301,21 +161,23 @@ Severity override map (default is "warning"):
|
||||
`, formatLinters(), formatSeverity())
|
||||
kingpin.Parse()
|
||||
|
||||
configureEnvironment()
|
||||
|
||||
if config.Install {
|
||||
if config.VendoredLinters {
|
||||
configureEnvironmentForInstall()
|
||||
}
|
||||
installLinters()
|
||||
return
|
||||
}
|
||||
|
||||
configureEnvironment()
|
||||
include, exclude := processConfig(config)
|
||||
|
||||
start := time.Now()
|
||||
paths := expandPaths(*pathsArg, config.Skip)
|
||||
paths := resolvePaths(*pathsArg, config.Skip)
|
||||
|
||||
linters := lintersFromFlags()
|
||||
linters := lintersFromConfig(config)
|
||||
issues, errch := runLinters(linters, paths, config.Concurrency, exclude, include)
|
||||
status := 0
|
||||
issues, errch := runLinters(linters, paths, *pathsArg, config.Concurrency, exclude, include)
|
||||
if config.JSON {
|
||||
status |= outputToJSON(issues)
|
||||
} else if config.Checkstyle {
|
||||
@@ -334,14 +196,12 @@ Severity override map (default is "warning"):
|
||||
|
||||
// nolint: gocyclo
|
||||
func processConfig(config *Config) (include *regexp.Regexp, exclude *regexp.Regexp) {
|
||||
// Move configured linters into linterDefinitions.
|
||||
for name, definition := range config.Linters {
|
||||
linterDefinitions[name] = definition
|
||||
}
|
||||
|
||||
tmpl, err := template.New("output").Parse(config.Format)
|
||||
kingpin.FatalIfError(err, "invalid format %q", config.Format)
|
||||
formatTemplate = tmpl
|
||||
|
||||
// Linters are by their very nature, short lived, so disable GC.
|
||||
// Reduced (user) linting time on kingpin from 0.97s to 0.64s.
|
||||
if !config.EnableGC {
|
||||
_ = os.Setenv("GOGC", "off")
|
||||
}
|
||||
@@ -413,74 +273,13 @@ func outputToJSON(issues chan *Issue) int {
|
||||
return status
|
||||
}
|
||||
|
||||
func runLinters(linters map[string]*Linter, paths, ellipsisPaths []string, concurrency int, exclude *regexp.Regexp, include *regexp.Regexp) (chan *Issue, chan error) {
|
||||
errch := make(chan error, len(linters)*(len(paths)+len(ellipsisPaths)))
|
||||
concurrencych := make(chan bool, config.Concurrency)
|
||||
incomingIssues := make(chan *Issue, 1000000)
|
||||
directives := newDirectiveParser(paths)
|
||||
processedIssues := filterIssuesViaDirectives(directives, maybeSortIssues(maybeAggregateIssues(incomingIssues)))
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, linter := range linters {
|
||||
// Recreated in each loop because it is mutated by executeLinter().
|
||||
vars := Vars{
|
||||
"duplthreshold": fmt.Sprintf("%d", config.DuplThreshold),
|
||||
"mincyclo": fmt.Sprintf("%d", config.Cyclo),
|
||||
"maxlinelength": fmt.Sprintf("%d", config.LineLength),
|
||||
"min_confidence": fmt.Sprintf("%f", config.MinConfidence),
|
||||
"min_occurrences": fmt.Sprintf("%d", config.MinOccurrences),
|
||||
"min_const_length": fmt.Sprintf("%d", config.MinConstLength),
|
||||
"tests": "",
|
||||
}
|
||||
if config.Test {
|
||||
vars["tests"] = "-t"
|
||||
}
|
||||
linterPaths := paths
|
||||
// Most linters don't exclude vendor paths when recursing, so we don't use ... paths.
|
||||
if acceptsEllipsis[linter.Name] && !config.Vendor && len(ellipsisPaths) > 0 {
|
||||
linterPaths = ellipsisPaths
|
||||
}
|
||||
for _, path := range linterPaths {
|
||||
wg.Add(1)
|
||||
deadline := time.After(config.Deadline)
|
||||
state := &linterState{
|
||||
Linter: linter,
|
||||
issues: incomingIssues,
|
||||
path: path,
|
||||
vars: vars.Copy(),
|
||||
exclude: exclude,
|
||||
include: include,
|
||||
deadline: deadline,
|
||||
}
|
||||
go func() {
|
||||
concurrencych <- true
|
||||
err := executeLinter(state)
|
||||
if err != nil {
|
||||
errch <- err
|
||||
}
|
||||
<-concurrencych
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(incomingIssues)
|
||||
close(errch)
|
||||
}()
|
||||
return processedIssues, errch
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func expandPaths(paths, skip []string) []string {
|
||||
func resolvePaths(paths, skip []string) []string {
|
||||
if len(paths) == 0 {
|
||||
paths = []string{"."}
|
||||
return []string{"."}
|
||||
}
|
||||
skipMap := map[string]bool{}
|
||||
for _, name := range skip {
|
||||
skipMap[name] = true
|
||||
}
|
||||
dirs := map[string]bool{}
|
||||
|
||||
skipPath := newPathFilter(skip)
|
||||
dirs := newStringSet()
|
||||
for _, path := range paths {
|
||||
if strings.HasSuffix(path, "/...") {
|
||||
root := filepath.Dir(path)
|
||||
@@ -490,24 +289,22 @@ func expandPaths(paths, skip []string) []string {
|
||||
return err
|
||||
}
|
||||
|
||||
base := filepath.Base(p)
|
||||
skip := skipMap[base] || skipMap[p] || (strings.ContainsAny(base[0:1], "_.") && base != "." && base != "..")
|
||||
if i.IsDir() {
|
||||
if skip {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
} else if !skip && strings.HasSuffix(p, ".go") {
|
||||
dirs[filepath.Clean(filepath.Dir(p))] = true
|
||||
skip := skipPath(p)
|
||||
switch {
|
||||
case i.IsDir() && skip:
|
||||
return filepath.SkipDir
|
||||
case !i.IsDir() && !skip && strings.HasSuffix(p, ".go"):
|
||||
dirs.add(filepath.Clean(filepath.Dir(p)))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
dirs[filepath.Clean(path)] = true
|
||||
dirs.add(filepath.Clean(path))
|
||||
}
|
||||
}
|
||||
out := make([]string, 0, len(dirs))
|
||||
for d := range dirs {
|
||||
out = append(out, d)
|
||||
out := make([]string, 0, dirs.size())
|
||||
for _, d := range dirs.asSlice() {
|
||||
out = append(out, relativePackagePath(d))
|
||||
}
|
||||
sort.Strings(out)
|
||||
for _, d := range out {
|
||||
@@ -516,313 +313,82 @@ func expandPaths(paths, skip []string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func makeInstallCommand(linters ...string) []string {
|
||||
cmd := []string{"get"}
|
||||
if config.VendoredLinters {
|
||||
cmd = []string{"install"}
|
||||
} else {
|
||||
if config.Update {
|
||||
cmd = append(cmd, "-u")
|
||||
func newPathFilter(skip []string) func(string) bool {
|
||||
filter := map[string]bool{}
|
||||
for _, name := range skip {
|
||||
filter[name] = true
|
||||
}
|
||||
|
||||
return func(path string) bool {
|
||||
base := filepath.Base(path)
|
||||
if filter[base] || filter[path] {
|
||||
return true
|
||||
}
|
||||
if config.Force {
|
||||
cmd = append(cmd, "-f")
|
||||
}
|
||||
if config.DownloadOnly {
|
||||
cmd = append(cmd, "-d")
|
||||
}
|
||||
}
|
||||
if config.Debug {
|
||||
cmd = append(cmd, "-v")
|
||||
}
|
||||
cmd = append(cmd, linters...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func installLintersWithOneCommand(targets []string) error {
|
||||
cmd := makeInstallCommand(targets...)
|
||||
debug("go %s", strings.Join(cmd, " "))
|
||||
c := exec.Command("go", cmd...) // nolint: gas
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
return c.Run()
|
||||
}
|
||||
|
||||
func installLintersIndividually(targets []string) {
|
||||
failed := []string{}
|
||||
for _, target := range targets {
|
||||
cmd := makeInstallCommand(target)
|
||||
debug("go %s", strings.Join(cmd, " "))
|
||||
c := exec.Command("go", cmd...) // nolint: gas
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
if err := c.Run(); err != nil {
|
||||
warning("failed to install %s: %s", target, err)
|
||||
failed = append(failed, target)
|
||||
}
|
||||
}
|
||||
if len(failed) > 0 {
|
||||
kingpin.Fatalf("failed to install the following linters: %s", strings.Join(failed, ", "))
|
||||
return base != "." && base != ".." && strings.ContainsAny(base[0:1], "_.")
|
||||
}
|
||||
}
|
||||
|
||||
func installLinters() {
|
||||
names := make([]string, 0, len(installMap))
|
||||
targets := make([]string, 0, len(installMap))
|
||||
for name, target := range installMap {
|
||||
names = append(names, name)
|
||||
targets = append(targets, target)
|
||||
func relativePackagePath(dir string) string {
|
||||
if filepath.IsAbs(dir) || strings.HasPrefix(dir, ".") {
|
||||
return dir
|
||||
}
|
||||
namesStr := strings.Join(names, "\n ")
|
||||
if config.DownloadOnly {
|
||||
fmt.Printf("Downloading:\n %s\n", namesStr)
|
||||
} else {
|
||||
fmt.Printf("Installing:\n %s\n", namesStr)
|
||||
}
|
||||
err := installLintersWithOneCommand(targets)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
warning("failed to install one or more linters: %s (installing individually)", err)
|
||||
installLintersIndividually(targets)
|
||||
// package names must start with a ./
|
||||
return "./" + dir
|
||||
}
|
||||
|
||||
func maybeAggregateIssues(issues chan *Issue) chan *Issue {
|
||||
if !config.Aggregate {
|
||||
return issues
|
||||
}
|
||||
return aggregateIssues(issues)
|
||||
}
|
||||
|
||||
func maybeSortIssues(issues chan *Issue) chan *Issue {
|
||||
if reflect.DeepEqual([]string{"none"}, config.Sort) {
|
||||
return issues
|
||||
}
|
||||
out := make(chan *Issue, 1000000)
|
||||
sorted := &sortedIssues{
|
||||
issues: []*Issue{},
|
||||
order: config.Sort,
|
||||
}
|
||||
go func() {
|
||||
for issue := range issues {
|
||||
sorted.issues = append(sorted.issues, issue)
|
||||
}
|
||||
sort.Sort(sorted)
|
||||
for _, issue := range sorted.issues {
|
||||
out <- issue
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
type linterState struct {
|
||||
*Linter
|
||||
path string
|
||||
issues chan *Issue
|
||||
vars Vars
|
||||
exclude *regexp.Regexp
|
||||
include *regexp.Regexp
|
||||
deadline <-chan time.Time
|
||||
}
|
||||
|
||||
func (l *linterState) InterpolatedCommand() string {
|
||||
vars := l.vars.Copy()
|
||||
if l.ShouldChdir() {
|
||||
vars["path"] = "."
|
||||
} else {
|
||||
vars["path"] = l.path
|
||||
}
|
||||
return vars.Replace(l.Command)
|
||||
}
|
||||
|
||||
func (l *linterState) ShouldChdir() bool {
|
||||
return config.Vendor || !strings.HasSuffix(l.path, "/...") || !strings.Contains(l.Command, "{path}")
|
||||
}
|
||||
|
||||
func parseCommand(dir, command string) (string, []string, error) {
|
||||
args, err := shlex.Split(command)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return "", nil, fmt.Errorf("invalid command %q", command)
|
||||
}
|
||||
exe, err := exec.LookPath(args[0])
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
out := []string{}
|
||||
for _, arg := range args[1:] {
|
||||
if strings.Contains(arg, "*") {
|
||||
pattern := filepath.Join(dir, arg)
|
||||
globbed, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
for i, g := range globbed {
|
||||
if strings.HasPrefix(g, dir+string(filepath.Separator)) {
|
||||
globbed[i] = g[len(dir)+1:]
|
||||
}
|
||||
}
|
||||
out = append(out, globbed...)
|
||||
} else {
|
||||
out = append(out, arg)
|
||||
}
|
||||
}
|
||||
return exe, out, nil
|
||||
}
|
||||
|
||||
func executeLinter(state *linterState) error {
|
||||
debug("linting with %s: %s (on %s)", state.Name, state.Command, state.path)
|
||||
|
||||
start := time.Now()
|
||||
command := state.InterpolatedCommand()
|
||||
exe, args, err := parseCommand(state.path, command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debug("executing %s %q", exe, args)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := exec.Command(exe, args...) // nolint: gas
|
||||
if state.ShouldChdir() {
|
||||
cmd.Dir = state.path
|
||||
}
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = buf
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute linter %s: %s", command, err)
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
err = cmd.Wait()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Wait for process to complete or deadline to expire.
|
||||
select {
|
||||
case <-done:
|
||||
|
||||
case <-state.deadline:
|
||||
err = fmt.Errorf("deadline exceeded by linter %s on %s (try increasing --deadline)",
|
||||
state.Name, state.path)
|
||||
kerr := cmd.Process.Kill()
|
||||
if kerr != nil {
|
||||
warning("failed to kill %s: %s", state.Name, kerr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
debug("warning: %s returned %s", command, err)
|
||||
}
|
||||
|
||||
processOutput(state, buf.Bytes())
|
||||
elapsed := time.Since(start)
|
||||
debug("%s linter took %s", state.Name, elapsed)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linterState) fixPath(path string) string {
|
||||
lpath := strings.TrimSuffix(l.path, "...")
|
||||
labspath, _ := filepath.Abs(lpath)
|
||||
|
||||
if !l.ShouldChdir() {
|
||||
path = strings.TrimPrefix(path, lpath)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
path, _ = filepath.Abs(filepath.Join(labspath, path))
|
||||
}
|
||||
if strings.HasPrefix(path, labspath) {
|
||||
return filepath.Join(lpath, strings.TrimPrefix(path, labspath))
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func lintersFromFlags() map[string]*Linter {
|
||||
func lintersFromConfig(config *Config) map[string]*Linter {
|
||||
out := map[string]*Linter{}
|
||||
for _, linter := range config.Enable {
|
||||
out[linter] = LinterFromName(linter)
|
||||
config.Enable = replaceWithMegacheck(config.Enable, config.EnableAll)
|
||||
for _, name := range config.Enable {
|
||||
linter := getLinterByName(name, config.Linters[name])
|
||||
|
||||
if config.Fast && !linter.IsFast {
|
||||
continue
|
||||
}
|
||||
out[name] = linter
|
||||
}
|
||||
for _, linter := range config.Disable {
|
||||
delete(out, linter)
|
||||
}
|
||||
if config.Fast {
|
||||
for _, linter := range slowLinters {
|
||||
delete(out, linter)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func processOutput(state *linterState, out []byte) {
|
||||
re := state.regex
|
||||
all := re.FindAllSubmatchIndex(out, -1)
|
||||
debug("%s hits %d: %s", state.Name, len(all), state.Pattern)
|
||||
for _, indices := range all {
|
||||
group := [][]byte{}
|
||||
for i := 0; i < len(indices); i += 2 {
|
||||
var fragment []byte
|
||||
if indices[i] != -1 {
|
||||
fragment = out[indices[i]:indices[i+1]]
|
||||
}
|
||||
group = append(group, fragment)
|
||||
// replaceWithMegacheck checks enabled linters if they duplicate megacheck and
|
||||
// returns a either a revised list removing those and adding megacheck or an
|
||||
// unchanged slice. Emits a warning if linters were removed and swapped with
|
||||
// megacheck.
|
||||
func replaceWithMegacheck(enabled []string, enableAll bool) []string {
|
||||
var (
|
||||
staticcheck,
|
||||
gosimple,
|
||||
unused bool
|
||||
revised []string
|
||||
)
|
||||
for _, linter := range enabled {
|
||||
switch linter {
|
||||
case "staticcheck":
|
||||
staticcheck = true
|
||||
case "gosimple":
|
||||
gosimple = true
|
||||
case "unused":
|
||||
unused = true
|
||||
case "megacheck":
|
||||
// Don't add to revised slice, we'll add it later
|
||||
default:
|
||||
revised = append(revised, linter)
|
||||
}
|
||||
|
||||
issue := &Issue{Line: 1}
|
||||
issue.Linter = LinterFromName(state.Name)
|
||||
for i, name := range re.SubexpNames() {
|
||||
if group[i] == nil {
|
||||
continue
|
||||
}
|
||||
part := string(group[i])
|
||||
if name != "" {
|
||||
state.vars[name] = part
|
||||
}
|
||||
switch name {
|
||||
case "path":
|
||||
issue.Path = state.fixPath(part)
|
||||
|
||||
case "line":
|
||||
n, err := strconv.ParseInt(part, 10, 32)
|
||||
kingpin.FatalIfError(err, "line matched invalid integer")
|
||||
issue.Line = int(n)
|
||||
|
||||
case "col":
|
||||
n, err := strconv.ParseInt(part, 10, 32)
|
||||
kingpin.FatalIfError(err, "col matched invalid integer")
|
||||
issue.Col = int(n)
|
||||
|
||||
case "message":
|
||||
issue.Message = part
|
||||
|
||||
case "":
|
||||
}
|
||||
}
|
||||
if m, ok := config.MessageOverride[state.Name]; ok {
|
||||
issue.Message = state.vars.Replace(m)
|
||||
}
|
||||
if sev, ok := config.Severity[state.Name]; ok {
|
||||
issue.Severity = Severity(sev)
|
||||
} else {
|
||||
issue.Severity = "warning"
|
||||
}
|
||||
if state.exclude != nil && state.exclude.MatchString(issue.String()) {
|
||||
continue
|
||||
}
|
||||
if state.include != nil && !state.include.MatchString(issue.String()) {
|
||||
continue
|
||||
}
|
||||
state.issues <- issue
|
||||
}
|
||||
return
|
||||
if staticcheck && gosimple && unused {
|
||||
if !enableAll {
|
||||
warning("staticcheck, gosimple and unused are all set, using megacheck instead")
|
||||
}
|
||||
return append(revised, "megacheck")
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
func findVendoredLinters() string {
|
||||
gopaths := strings.Split(getGoPath(), string(os.PathListSeparator))
|
||||
gopaths := getGoPathList()
|
||||
for _, home := range vendoredSearchPaths {
|
||||
for _, p := range gopaths {
|
||||
joined := append([]string{p, "src"}, home...)
|
||||
@@ -833,7 +399,6 @@ func findVendoredLinters() string {
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
// Go 1.8 compatible GOPATH.
|
||||
@@ -847,62 +412,70 @@ func getGoPath() string {
|
||||
return path
|
||||
}
|
||||
|
||||
// addPath appends p to paths and returns it if:
|
||||
// 1. p is not a blank string
|
||||
// 2. p doesn't already exist in paths
|
||||
// Otherwise paths is returned unchanged.
|
||||
func addPath(p string, paths []string) []string {
|
||||
if p == "" {
|
||||
return paths
|
||||
}
|
||||
for _, path := range paths {
|
||||
if p == path {
|
||||
func getGoPathList() []string {
|
||||
return strings.Split(getGoPath(), string(os.PathListSeparator))
|
||||
}
|
||||
|
||||
// addPath appends path to paths if path does not already exist in paths. Returns
|
||||
// the new paths.
|
||||
func addPath(paths []string, path string) []string {
|
||||
for _, existingpath := range paths {
|
||||
if path == existingpath {
|
||||
return paths
|
||||
}
|
||||
}
|
||||
return append(paths, p)
|
||||
return append(paths, path)
|
||||
}
|
||||
|
||||
// Ensure all "bin" directories from GOPATH exists in PATH, as well as GOBIN if set.
|
||||
// configureEnvironment adds all `bin/` directories from $GOPATH to $PATH
|
||||
func configureEnvironment() {
|
||||
gopaths := strings.Split(getGoPath(), string(os.PathListSeparator))
|
||||
paths := addGoBinsToPath(getGoPathList())
|
||||
setEnv("PATH", strings.Join(paths, string(os.PathListSeparator)))
|
||||
debugPrintEnv()
|
||||
}
|
||||
|
||||
func addGoBinsToPath(gopaths []string) []string {
|
||||
paths := strings.Split(os.Getenv("PATH"), string(os.PathListSeparator))
|
||||
gobin := os.Getenv("GOBIN")
|
||||
|
||||
if config.VendoredLinters && config.Install {
|
||||
vendorRoot := findVendoredLinters()
|
||||
if vendorRoot == "" {
|
||||
kingpin.Fatalf("could not find vendored linters in GOPATH=%q", getGoPath())
|
||||
}
|
||||
debug("found vendored linters at %s, updating environment", vendorRoot)
|
||||
if gobin == "" {
|
||||
gobin = filepath.Join(gopaths[0], "bin")
|
||||
}
|
||||
// "go install" panics when one GOPATH element is beneath another, so we just set
|
||||
// our vendor root instead.
|
||||
gopaths = []string{vendorRoot}
|
||||
}
|
||||
|
||||
for _, p := range gopaths {
|
||||
paths = addPath(filepath.Join(p, "bin"), paths)
|
||||
paths = addPath(paths, filepath.Join(p, "bin"))
|
||||
}
|
||||
paths = addPath(gobin, paths)
|
||||
|
||||
path := strings.Join(paths, string(os.PathListSeparator))
|
||||
gopath := strings.Join(gopaths, string(os.PathListSeparator))
|
||||
|
||||
if err := os.Setenv("PATH", path); err != nil {
|
||||
warning("setenv PATH: %s", err)
|
||||
gobin := os.Getenv("GOBIN")
|
||||
if gobin != "" {
|
||||
paths = addPath(paths, gobin)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// configureEnvironmentForInstall sets GOPATH and GOBIN so that vendored linters
|
||||
// can be installed
|
||||
func configureEnvironmentForInstall() {
|
||||
gopaths := getGoPathList()
|
||||
vendorRoot := findVendoredLinters()
|
||||
if vendorRoot == "" {
|
||||
kingpin.Fatalf("could not find vendored linters in GOPATH=%q", getGoPath())
|
||||
}
|
||||
debug("found vendored linters at %s, updating environment", vendorRoot)
|
||||
|
||||
gobin := os.Getenv("GOBIN")
|
||||
if gobin == "" {
|
||||
gobin = filepath.Join(gopaths[0], "bin")
|
||||
}
|
||||
setEnv("GOBIN", gobin)
|
||||
|
||||
// "go install" panics when one GOPATH element is beneath another, so set
|
||||
// GOPATH to the vendor root
|
||||
setEnv("GOPATH", vendorRoot)
|
||||
debugPrintEnv()
|
||||
}
|
||||
|
||||
func setEnv(key string, value string) {
|
||||
if err := os.Setenv(key, value); err != nil {
|
||||
warning("setenv %s: %s", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
func debugPrintEnv() {
|
||||
debug("PATH=%s", os.Getenv("PATH"))
|
||||
|
||||
if err := os.Setenv("GOPATH", gopath); err != nil {
|
||||
warning("setenv GOPATH: %s", err)
|
||||
}
|
||||
debug("GOPATH=%s", os.Getenv("GOPATH"))
|
||||
|
||||
if err := os.Setenv("GOBIN", gobin); err != nil {
|
||||
warning("setenv GOBIN: %s", err)
|
||||
}
|
||||
debug("GOBIN=%s", os.Getenv("GOBIN"))
|
||||
}
|
||||
|
Reference in New Issue
Block a user