pretty printing issues
This commit is contained in:
parent
073ad51ed9
commit
5dc876c260
@ -2,22 +2,19 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/golangci/go-tools/ssa"
|
"github.com/golangci/go-tools/ssa"
|
||||||
"github.com/golangci/go-tools/ssa/ssautil"
|
"github.com/golangci/go-tools/ssa/ssautil"
|
||||||
"github.com/golangci/golangci-lint/pkg"
|
"github.com/golangci/golangci-lint/pkg"
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||||
"github.com/golangci/golangci-lint/pkg/golinters"
|
"github.com/golangci/golangci-lint/pkg/golinters"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/printers"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
"github.com/golangci/golangci-lint/pkg/result/processors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -39,6 +36,8 @@ func (e *Executor) initRun() {
|
|||||||
runCmd.Flags().StringVar(&rc.OutFormat, "out-format",
|
runCmd.Flags().StringVar(&rc.OutFormat, "out-format",
|
||||||
config.OutFormatColoredLineNumber,
|
config.OutFormatColoredLineNumber,
|
||||||
fmt.Sprintf("Format of output: %s", strings.Join(config.OutFormats, "|")))
|
fmt.Sprintf("Format of output: %s", strings.Join(config.OutFormats, "|")))
|
||||||
|
runCmd.Flags().BoolVar(&rc.PrintIssuedLine, "print-issued-lines", true, "Print lines of code with issue")
|
||||||
|
|
||||||
runCmd.Flags().IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
|
runCmd.Flags().IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
|
||||||
1, "Exit code when issues were found")
|
1, "Exit code when issues were found")
|
||||||
runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)")
|
runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)")
|
||||||
@ -227,9 +226,15 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gotAnyIssues, err := outputIssues(e.cfg.Run.OutFormat, issues)
|
var p printers.Printer
|
||||||
|
if e.cfg.Run.OutFormat == config.OutFormatJSON {
|
||||||
|
p = printers.NewJSON()
|
||||||
|
} else {
|
||||||
|
p = printers.NewText(e.cfg.Run.PrintIssuedLine, e.cfg.Run.OutFormat == config.OutFormatColoredLineNumber)
|
||||||
|
}
|
||||||
|
gotAnyIssues, err := p.Print(issues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't output %d issues: %s", len(issues), err)
|
return fmt.Errorf("can't print %d issues: %s", len(issues), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gotAnyIssues {
|
if gotAnyIssues {
|
||||||
@ -247,43 +252,3 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputIssues(format string, issues chan result.Issue) (bool, error) {
|
|
||||||
stdout := os.NewFile(uintptr(syscall.Stdout), "/dev/stdout") // was set to /dev/null
|
|
||||||
if format == config.OutFormatLineNumber || format == config.OutFormatColoredLineNumber {
|
|
||||||
gotAnyIssue := false
|
|
||||||
for i := range issues {
|
|
||||||
gotAnyIssue = true
|
|
||||||
text := i.Text
|
|
||||||
if format == config.OutFormatColoredLineNumber {
|
|
||||||
text = color.RedString(text)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(stdout, "%s:%d: %s\n", i.File, i.LineNumber, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !gotAnyIssue {
|
|
||||||
outStr := "Congrats! No issues were found."
|
|
||||||
if format == config.OutFormatColoredLineNumber {
|
|
||||||
outStr = color.GreenString(outStr)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(stdout, outStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return gotAnyIssue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if format == config.OutFormatJSON {
|
|
||||||
var allIssues []result.Issue
|
|
||||||
for i := range issues {
|
|
||||||
allIssues = append(allIssues, i)
|
|
||||||
}
|
|
||||||
outputJSON, err := json.Marshal(allIssues)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
fmt.Fprint(stdout, string(outputJSON))
|
|
||||||
return len(allIssues) != 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, fmt.Errorf("unknown output format %q", format)
|
|
||||||
}
|
|
||||||
|
@ -28,6 +28,8 @@ type Run struct {
|
|||||||
BuildTags []string
|
BuildTags []string
|
||||||
|
|
||||||
OutFormat string
|
OutFormat string
|
||||||
|
PrintIssuedLine bool
|
||||||
|
|
||||||
ExitCodeIfIssuesFound int
|
ExitCodeIfIssuesFound int
|
||||||
|
|
||||||
Errcheck struct {
|
Errcheck struct {
|
||||||
|
@ -23,8 +23,7 @@ func (d Deadcode) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, er
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: fmt.Sprintf("%s is unused", formatCode(i.UnusedIdentName, lintCtx.RunCfg())),
|
Text: fmt.Sprintf("%s is unused", formatCode(i.UnusedIdentName, lintCtx.RunCfg())),
|
||||||
FromLinter: d.Name(),
|
FromLinter: d.Name(),
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ package golinters
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
duplAPI "github.com/mibk/dupl"
|
duplAPI "github.com/mibk/dupl"
|
||||||
@ -27,8 +28,14 @@ func (d Dupl) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error)
|
|||||||
i.From.LineStart(), i.From.LineEnd(),
|
i.From.LineStart(), i.From.LineEnd(),
|
||||||
formatCode(dupl, lintCtx.RunCfg()))
|
formatCode(dupl, lintCtx.RunCfg()))
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.From.Filename(),
|
Pos: token.Position{
|
||||||
LineNumber: i.From.LineStart(),
|
Filename: i.From.Filename(),
|
||||||
|
Line: i.From.LineStart(),
|
||||||
|
},
|
||||||
|
LineRange: result.Range{
|
||||||
|
From: i.From.LineStart(),
|
||||||
|
To: i.From.LineEnd(),
|
||||||
|
},
|
||||||
Text: text,
|
Text: text,
|
||||||
FromLinter: d.Name(),
|
FromLinter: d.Name(),
|
||||||
})
|
})
|
||||||
|
@ -37,8 +37,7 @@ func (e Errcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, er
|
|||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
FromLinter: e.Name(),
|
FromLinter: e.Name(),
|
||||||
Text: text,
|
Text: text,
|
||||||
LineNumber: i.Pos.Line,
|
Pos: i.Pos,
|
||||||
File: i.Pos.Filename,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package golinters
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -33,8 +34,10 @@ func (lint Gas) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, erro
|
|||||||
text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
|
text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
|
||||||
line, _ := strconv.Atoi(i.Line)
|
line, _ := strconv.Atoi(i.Line)
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.File,
|
Pos: token.Position{
|
||||||
LineNumber: line,
|
Filename: i.File,
|
||||||
|
Line: line,
|
||||||
|
},
|
||||||
Text: text,
|
Text: text,
|
||||||
FromLinter: lint.Name(),
|
FromLinter: lint.Name(),
|
||||||
})
|
})
|
||||||
|
@ -33,8 +33,7 @@ func (lint Goconst) Run(ctx context.Context, lintCtx *Context) ([]result.Issue,
|
|||||||
textEnd = fmt.Sprintf(", but such constant %s already exists", formatCode(i.MatchingConst, lintCtx.RunCfg()))
|
textEnd = fmt.Sprintf(", but such constant %s already exists", formatCode(i.MatchingConst, lintCtx.RunCfg()))
|
||||||
}
|
}
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: textBegin + textEnd,
|
Text: textBegin + textEnd,
|
||||||
FromLinter: lint.Name(),
|
FromLinter: lint.Name(),
|
||||||
})
|
})
|
||||||
|
@ -24,8 +24,7 @@ func (g Gocyclo) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: s.Pos.Filename,
|
Pos: s.Pos,
|
||||||
LineNumber: s.Pos.Line,
|
|
||||||
Text: fmt.Sprintf("cyclomatic complexity %d of func %s is high (> %d)",
|
Text: fmt.Sprintf("cyclomatic complexity %d of func %s is high (> %d)",
|
||||||
s.Complexity, formatCode(s.FuncName, lintCtx.RunCfg()), lintCtx.RunCfg().Gocyclo.MinComplexity),
|
s.Complexity, formatCode(s.FuncName, lintCtx.RunCfg()), lintCtx.RunCfg().Gocyclo.MinComplexity),
|
||||||
FromLinter: g.Name(),
|
FromLinter: g.Name(),
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
|
||||||
gofmtAPI "github.com/golangci/gofmt/gofmt"
|
gofmtAPI "github.com/golangci/gofmt/gofmt"
|
||||||
goimportsAPI "github.com/golangci/gofmt/goimports"
|
goimportsAPI "github.com/golangci/gofmt/goimports"
|
||||||
@ -79,8 +80,10 @@ func (g Gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
|||||||
}
|
}
|
||||||
i := result.Issue{
|
i := result.Issue{
|
||||||
FromLinter: g.Name(),
|
FromLinter: g.Name(),
|
||||||
File: d.NewName,
|
Pos: token.Position{
|
||||||
LineNumber: deletedLine,
|
Filename: d.NewName,
|
||||||
|
Line: deletedLine,
|
||||||
|
},
|
||||||
Text: text,
|
Text: text,
|
||||||
}
|
}
|
||||||
issues = append(issues, i)
|
issues = append(issues, i)
|
||||||
|
@ -89,8 +89,7 @@ func lintFiles(minConfidence float64, filenames ...string) ([]result.Issue, erro
|
|||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
if p.Confidence >= minConfidence {
|
if p.Confidence >= minConfidence {
|
||||||
issues = append(issues, result.Issue{
|
issues = append(issues, result.Issue{
|
||||||
File: p.Position.Filename,
|
Pos: p.Position,
|
||||||
LineNumber: p.Position.Line,
|
|
||||||
Text: p.Text,
|
Text: p.Text,
|
||||||
})
|
})
|
||||||
// TODO: use p.Link and p.Category
|
// TODO: use p.Link and p.Category
|
||||||
|
@ -22,8 +22,7 @@ func (g Govet) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: i.Message,
|
Text: i.Message,
|
||||||
FromLinter: g.Name(),
|
FromLinter: g.Name(),
|
||||||
})
|
})
|
||||||
|
@ -20,8 +20,7 @@ func (lint Ineffassign) Run(ctx context.Context, lintCtx *Context) ([]result.Iss
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: fmt.Sprintf("ineffectual assignment to %s", formatCode(i.IdentName, lintCtx.RunCfg())),
|
Text: fmt.Sprintf("ineffectual assignment to %s", formatCode(i.IdentName, lintCtx.RunCfg())),
|
||||||
FromLinter: lint.Name(),
|
FromLinter: lint.Name(),
|
||||||
})
|
})
|
||||||
|
@ -28,8 +28,7 @@ func (lint Interfacer) Run(ctx context.Context, lintCtx *Context) ([]result.Issu
|
|||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
pos := lintCtx.SSAProgram.Fset.Position(i.Pos())
|
pos := lintCtx.SSAProgram.Fset.Position(i.Pos())
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: pos.Filename,
|
Pos: pos,
|
||||||
LineNumber: pos.Line,
|
|
||||||
Text: i.Message(),
|
Text: i.Message(),
|
||||||
FromLinter: lint.Name(),
|
FromLinter: lint.Name(),
|
||||||
})
|
})
|
||||||
|
@ -24,8 +24,7 @@ func (m Maligned) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, er
|
|||||||
text += fmt.Sprintf(":\n%s", formatCodeBlock(i.NewStructDef, lintCtx.RunCfg()))
|
text += fmt.Sprintf(":\n%s", formatCodeBlock(i.NewStructDef, lintCtx.RunCfg()))
|
||||||
}
|
}
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: text,
|
Text: text,
|
||||||
FromLinter: m.Name(),
|
FromLinter: m.Name(),
|
||||||
})
|
})
|
||||||
|
@ -20,8 +20,7 @@ func (m Megacheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, e
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Position.Filename,
|
Pos: i.Position,
|
||||||
LineNumber: i.Position.Line,
|
|
||||||
Text: i.Text,
|
Text: i.Text,
|
||||||
FromLinter: m.Name(),
|
FromLinter: m.Name(),
|
||||||
})
|
})
|
||||||
|
@ -20,8 +20,7 @@ func (s Structcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue,
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: fmt.Sprintf("%s is unused", formatCode(i.FieldName, lintCtx.RunCfg())),
|
Text: fmt.Sprintf("%s is unused", formatCode(i.FieldName, lintCtx.RunCfg())),
|
||||||
FromLinter: s.Name(),
|
FromLinter: s.Name(),
|
||||||
})
|
})
|
||||||
|
@ -18,8 +18,7 @@ func (lint Unconvert) Run(ctx context.Context, lintCtx *Context) ([]result.Issue
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, pos := range positions {
|
for _, pos := range positions {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: pos.Filename,
|
Pos: pos,
|
||||||
LineNumber: pos.Line,
|
|
||||||
Text: "unnecessary conversion",
|
Text: "unnecessary conversion",
|
||||||
FromLinter: lint.Name(),
|
FromLinter: lint.Name(),
|
||||||
})
|
})
|
||||||
|
@ -20,8 +20,7 @@ func (v Varcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, er
|
|||||||
var res []result.Issue
|
var res []result.Issue
|
||||||
for _, i := range issues {
|
for _, i := range issues {
|
||||||
res = append(res, result.Issue{
|
res = append(res, result.Issue{
|
||||||
File: i.Pos.Filename,
|
Pos: i.Pos,
|
||||||
LineNumber: i.Pos.Line,
|
|
||||||
Text: fmt.Sprintf("%s is unused", formatCode(i.VarName, lintCtx.RunCfg())),
|
Text: fmt.Sprintf("%s is unused", formatCode(i.VarName, lintCtx.RunCfg())),
|
||||||
FromLinter: v.Name(),
|
FromLinter: v.Name(),
|
||||||
})
|
})
|
||||||
|
27
pkg/printers/json.go
Normal file
27
pkg/printers/json.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSON struct{}
|
||||||
|
|
||||||
|
func NewJSON() *JSON {
|
||||||
|
return &JSON{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (JSON) Print(issues chan result.Issue) (bool, error) {
|
||||||
|
var allIssues []result.Issue
|
||||||
|
for i := range issues {
|
||||||
|
allIssues = append(allIssues, i)
|
||||||
|
}
|
||||||
|
outputJSON, err := json.Marshal(allIssues)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
fmt.Fprint(getOutWriter(), string(outputJSON))
|
||||||
|
return len(allIssues) != 0, nil
|
||||||
|
}
|
7
pkg/printers/printer.go
Normal file
7
pkg/printers/printer.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package printers
|
||||||
|
|
||||||
|
import "github.com/golangci/golangci-lint/pkg/result"
|
||||||
|
|
||||||
|
type Printer interface {
|
||||||
|
Print(issues chan result.Issue) (bool, error)
|
||||||
|
}
|
89
pkg/printers/text.go
Normal file
89
pkg/printers/text.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Text struct {
|
||||||
|
printIssuedLine bool
|
||||||
|
useColors bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewText(printIssuedLine bool, useColors bool) *Text {
|
||||||
|
return &Text{
|
||||||
|
printIssuedLine: printIssuedLine,
|
||||||
|
useColors: useColors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type linesCache [][]byte
|
||||||
|
type filesCache map[string]linesCache
|
||||||
|
|
||||||
|
func (p Text) SprintfColored(ca color.Attribute, format string, args ...interface{}) string {
|
||||||
|
if !p.useColors {
|
||||||
|
return fmt.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := color.New(ca)
|
||||||
|
return c.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Text) Print(issues chan result.Issue) (bool, error) {
|
||||||
|
var issuedLineExtractingDuration time.Duration
|
||||||
|
defer func() {
|
||||||
|
logrus.Infof("Extracting issued lines took %s", issuedLineExtractingDuration)
|
||||||
|
}()
|
||||||
|
|
||||||
|
gotAnyIssue := false
|
||||||
|
cache := filesCache{}
|
||||||
|
out := getOutWriter()
|
||||||
|
for i := range issues {
|
||||||
|
gotAnyIssue = true
|
||||||
|
text := p.SprintfColored(color.FgRed, "%s", i.Text)
|
||||||
|
pos := p.SprintfColored(color.Bold, "%s:%d", i.FilePath(), i.Line())
|
||||||
|
fmt.Fprintf(out, "%s: %s\n", pos, text)
|
||||||
|
|
||||||
|
if !p.printIssuedLine {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fc := cache[i.FilePath()]
|
||||||
|
if fc == nil {
|
||||||
|
startedAt := time.Now()
|
||||||
|
// TODO: make more optimal algorithm: don't load all files into memory
|
||||||
|
fileBytes, err := ioutil.ReadFile(i.FilePath())
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("can't read file %s for printing issued line: %s", i.FilePath(), err)
|
||||||
|
}
|
||||||
|
lines := bytes.Split(fileBytes, []byte("\n")) // TODO: what about \r\n?
|
||||||
|
fc = lines
|
||||||
|
cache[i.FilePath()] = fc
|
||||||
|
issuedLineExtractingDuration += time.Since(startedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
lineRange := i.GetLineRange()
|
||||||
|
for line := lineRange.From; line <= lineRange.To; line++ {
|
||||||
|
zeroIndexedLine := line - 1
|
||||||
|
if zeroIndexedLine >= len(fc) {
|
||||||
|
logrus.Warnf("No line %d in file %s", line, i.FilePath())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, string(bytes.Trim(fc[zeroIndexedLine], "\r")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gotAnyIssue {
|
||||||
|
outStr := p.SprintfColored(color.FgGreen, "Congrats! No issues were found.")
|
||||||
|
fmt.Fprintln(out, outStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gotAnyIssue, nil
|
||||||
|
}
|
11
pkg/printers/utils.go
Normal file
11
pkg/printers/utils.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getOutWriter() io.Writer {
|
||||||
|
return os.NewFile(uintptr(syscall.Stdout), "/dev/stdout") // was set to /dev/null
|
||||||
|
}
|
@ -1,27 +1,35 @@
|
|||||||
package result
|
package result
|
||||||
|
|
||||||
|
import "go/token"
|
||||||
|
|
||||||
|
type Range struct {
|
||||||
|
From, To int
|
||||||
|
}
|
||||||
|
|
||||||
type Issue struct {
|
type Issue struct {
|
||||||
FromLinter string
|
FromLinter string
|
||||||
Text string
|
Text string
|
||||||
File string
|
|
||||||
LineNumber int
|
Pos token.Position
|
||||||
|
LineRange Range
|
||||||
HunkPos int
|
HunkPos int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Issue) FilePath() string {
|
func (i Issue) FilePath() string {
|
||||||
return i.File
|
return i.Pos.Filename
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Issue) Line() int {
|
func (i Issue) Line() int {
|
||||||
return i.LineNumber
|
return i.Pos.Line
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIssue(fromLinter, text, file string, lineNumber, hunkPos int) Issue {
|
func (i Issue) GetLineRange() Range {
|
||||||
return Issue{
|
if i.LineRange.From == 0 {
|
||||||
FromLinter: fromLinter,
|
return Range{
|
||||||
Text: text,
|
From: i.Line(),
|
||||||
File: file,
|
To: i.Line(),
|
||||||
LineNumber: lineNumber,
|
|
||||||
HunkPos: hunkPos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return i.LineRange
|
||||||
|
}
|
||||||
|
@ -36,16 +36,16 @@ func (p *MaxPerFileFromLinter) Process(issues []result.Issue) ([]result.Issue, e
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
lm := p.flc[i.File]
|
lm := p.flc[i.FilePath()]
|
||||||
if lm == nil {
|
if lm == nil {
|
||||||
p.flc[i.File] = linterToCountMap{}
|
p.flc[i.FilePath()] = linterToCountMap{}
|
||||||
}
|
}
|
||||||
count := p.flc[i.File][i.FromLinter]
|
count := p.flc[i.FilePath()][i.FromLinter]
|
||||||
if count >= limit {
|
if count >= limit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
p.flc[i.File][i.FromLinter]++
|
p.flc[i.FilePath()][i.FromLinter]++
|
||||||
return true
|
return true
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
@ -39,19 +39,19 @@ func (p *Nolint) Process(issues []result.Issue) ([]result.Issue, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) {
|
func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) {
|
||||||
comments := p.cache[i.File]
|
comments := p.cache[i.FilePath()]
|
||||||
if comments == nil {
|
if comments == nil {
|
||||||
file, err := parser.ParseFile(p.fset, i.File, nil, parser.ParseComments)
|
file, err := parser.ParseFile(p.fset, i.FilePath(), nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
comments = extractFileComments(p.fset, file.Comments...)
|
comments = extractFileComments(p.fset, file.Comments...)
|
||||||
p.cache[i.File] = comments
|
p.cache[i.FilePath()] = comments
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
if comment.line != i.LineNumber {
|
if comment.line != i.Line() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,10 @@ import (
|
|||||||
|
|
||||||
func newNolintFileIssue(line int, fromLinter string) result.Issue {
|
func newNolintFileIssue(line int, fromLinter string) result.Issue {
|
||||||
return result.Issue{
|
return result.Issue{
|
||||||
File: filepath.Join("testdata", "nolint.go"),
|
Pos: token.Position{
|
||||||
LineNumber: line,
|
Filename: filepath.Join("testdata", "nolint.go"),
|
||||||
|
Line: line,
|
||||||
|
},
|
||||||
FromLinter: fromLinter,
|
FromLinter: fromLinter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,17 +30,17 @@ func (p PathPrettifier) Name() string {
|
|||||||
|
|
||||||
func (p PathPrettifier) Process(issues []result.Issue) ([]result.Issue, error) {
|
func (p PathPrettifier) Process(issues []result.Issue) ([]result.Issue, error) {
|
||||||
return transformIssues(issues, func(i *result.Issue) *result.Issue {
|
return transformIssues(issues, func(i *result.Issue) *result.Issue {
|
||||||
if !filepath.IsAbs(i.File) {
|
if !filepath.IsAbs(i.FilePath()) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
rel, err := filepath.Rel(p.root, i.File)
|
rel, err := filepath.Rel(p.root, i.FilePath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
newI := i
|
newI := i
|
||||||
newI.File = rel
|
newI.Pos.Filename = rel
|
||||||
return newI
|
return newI
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
@ -25,19 +25,19 @@ func (p UniqByLine) Name() string {
|
|||||||
|
|
||||||
func (p *UniqByLine) Process(issues []result.Issue) ([]result.Issue, error) {
|
func (p *UniqByLine) Process(issues []result.Issue) ([]result.Issue, error) {
|
||||||
return filterIssues(issues, func(i *result.Issue) bool {
|
return filterIssues(issues, func(i *result.Issue) bool {
|
||||||
lc := p.flc[i.File]
|
lc := p.flc[i.FilePath()]
|
||||||
if lc == nil {
|
if lc == nil {
|
||||||
lc = lineToCount{}
|
lc = lineToCount{}
|
||||||
p.flc[i.File] = lc
|
p.flc[i.FilePath()] = lc
|
||||||
}
|
}
|
||||||
|
|
||||||
const limit = 1
|
const limit = 1
|
||||||
count := lc[i.LineNumber]
|
count := lc[i.Line()]
|
||||||
if count == limit {
|
if count == limit {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
lc[i.LineNumber]++
|
lc[i.Line()]++
|
||||||
return true
|
return true
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package processors
|
package processors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/token"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
@ -8,8 +9,10 @@ import (
|
|||||||
|
|
||||||
func newFLIssue(file string, line int) result.Issue {
|
func newFLIssue(file string, line int) result.Issue {
|
||||||
return result.Issue{
|
return result.Issue{
|
||||||
File: file,
|
Pos: token.Position{
|
||||||
LineNumber: line,
|
Filename: file,
|
||||||
|
Line: line,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user