174 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package processors
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/golangci/golangci-lint/pkg/config"
 | |
| 	"github.com/golangci/golangci-lint/pkg/result"
 | |
| )
 | |
| 
 | |
| // Base propose of this functionality to sort results (issues)
 | |
| // produced by various linters by analyzing code. We achieving this
 | |
| // by sorting results.Issues using processor step, and chain based
 | |
| // rules that can compare different properties of the Issues struct.
 | |
| 
 | |
| var _ Processor = (*SortResults)(nil)
 | |
| 
 | |
| type SortResults struct {
 | |
| 	cmp comparator
 | |
| 	cfg *config.Config
 | |
| }
 | |
| 
 | |
| func NewSortResults(cfg *config.Config) *SortResults {
 | |
| 	// For sorting we are comparing (in next order): file names, line numbers,
 | |
| 	// position, and finally - giving up.
 | |
| 	return &SortResults{
 | |
| 		cmp: ByName{
 | |
| 			next: ByLine{
 | |
| 				next: ByColumn{},
 | |
| 			},
 | |
| 		},
 | |
| 		cfg: cfg,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Process is performing sorting of the result issues.
 | |
| func (sr SortResults) Process(issues []result.Issue) ([]result.Issue, error) {
 | |
| 	if !sr.cfg.Output.SortResults {
 | |
| 		return issues, nil
 | |
| 	}
 | |
| 
 | |
| 	sort.Slice(issues, func(i, j int) bool {
 | |
| 		return sr.cmp.Compare(&issues[i], &issues[j]) == Less
 | |
| 	})
 | |
| 
 | |
| 	return issues, nil
 | |
| }
 | |
| 
 | |
| func (sr SortResults) Name() string { return "sort_results" }
 | |
| func (sr SortResults) Finish()      {}
 | |
| 
 | |
| type compareResult int
 | |
| 
 | |
| const (
 | |
| 	Less compareResult = iota - 1
 | |
| 	Equal
 | |
| 	Greater
 | |
| 	None
 | |
| )
 | |
| 
 | |
| func (c compareResult) isNeutral() bool {
 | |
| 	// return true if compare result is incomparable or equal.
 | |
| 	return c == None || c == Equal
 | |
| }
 | |
| 
 | |
| //nolint:exhaustive
 | |
| func (c compareResult) String() string {
 | |
| 	switch c {
 | |
| 	case Less:
 | |
| 		return "Less"
 | |
| 	case Equal:
 | |
| 		return "Equal"
 | |
| 	case Greater:
 | |
| 		return "Greater"
 | |
| 	}
 | |
| 
 | |
| 	return "None"
 | |
| }
 | |
| 
 | |
| // comparator describe how to implement compare for two "issues" lexicographically
 | |
| type comparator interface {
 | |
| 	Compare(a, b *result.Issue) compareResult
 | |
| 	Next() comparator
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ comparator = (*ByName)(nil)
 | |
| 	_ comparator = (*ByLine)(nil)
 | |
| 	_ comparator = (*ByColumn)(nil)
 | |
| )
 | |
| 
 | |
| type ByName struct{ next comparator }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByName) Next() comparator { return cmp.next }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByName) Compare(a, b *result.Issue) compareResult {
 | |
| 	var res compareResult
 | |
| 
 | |
| 	if res = compareResult(strings.Compare(a.FilePath(), b.FilePath())); !res.isNeutral() {
 | |
| 		return res
 | |
| 	}
 | |
| 
 | |
| 	if next := cmp.Next(); next != nil {
 | |
| 		return next.Compare(a, b)
 | |
| 	}
 | |
| 
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| type ByLine struct{ next comparator }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByLine) Next() comparator { return cmp.next }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByLine) Compare(a, b *result.Issue) compareResult {
 | |
| 	var res compareResult
 | |
| 
 | |
| 	if res = numericCompare(a.Line(), b.Line()); !res.isNeutral() {
 | |
| 		return res
 | |
| 	}
 | |
| 
 | |
| 	if next := cmp.Next(); next != nil {
 | |
| 		return next.Compare(a, b)
 | |
| 	}
 | |
| 
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| type ByColumn struct{ next comparator }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByColumn) Next() comparator { return cmp.next }
 | |
| 
 | |
| //nolint:golint
 | |
| func (cmp ByColumn) Compare(a, b *result.Issue) compareResult {
 | |
| 	var res compareResult
 | |
| 
 | |
| 	if res = numericCompare(a.Column(), b.Column()); !res.isNeutral() {
 | |
| 		return res
 | |
| 	}
 | |
| 
 | |
| 	if next := cmp.Next(); next != nil {
 | |
| 		return next.Compare(a, b)
 | |
| 	}
 | |
| 
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| func numericCompare(a, b int) compareResult {
 | |
| 	var (
 | |
| 		isValuesInvalid  = a < 0 || b < 0
 | |
| 		isZeroValuesBoth = a == 0 && b == 0
 | |
| 		isEqual          = a == b
 | |
| 		isZeroValueInA   = b > 0 && a == 0
 | |
| 		isZeroValueInB   = a > 0 && b == 0
 | |
| 	)
 | |
| 
 | |
| 	switch {
 | |
| 	case isZeroValuesBoth || isEqual:
 | |
| 		return Equal
 | |
| 	case isValuesInvalid || isZeroValueInA || isZeroValueInB:
 | |
| 		return None
 | |
| 	case a > b:
 | |
| 		return Greater
 | |
| 	case a < b:
 | |
| 		return Less
 | |
| 	}
 | |
| 
 | |
| 	return Equal
 | |
| }
 | 
