s1032.go raw

   1  package s1032
   2  
   3  import (
   4  	"go/ast"
   5  	"go/token"
   6  	"sort"
   7  
   8  	"honnef.co/go/tools/analysis/code"
   9  	"honnef.co/go/tools/analysis/facts/generated"
  10  	"honnef.co/go/tools/analysis/lint"
  11  	"honnef.co/go/tools/analysis/report"
  12  	"honnef.co/go/tools/knowledge"
  13  
  14  	"golang.org/x/tools/go/analysis"
  15  	"golang.org/x/tools/go/analysis/passes/inspect"
  16  )
  17  
  18  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  19  	Analyzer: &analysis.Analyzer{
  20  		Name:     "S1032",
  21  		Run:      run,
  22  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  23  	},
  24  	Doc: &lint.RawDocumentation{
  25  		Title: `Use \'sort.Ints(x)\', \'sort.Float64s(x)\', and \'sort.Strings(x)\'`,
  26  		Text: `The \'sort.Ints\', \'sort.Float64s\' and \'sort.Strings\' functions are easier to
  27  read than \'sort.Sort(sort.IntSlice(x))\', \'sort.Sort(sort.Float64Slice(x))\'
  28  and \'sort.Sort(sort.StringSlice(x))\'.`,
  29  		Before:  `sort.Sort(sort.StringSlice(x))`,
  30  		After:   `sort.Strings(x)`,
  31  		Since:   "2019.1",
  32  		MergeIf: lint.MergeIfAny,
  33  	},
  34  })
  35  
  36  var Analyzer = SCAnalyzer.Analyzer
  37  
  38  func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool {
  39  	call := node.(*ast.CallExpr)
  40  	typeconv, ok := call.Args[0].(*ast.CallExpr)
  41  	if !ok {
  42  		return true
  43  	}
  44  
  45  	sel, ok := typeconv.Fun.(*ast.SelectorExpr)
  46  	if !ok {
  47  		return true
  48  	}
  49  	name := code.SelectorName(pass, sel)
  50  	switch name {
  51  	case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice":
  52  	default:
  53  		return true
  54  	}
  55  
  56  	return false
  57  }
  58  
  59  func run(pass *analysis.Pass) (interface{}, error) {
  60  	type Error struct {
  61  		node ast.Node
  62  		msg  string
  63  	}
  64  	var allErrors []Error
  65  	fn := func(node ast.Node) {
  66  		var body *ast.BlockStmt
  67  		switch node := node.(type) {
  68  		case *ast.FuncLit:
  69  			body = node.Body
  70  		case *ast.FuncDecl:
  71  			body = node.Body
  72  		default:
  73  			lint.ExhaustiveTypeSwitch(node)
  74  		}
  75  		if body == nil {
  76  			return
  77  		}
  78  
  79  		var errors []Error
  80  		permissible := false
  81  		fnSorts := func(node ast.Node) bool {
  82  			if permissible {
  83  				return false
  84  			}
  85  			if !code.IsCallTo(pass, node, "sort.Sort") {
  86  				return true
  87  			}
  88  			if isPermissibleSort(pass, node) {
  89  				permissible = true
  90  				return false
  91  			}
  92  			call := node.(*ast.CallExpr)
  93  			// isPermissibleSort guarantees that this type assertion will succeed
  94  			typeconv := call.Args[knowledge.Arg("sort.Sort.data")].(*ast.CallExpr)
  95  			sel := typeconv.Fun.(*ast.SelectorExpr)
  96  			name := code.SelectorName(pass, sel)
  97  
  98  			switch name {
  99  			case "sort.IntSlice":
 100  				errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"})
 101  			case "sort.Float64Slice":
 102  				errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"})
 103  			case "sort.StringSlice":
 104  				errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"})
 105  			}
 106  			return true
 107  		}
 108  		ast.Inspect(body, fnSorts)
 109  
 110  		if permissible {
 111  			return
 112  		}
 113  		allErrors = append(allErrors, errors...)
 114  	}
 115  	code.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil))
 116  	sort.Slice(allErrors, func(i, j int) bool {
 117  		return allErrors[i].node.Pos() < allErrors[j].node.Pos()
 118  	})
 119  	var prev token.Pos
 120  	for _, err := range allErrors {
 121  		if err.node.Pos() == prev {
 122  			continue
 123  		}
 124  		prev = err.node.Pos()
 125  		report.Report(pass, err.node, err.msg, report.FilterGenerated())
 126  	}
 127  	return nil, nil
 128  }
 129