github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/result/processors/sort_results.go (about) 1 package processors 2 3 import ( 4 "sort" 5 "strings" 6 7 "github.com/elek/golangci-lint/pkg/config" 8 "github.com/elek/golangci-lint/pkg/result" 9 ) 10 11 // Base propose of this functionality to sort results (issues) 12 // produced by various linters by analyzing code. We achieving this 13 // by sorting results.Issues using processor step, and chain based 14 // rules that can compare different properties of the Issues struct. 15 16 var _ Processor = (*SortResults)(nil) 17 18 type SortResults struct { 19 cmp comparator 20 cfg *config.Config 21 } 22 23 func NewSortResults(cfg *config.Config) *SortResults { 24 // For sorting we are comparing (in next order): file names, line numbers, 25 // position, and finally - giving up. 26 return &SortResults{ 27 cmp: ByName{ 28 next: ByLine{ 29 next: ByColumn{}, 30 }, 31 }, 32 cfg: cfg, 33 } 34 } 35 36 // Process is performing sorting of the result issues. 37 func (sr SortResults) Process(issues []result.Issue) ([]result.Issue, error) { 38 if !sr.cfg.Output.SortResults { 39 return issues, nil 40 } 41 42 sort.Slice(issues, func(i, j int) bool { 43 return sr.cmp.Compare(&issues[i], &issues[j]) == Less 44 }) 45 46 return issues, nil 47 } 48 49 func (sr SortResults) Name() string { return "sort_results" } 50 func (sr SortResults) Finish() {} 51 52 type compareResult int 53 54 const ( 55 Less compareResult = iota - 1 56 Equal 57 Greater 58 None 59 ) 60 61 func (c compareResult) isNeutral() bool { 62 // return true if compare result is incomparable or equal. 63 return c == None || c == Equal 64 } 65 66 //nolint:exhaustive 67 func (c compareResult) String() string { 68 switch c { 69 case Less: 70 return "Less" 71 case Equal: 72 return "Equal" 73 case Greater: 74 return "Greater" 75 } 76 77 return "None" 78 } 79 80 // comparator describe how to implement compare for two "issues" lexicographically 81 type comparator interface { 82 Compare(a, b *result.Issue) compareResult 83 Next() comparator 84 } 85 86 var ( 87 _ comparator = (*ByName)(nil) 88 _ comparator = (*ByLine)(nil) 89 _ comparator = (*ByColumn)(nil) 90 ) 91 92 type ByName struct{ next comparator } 93 94 //nolint:golint 95 func (cmp ByName) Next() comparator { return cmp.next } 96 97 //nolint:golint 98 func (cmp ByName) Compare(a, b *result.Issue) compareResult { 99 var res compareResult 100 101 if res = compareResult(strings.Compare(a.FilePath(), b.FilePath())); !res.isNeutral() { 102 return res 103 } 104 105 if next := cmp.Next(); next != nil { 106 return next.Compare(a, b) 107 } 108 109 return res 110 } 111 112 type ByLine struct{ next comparator } 113 114 //nolint:golint 115 func (cmp ByLine) Next() comparator { return cmp.next } 116 117 //nolint:golint 118 func (cmp ByLine) Compare(a, b *result.Issue) compareResult { 119 var res compareResult 120 121 if res = numericCompare(a.Line(), b.Line()); !res.isNeutral() { 122 return res 123 } 124 125 if next := cmp.Next(); next != nil { 126 return next.Compare(a, b) 127 } 128 129 return res 130 } 131 132 type ByColumn struct{ next comparator } 133 134 //nolint:golint 135 func (cmp ByColumn) Next() comparator { return cmp.next } 136 137 //nolint:golint 138 func (cmp ByColumn) Compare(a, b *result.Issue) compareResult { 139 var res compareResult 140 141 if res = numericCompare(a.Column(), b.Column()); !res.isNeutral() { 142 return res 143 } 144 145 if next := cmp.Next(); next != nil { 146 return next.Compare(a, b) 147 } 148 149 return res 150 } 151 152 func numericCompare(a, b int) compareResult { 153 var ( 154 isValuesInvalid = a < 0 || b < 0 155 isZeroValuesBoth = a == 0 && b == 0 156 isEqual = a == b 157 isZeroValueInA = b > 0 && a == 0 158 isZeroValueInB = a > 0 && b == 0 159 ) 160 161 switch { 162 case isZeroValuesBoth || isEqual: 163 return Equal 164 case isValuesInvalid || isZeroValueInA || isZeroValueInB: 165 return None 166 case a > b: 167 return Greater 168 case a < b: 169 return Less 170 } 171 172 return Equal 173 }