Skip to content

Commit af9b92a

Browse files
authored
dev: simplify sort results processors (#5150)
1 parent e0e37c4 commit af9b92a

File tree

2 files changed

+108
-254
lines changed

2 files changed

+108
-254
lines changed

pkg/result/processors/sort_results.go

+50-161
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package processors
22

33
import (
4-
"errors"
4+
"cmp"
55
"fmt"
66
"slices"
7-
"sort"
87
"strings"
98

109
"github.com/golangci/golangci-lint/pkg/config"
@@ -22,24 +21,32 @@ const (
2221
orderNameSeverity = "severity"
2322
)
2423

24+
const (
25+
less = iota - 1
26+
equal
27+
greater
28+
)
29+
2530
var _ Processor = (*SortResults)(nil)
2631

32+
type issueComparator func(a, b *result.Issue) int
33+
2734
type SortResults struct {
28-
cmps map[string]*comparator
35+
cmps map[string][]issueComparator
2936

3037
cfg *config.Output
3138
}
3239

3340
func NewSortResults(cfg *config.Config) *SortResults {
3441
return &SortResults{
35-
cmps: map[string]*comparator{
42+
cmps: map[string][]issueComparator{
3643
// For sorting we are comparing (in next order):
3744
// file names, line numbers, position, and finally - giving up.
38-
orderNameFile: byFileName().SetNext(byLine().SetNext(byColumn())),
45+
orderNameFile: {byFileName, byLine, byColumn},
3946
// For sorting we are comparing: linter name
40-
orderNameLinter: byLinter(),
47+
orderNameLinter: {byLinter},
4148
// For sorting we are comparing: severity
42-
orderNameSeverity: bySeverity(),
49+
orderNameSeverity: {bySeverity},
4350
},
4451
cfg: &cfg.Output,
4552
}
@@ -57,171 +64,54 @@ func (p SortResults) Process(issues []result.Issue) ([]result.Issue, error) {
5764
p.cfg.SortOrder = []string{orderNameFile}
5865
}
5966

60-
var cmps []*comparator
67+
var cmps []issueComparator
68+
6169
for _, name := range p.cfg.SortOrder {
6270
c, ok := p.cmps[name]
6371
if !ok {
6472
return nil, fmt.Errorf("unsupported sort-order name %q", name)
6573
}
6674

67-
cmps = append(cmps, c)
75+
cmps = append(cmps, c...)
6876
}
6977

70-
cmp, err := mergeComparators(cmps)
71-
if err != nil {
72-
return nil, err
73-
}
78+
comp := mergeComparators(cmps...)
7479

75-
sort.Slice(issues, func(i, j int) bool {
76-
return cmp.Compare(&issues[i], &issues[j]) == less
80+
slices.SortFunc(issues, func(a, b result.Issue) int {
81+
return comp(&a, &b)
7782
})
7883

7984
return issues, nil
8085
}
8186

8287
func (SortResults) Finish() {}
8388

84-
type compareResult int
85-
86-
const (
87-
less compareResult = iota - 1
88-
equal
89-
greater
90-
none
91-
)
92-
93-
func (c compareResult) isNeutral() bool {
94-
// return true if compare result is incomparable or equal.
95-
return c == none || c == equal
96-
}
97-
98-
func (c compareResult) String() string {
99-
switch c {
100-
case less:
101-
return "less"
102-
case equal:
103-
return "equal"
104-
case greater:
105-
return "greater"
106-
default:
107-
return "none"
108-
}
109-
}
110-
111-
// comparator describes how to implement compare for two "issues".
112-
type comparator struct {
113-
name string
114-
compare func(a, b *result.Issue) compareResult
115-
next *comparator
116-
}
117-
118-
func (cmp *comparator) Next() *comparator { return cmp.next }
119-
120-
func (cmp *comparator) SetNext(c *comparator) *comparator {
121-
cmp.next = c
122-
return cmp
89+
func byFileName(a, b *result.Issue) int {
90+
return strings.Compare(a.FilePath(), b.FilePath())
12391
}
12492

125-
func (cmp *comparator) String() string {
126-
s := cmp.name
127-
if cmp.Next() != nil {
128-
s += " > " + cmp.Next().String()
129-
}
130-
131-
return s
93+
func byLine(a, b *result.Issue) int {
94+
return numericCompare(a.Line(), b.Line())
13295
}
13396

134-
func (cmp *comparator) Compare(a, b *result.Issue) compareResult {
135-
res := cmp.compare(a, b)
136-
if !res.isNeutral() {
137-
return res
138-
}
139-
140-
if next := cmp.Next(); next != nil {
141-
return next.Compare(a, b)
142-
}
143-
144-
return res
97+
func byColumn(a, b *result.Issue) int {
98+
return numericCompare(a.Column(), b.Column())
14599
}
146100

147-
func byFileName() *comparator {
148-
return &comparator{
149-
name: "byFileName",
150-
compare: func(a, b *result.Issue) compareResult {
151-
return compareResult(strings.Compare(a.FilePath(), b.FilePath()))
152-
},
153-
}
101+
func byLinter(a, b *result.Issue) int {
102+
return strings.Compare(a.FromLinter, b.FromLinter)
154103
}
155104

156-
func byLine() *comparator {
157-
return &comparator{
158-
name: "byLine",
159-
compare: func(a, b *result.Issue) compareResult {
160-
return numericCompare(a.Line(), b.Line())
161-
},
162-
}
105+
func bySeverity(a, b *result.Issue) int {
106+
return severityCompare(a.Severity, b.Severity)
163107
}
164108

165-
func byColumn() *comparator {
166-
return &comparator{
167-
name: "byColumn",
168-
compare: func(a, b *result.Issue) compareResult {
169-
return numericCompare(a.Column(), b.Column())
170-
},
171-
}
172-
}
173-
174-
func byLinter() *comparator {
175-
return &comparator{
176-
name: "byLinter",
177-
compare: func(a, b *result.Issue) compareResult {
178-
return compareResult(strings.Compare(a.FromLinter, b.FromLinter))
179-
},
180-
}
181-
}
182-
183-
func bySeverity() *comparator {
184-
return &comparator{
185-
name: "bySeverity",
186-
compare: func(a, b *result.Issue) compareResult {
187-
return severityCompare(a.Severity, b.Severity)
188-
},
189-
}
190-
}
191-
192-
func mergeComparators(cmps []*comparator) (*comparator, error) {
193-
if len(cmps) == 0 {
194-
return nil, errors.New("no comparator")
195-
}
196-
197-
for i := range len(cmps) - 1 {
198-
findComparatorTip(cmps[i]).SetNext(cmps[i+1])
199-
}
200-
201-
return cmps[0], nil
202-
}
203-
204-
func findComparatorTip(cmp *comparator) *comparator {
205-
if cmp.Next() != nil {
206-
return findComparatorTip(cmp.Next())
207-
}
208-
209-
return cmp
210-
}
211-
212-
func severityCompare(a, b string) compareResult {
109+
func severityCompare(a, b string) int {
213110
// The position inside the slice define the importance (lower to higher).
214111
classic := []string{"low", "medium", "high", "warning", "error"}
215112

216113
if slices.Contains(classic, a) && slices.Contains(classic, b) {
217-
switch {
218-
case slices.Index(classic, a) > slices.Index(classic, b):
219-
return greater
220-
case slices.Index(classic, a) < slices.Index(classic, b):
221-
return less
222-
default:
223-
return equal
224-
}
114+
return cmp.Compare(slices.Index(classic, a), slices.Index(classic, b))
225115
}
226116

227117
if slices.Contains(classic, a) {
@@ -232,28 +122,27 @@ func severityCompare(a, b string) compareResult {
232122
return less
233123
}
234124

235-
return compareResult(strings.Compare(a, b))
125+
return strings.Compare(a, b)
236126
}
237127

238-
func numericCompare(a, b int) compareResult {
239-
var (
240-
isValuesInvalid = a < 0 || b < 0
241-
isZeroValuesBoth = a == 0 && b == 0
242-
isEqual = a == b
243-
isZeroValueInA = b > 0 && a == 0
244-
isZeroValueInB = a > 0 && b == 0
245-
)
246-
247-
switch {
248-
case isZeroValuesBoth || isEqual:
128+
func numericCompare(a, b int) int {
129+
// Negative values and zeros are skipped (equal) because they either invalid or "neutral" (default int value).
130+
if a <= 0 || b <= 0 {
249131
return equal
250-
case isValuesInvalid || isZeroValueInA || isZeroValueInB:
251-
return none
252-
case a > b:
253-
return greater
254-
case a < b:
255-
return less
256132
}
257133

258-
return equal
134+
return cmp.Compare(a, b)
135+
}
136+
137+
func mergeComparators(comps ...issueComparator) issueComparator {
138+
return func(a, b *result.Issue) int {
139+
for _, comp := range comps {
140+
i := comp(a, b)
141+
if i != equal {
142+
return i
143+
}
144+
}
145+
146+
return equal
147+
}
259148
}

0 commit comments

Comments
 (0)