diff --git a/internal/commands/run.go b/internal/commands/run.go index 67f2e163..b2ad91ef 100644 --- a/internal/commands/run.go +++ b/internal/commands/run.go @@ -81,6 +81,8 @@ func (e *Executor) initRun() { runCmd.Flags().DurationVar(&rc.Deadline, "deadline", time.Second*30, "Deadline for total work") runCmd.Flags().StringSliceVarP(&rc.ExcludePatterns, "exclude", "e", config.DefaultExcludePatterns, "Exclude issue by regexp") + + runCmd.Flags().IntVar(&rc.MaxIssuesPerLinter, "max-issues-per-linter", 50, "Maximum issues count per one linter. Set to 0 to disable") } func isFullImportNeeded(linters []pkg.Linter) bool { @@ -198,6 +200,7 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (chan result. processors.NewExclude(fmt.Sprintf("(%s)", strings.Join(e.cfg.Run.ExcludePatterns, "|"))), processors.NewNolint(lintCtx.Program.Fset), processors.NewUniqByLine(), + processors.NewMaxFromLinter(e.cfg.Run.MaxIssuesPerLinter), processors.NewPathPrettifier(), }, } diff --git a/pkg/config/config.go b/pkg/config/config.go index 1178cbde..92998925 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -77,6 +77,8 @@ type Run struct { ExcludePatterns []string Deadline time.Duration + + MaxIssuesPerLinter int } type Config struct { diff --git a/pkg/result/processors/exclude.go b/pkg/result/processors/exclude.go index 98690a70..ae841066 100644 --- a/pkg/result/processors/exclude.go +++ b/pkg/result/processors/exclude.go @@ -35,3 +35,5 @@ func (p Exclude) Process(issues []result.Issue) ([]result.Issue, error) { return !p.pattern.MatchString(i.Text) }), nil } + +func (p Exclude) Finish() {} diff --git a/pkg/result/processors/max_from_linter.go b/pkg/result/processors/max_from_linter.go new file mode 100644 index 00000000..8c9eade4 --- /dev/null +++ b/pkg/result/processors/max_from_linter.go @@ -0,0 +1,44 @@ +package processors + +import ( + "github.com/golangci/golangci-lint/pkg/result" + "github.com/sirupsen/logrus" +) + +type MaxFromLinter struct { + lc linterToCountMap + limit int +} + +var _ Processor = &MaxFromLinter{} + +func NewMaxFromLinter(limit int) *MaxFromLinter { + return &MaxFromLinter{ + lc: linterToCountMap{}, + limit: limit, + } +} + +func (p MaxFromLinter) Name() string { + return "max_from_linter" +} + +func (p *MaxFromLinter) Process(issues []result.Issue) ([]result.Issue, error) { + if p.limit <= 0 { // no limit + return issues, nil + } + + return filterIssues(issues, func(i *result.Issue) bool { + p.lc[i.FromLinter]++ // always inc for stat + return p.lc[i.FromLinter] <= p.limit + }), nil +} + +func (p MaxFromLinter) Finish() { + for linter, count := range p.lc { + if count > p.limit { + logrus.Infof("%d/%d issues from linter %s were hidden, use --max-issues-per-linter", + count-p.limit, count, linter) + } + } +} diff --git a/pkg/result/processors/max_from_linter_test.go b/pkg/result/processors/max_from_linter_test.go new file mode 100644 index 00000000..05ffb632 --- /dev/null +++ b/pkg/result/processors/max_from_linter_test.go @@ -0,0 +1,14 @@ +package processors + +import ( + "testing" +) + +func TestMaxFromLinter(t *testing.T) { + p := NewMaxFromLinter(1) + gosimple := newFromLinterIssue("gosimple") + gofmt := newFromLinterIssue("gofmt") + processAssertSame(t, p, gosimple) // ok + processAssertSame(t, p, gofmt) // ok: another + processAssertEmpty(t, p, gosimple) // skip +} diff --git a/pkg/result/processors/max_per_file_from_linter.go b/pkg/result/processors/max_per_file_from_linter.go index 5f4dffc6..91c413d4 100644 --- a/pkg/result/processors/max_per_file_from_linter.go +++ b/pkg/result/processors/max_per_file_from_linter.go @@ -49,3 +49,5 @@ func (p *MaxPerFileFromLinter) Process(issues []result.Issue) ([]result.Issue, e return true }), nil } + +func (p MaxPerFileFromLinter) Finish() {} diff --git a/pkg/result/processors/nolint.go b/pkg/result/processors/nolint.go index c8682fd1..f4004767 100644 --- a/pkg/result/processors/nolint.go +++ b/pkg/result/processors/nolint.go @@ -94,3 +94,5 @@ func extractFileComments(fset *token.FileSet, comments ...*ast.CommentGroup) fil return ret } + +func (p Nolint) Finish() {} diff --git a/pkg/result/processors/path_prettifier.go b/pkg/result/processors/path_prettifier.go index 40a379d1..043b8973 100644 --- a/pkg/result/processors/path_prettifier.go +++ b/pkg/result/processors/path_prettifier.go @@ -44,3 +44,5 @@ func (p PathPrettifier) Process(issues []result.Issue) ([]result.Issue, error) { return newI }), nil } + +func (p PathPrettifier) Finish() {} diff --git a/pkg/result/processors/processor.go b/pkg/result/processors/processor.go index 5e45a04b..1daec62e 100644 --- a/pkg/result/processors/processor.go +++ b/pkg/result/processors/processor.go @@ -5,4 +5,5 @@ import "github.com/golangci/golangci-lint/pkg/result" type Processor interface { Process(issues []result.Issue) ([]result.Issue, error) Name() string + Finish() } diff --git a/pkg/result/processors/uniq_by_line.go b/pkg/result/processors/uniq_by_line.go index dd7a8072..baae38a2 100644 --- a/pkg/result/processors/uniq_by_line.go +++ b/pkg/result/processors/uniq_by_line.go @@ -41,3 +41,5 @@ func (p *UniqByLine) Process(issues []result.Issue) ([]result.Issue, error) { return true }), nil } + +func (p UniqByLine) Finish() {} diff --git a/pkg/runner.go b/pkg/runner.go index 8699e630..8f05a0e7 100644 --- a/pkg/runner.go +++ b/pkg/runner.go @@ -125,6 +125,11 @@ func (r SimpleRunner) runGo(ctx context.Context, linters []Linter, lintCtx *goli } } + // finalize processors: logging, clearing, no heavy work here + for _, p := range r.Processors { + p.Finish() + } + if ctx.Err() != nil { return fmt.Errorf("%d/%d linters finished: deadline exceeded: try increase it by passing --deadline option", finishedN, len(linters))