From b34b77b9fa6711011744ff98fae20f33297711e6 Mon Sep 17 00:00:00 2001 From: Marccio Silva Date: Thu, 18 Jan 2024 14:39:38 -0300 Subject: [PATCH] Add output option to group results by linter name --- .golangci.reference.yml | 4 ++ pkg/commands/run.go | 1 + pkg/config/output.go | 15 ++++---- pkg/result/processors/sort_results.go | 43 ++++++++++++++++++++++ pkg/result/processors/sort_results_test.go | 31 ++++++++++++++++ 5 files changed, 87 insertions(+), 7 deletions(-) diff --git a/.golangci.reference.yml b/.golangci.reference.yml index f08c10636338..58cc3fd0df3c 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -117,6 +117,10 @@ output: # Default: false sort-results: true + # Group results by linter name. + # Default: false + group-results-by-linter: false + # All available settings of specific linters. linters-settings: diff --git a/pkg/commands/run.go b/pkg/commands/run.go index a082d7adfc91..16fd0df341ae 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -86,6 +86,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is fs.BoolVar(&oc.PrintLinterName, "print-linter-name", true, wh("Print linter name in issue line")) fs.BoolVar(&oc.UniqByLine, "uniq-by-line", true, wh("Make issues output unique by line")) fs.BoolVar(&oc.SortResults, "sort-results", false, wh("Sort linter results")) + fs.BoolVar(&oc.GroupResultsByLinter, "group-results-by-linter", false, wh("Group results by linter name")) fs.BoolVar(&oc.PrintWelcomeMessage, "print-welcome", false, wh("Print welcome message")) fs.StringVar(&oc.PathPrefix, "path-prefix", "", wh("Path prefix to add to output")) hideFlag("print-welcome") // no longer used diff --git a/pkg/config/output.go b/pkg/config/output.go index e8726392055d..5ee4a7039ba8 100644 --- a/pkg/config/output.go +++ b/pkg/config/output.go @@ -28,13 +28,14 @@ var OutFormats = []string{ } type Output struct { - Format string - PrintIssuedLine bool `mapstructure:"print-issued-lines"` - PrintLinterName bool `mapstructure:"print-linter-name"` - UniqByLine bool `mapstructure:"uniq-by-line"` - SortResults bool `mapstructure:"sort-results"` - PrintWelcomeMessage bool `mapstructure:"print-welcome"` - PathPrefix string `mapstructure:"path-prefix"` + Format string + PrintIssuedLine bool `mapstructure:"print-issued-lines"` + PrintLinterName bool `mapstructure:"print-linter-name"` + UniqByLine bool `mapstructure:"uniq-by-line"` + SortResults bool `mapstructure:"sort-results"` + GroupResultsByLinter bool `mapstructure:"group-results-by-linter"` + PrintWelcomeMessage bool `mapstructure:"print-welcome"` + PathPrefix string `mapstructure:"path-prefix"` // only work with CLI flags because the setup of logs is done before the config file parsing. Color string diff --git a/pkg/result/processors/sort_results.go b/pkg/result/processors/sort_results.go index 740c4fa8c37a..cfe092468823 100644 --- a/pkg/result/processors/sort_results.go +++ b/pkg/result/processors/sort_results.go @@ -1,6 +1,7 @@ package processors import ( + "fmt" "sort" "strings" @@ -35,6 +36,48 @@ func NewSortResults(cfg *config.Config) *SortResults { // Process is performing sorting of the result issues. func (sr SortResults) Process(issues []result.Issue) ([]result.Issue, error) { + if sr.cfg.Output.GroupResultsByLinter { + issuesByLinterName := sr.groupIssuesByLinterName(issues) + return sr.processIssuesByLinterName(issuesByLinterName) + } + return sr.processFlatIssues(issues) +} + +func (sr SortResults) groupIssuesByLinterName(issues []result.Issue) map[string][]result.Issue { + issuesByLinterName := map[string][]result.Issue{} + for _, issue := range issues { + if _, ok := issuesByLinterName[issue.FromLinter]; !ok { + issuesByLinterName[issue.FromLinter] = []result.Issue{} + } + issuesByLinterName[issue.FromLinter] = append(issuesByLinterName[issue.FromLinter], issue) + } + return issuesByLinterName +} + +func (sr SortResults) processIssuesByLinterName(issuesByLinterName map[string][]result.Issue) ([]result.Issue, error) { + linterNames := sr.getSortedLinterNames(issuesByLinterName) + var processedIssues []result.Issue + for _, linterName := range linterNames { + linterIssues := issuesByLinterName[linterName] + processedLinterIssues, err := sr.processFlatIssues(linterIssues) + if err != nil { + return nil, fmt.Errorf("failed to process issues from %s linter: %w", linterName, err) + } + processedIssues = append(processedIssues, processedLinterIssues...) + } + return processedIssues, nil +} + +func (sr SortResults) getSortedLinterNames(issuesByLinterName map[string][]result.Issue) []string { + linterNames := make([]string, 0, len(issuesByLinterName)) + for linterName := range issuesByLinterName { + linterNames = append(linterNames, linterName) + } + sort.Strings(linterNames) + return linterNames +} + +func (sr SortResults) processFlatIssues(issues []result.Issue) ([]result.Issue, error) { if !sr.cfg.Output.SortResults { return issues, nil } diff --git a/pkg/result/processors/sort_results_test.go b/pkg/result/processors/sort_results_test.go index 141db72359e2..7ec7fd49e7a9 100644 --- a/pkg/result/processors/sort_results_test.go +++ b/pkg/result/processors/sort_results_test.go @@ -14,6 +14,7 @@ import ( var issues = []result.Issue{ { + FromLinter: "linter-a", Pos: token.Position{ Filename: "file_windows.go", Column: 80, @@ -21,6 +22,7 @@ var issues = []result.Issue{ }, }, { + FromLinter: "linter-b", Pos: token.Position{ Filename: "file_linux.go", Column: 70, @@ -28,12 +30,14 @@ var issues = []result.Issue{ }, }, { + FromLinter: "linter-a", Pos: token.Position{ Filename: "file_darwin.go", Line: 12, }, }, { + FromLinter: "linter-b", Pos: token.Position{ Filename: "file_darwin.go", Column: 60, @@ -175,3 +179,30 @@ func TestSorting(t *testing.T) { require.NoError(t, err) assert.Equal(t, []result.Issue{issues[3], issues[2], issues[1], issues[0]}, results) } + +func TestGroupingByLinterName(t *testing.T) { + var tests = make([]result.Issue, len(issues)) + copy(tests, issues) + + var cfg = config.Config{} + cfg.Output.GroupResultsByLinter = true + var sr = NewSortResults(&cfg) + + results, err := sr.Process(tests) + require.NoError(t, err) + assert.Equal(t, []result.Issue{issues[0], issues[2], issues[1], issues[3]}, results) +} + +func TestSortingAndGroupingByLinterName(t *testing.T) { + var tests = make([]result.Issue, len(issues)) + copy(tests, issues) + + var cfg = config.Config{} + cfg.Output.SortResults = true + cfg.Output.GroupResultsByLinter = true + var sr = NewSortResults(&cfg) + + results, err := sr.Process(tests) + require.NoError(t, err) + assert.Equal(t, []result.Issue{issues[2], issues[0], issues[3], issues[1]}, results) +}