代码拉取完成,页面将自动刷新
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"reflect"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/google/shlex"
kingpin "gopkg.in/alecthomas/kingpin.v3-unstable"
)
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
}
type linterState struct {
*Linter
issues chan *Issue
vars Vars
exclude *regexp.Regexp
include *regexp.Regexp
deadline <-chan time.Time
}
func (l *linterState) Partitions(paths []string) ([][]string, error) {
cmdArgs, err := parseCommand(l.command())
if err != nil {
return nil, err
}
parts, err := l.Linter.PartitionStrategy(cmdArgs, paths)
if err != nil {
return nil, err
}
return parts, nil
}
func (l *linterState) command() string {
return l.vars.Replace(l.Command)
}
func runLinters(linters map[string]*Linter, paths []string, concurrency int, exclude, include *regexp.Regexp) (chan *Issue, chan error) {
errch := make(chan error, len(linters))
concurrencych := make(chan bool, concurrency)
incomingIssues := make(chan *Issue, 1000000)
directiveParser := newDirectiveParser()
if config.WarnUnmatchedDirective {
directiveParser.LoadFiles(paths)
}
processedIssues := maybeSortIssues(filterIssuesViaDirectives(
directiveParser, maybeAggregateIssues(incomingIssues)))
vars := Vars{
"duplthreshold": fmt.Sprintf("%d", config.DuplThreshold),
"mincyclo": fmt.Sprintf("%d", config.Cyclo),
"maxlinelength": fmt.Sprintf("%d", config.LineLength),
"misspelllocale": fmt.Sprintf("%s", config.MisspellLocale),
"min_confidence": fmt.Sprintf("%f", config.MinConfidence),
"min_occurrences": fmt.Sprintf("%d", config.MinOccurrences),
"min_const_length": fmt.Sprintf("%d", config.MinConstLength),
"tests": "",
"not_tests": "true",
}
if config.Test {
vars["tests"] = "true"
vars["not_tests"] = ""
}
wg := &sync.WaitGroup{}
id := 1
for _, linter := range linters {
deadline := time.After(config.Deadline.Duration())
state := &linterState{
Linter: linter,
issues: incomingIssues,
vars: vars,
exclude: exclude,
include: include,
deadline: deadline,
}
partitions, err := state.Partitions(paths)
if err != nil {
errch <- err
continue
}
for _, args := range partitions {
wg.Add(1)
concurrencych <- true
// Call the goroutine with a copy of the args array so that the
// contents of the array are not modified by the next iteration of
// the above for loop
go func(id int, args []string) {
err := executeLinter(id, state, args)
if err != nil {
errch <- err
}
<-concurrencych
wg.Done()
}(id, args)
id++
}
}
go func() {
wg.Wait()
close(incomingIssues)
close(errch)
}()
return processedIssues, errch
}
func executeLinter(id int, state *linterState, args []string) error {
if len(args) == 0 {
return fmt.Errorf("missing linter command")
}
start := time.Now()
dbg := namespacedDebug(fmt.Sprintf("[%s.%d]: ", state.Name, id))
dbg("executing %s", strings.Join(args, " "))
buf := bytes.NewBuffer(nil)
command := args[0]
cmd := exec.Command(command, args[1:]...) // nolint: gosec
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 error)
go func() {
done <- cmd.Wait()
}()
// Wait for process to complete or deadline to expire.
select {
case err = <-done:
case <-state.deadline:
err = fmt.Errorf("deadline exceeded by linter %s (try increasing --deadline)",
state.Name)
kerr := cmd.Process.Kill()
if kerr != nil {
warning("failed to kill %s: %s", state.Name, kerr)
}
return err
}
if err != nil {
dbg("warning: %s returned %s: %s", command, err, buf.String())
}
processOutput(dbg, state, buf.Bytes())
elapsed := time.Since(start)
dbg("%s linter took %s", state.Name, elapsed)
return nil
}
func parseCommand(command 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
}
return append([]string{exe}, args[1:]...), nil
}
// nolint: gocyclo
func processOutput(dbg debugFunction, state *linterState, out []byte) {
re := state.regex
all := re.FindAllSubmatchIndex(out, -1)
dbg("%s hits %d: %s", state.Name, len(all), state.Pattern)
cwd, err := os.Getwd()
if err != nil {
warning("failed to get working directory %s", err)
}
// Create a local copy of vars so they can be modified by the linter output
vars := state.vars.Copy()
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)
}
issue, err := NewIssue(state.Linter.Name, config.formatTemplate)
kingpin.FatalIfError(err, "Invalid output format")
for i, name := range re.SubexpNames() {
if group[i] == nil {
continue
}
part := string(group[i])
if name != "" {
vars[name] = part
}
switch name {
case "path":
issue.Path, err = newIssuePathFromAbsPath(cwd, part)
if err != nil {
warning("failed to make %s a relative path: %s", part, err)
}
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 "":
}
}
// TODO: set messageOveride and severity on the Linter instead of reading
// them directly from the static config
if m, ok := config.MessageOverride[state.Name]; ok {
issue.Message = vars.Replace(m)
}
if sev, ok := config.Severity[state.Name]; ok {
issue.Severity = Severity(sev)
}
if state.exclude != nil && state.exclude.MatchString(issue.String()) {
continue
}
if state.include != nil && !state.include.MatchString(issue.String()) {
continue
}
state.issues <- issue
}
}
func maybeSortIssues(issues chan *Issue) chan *Issue {
if reflect.DeepEqual([]string{"none"}, config.Sort) {
return issues
}
return SortIssueChan(issues, config.Sort)
}
func maybeAggregateIssues(issues chan *Issue) chan *Issue {
if !config.Aggregate {
return issues
}
return AggregateIssueChan(issues)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。