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  }