github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/result/processors/sort_results.go (about) 1 package processors 2 3 import ( 4 "sort" 5 "strings" 6 7 "github.com/chenfeining/golangci-lint/pkg/config" 8 "github.com/chenfeining/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're 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 func (c compareResult) String() string { 67 switch c { 68 case Less: 69 return "Less" 70 case Equal: 71 return "Equal" 72 case Greater: 73 return "Greater" 74 } 75 76 return "None" 77 } 78 79 // comparator describe how to implement compare for two "issues" lexicographically 80 type comparator interface { 81 Compare(a, b *result.Issue) compareResult 82 Next() comparator 83 } 84 85 var ( 86 _ comparator = (*ByName)(nil) 87 _ comparator = (*ByLine)(nil) 88 _ comparator = (*ByColumn)(nil) 89 ) 90 91 type ByName struct{ next comparator } 92 93 func (cmp ByName) Next() comparator { return cmp.next } 94 95 func (cmp ByName) Compare(a, b *result.Issue) compareResult { 96 var res compareResult 97 98 if res = compareResult(strings.Compare(a.FilePath(), b.FilePath())); !res.isNeutral() { 99 return res 100 } 101 102 if next := cmp.Next(); next != nil { 103 return next.Compare(a, b) 104 } 105 106 return res 107 } 108 109 type ByLine struct{ next comparator } 110 111 func (cmp ByLine) Next() comparator { return cmp.next } 112 113 func (cmp ByLine) Compare(a, b *result.Issue) compareResult { 114 var res compareResult 115 116 if res = numericCompare(a.Line(), b.Line()); !res.isNeutral() { 117 return res 118 } 119 120 if next := cmp.Next(); next != nil { 121 return next.Compare(a, b) 122 } 123 124 return res 125 } 126 127 type ByColumn struct{ next comparator } 128 129 func (cmp ByColumn) Next() comparator { return cmp.next } 130 131 func (cmp ByColumn) Compare(a, b *result.Issue) compareResult { 132 var res compareResult 133 134 if res = numericCompare(a.Column(), b.Column()); !res.isNeutral() { 135 return res 136 } 137 138 if next := cmp.Next(); next != nil { 139 return next.Compare(a, b) 140 } 141 142 return res 143 } 144 145 func numericCompare(a, b int) compareResult { 146 var ( 147 isValuesInvalid = a < 0 || b < 0 148 isZeroValuesBoth = a == 0 && b == 0 149 isEqual = a == b 150 isZeroValueInA = b > 0 && a == 0 151 isZeroValueInB = a > 0 && b == 0 152 ) 153 154 switch { 155 case isZeroValuesBoth || isEqual: 156 return Equal 157 case isValuesInvalid || isZeroValueInA || isZeroValueInB: 158 return None 159 case a > b: 160 return Greater 161 case a < b: 162 return Less 163 } 164 165 return Equal 166 }