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  }