1 package sa1028
2 3 import (
4 "fmt"
5 "go/types"
6 7 "honnef.co/go/tools/analysis/callcheck"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/go/tools/go/ir"
10 "honnef.co/go/tools/internal/passes/buildir"
11 12 "golang.org/x/tools/go/analysis"
13 )
14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
16 Analyzer: &analysis.Analyzer{
17 Name: "SA1028",
18 Requires: []*analysis.Analyzer{buildir.Analyzer},
19 Run: callcheck.Analyzer(rules),
20 },
21 Doc: &lint.RawDocumentation{
22 Title: `\'sort.Slice\' can only be used on slices`,
23 Text: `The first argument of \'sort.Slice\' must be a slice.`,
24 Since: "2020.1",
25 Severity: lint.SeverityError,
26 MergeIf: lint.MergeIfAny,
27 },
28 })
29 30 var Analyzer = SCAnalyzer.Analyzer
31 32 var rules = map[string]callcheck.Check{
33 "sort.Slice": check,
34 "sort.SliceIsSorted": check,
35 "sort.SliceStable": check,
36 }
37 38 func check(call *callcheck.Call) {
39 c := call.Instr.Common().StaticCallee()
40 arg := call.Args[0]
41 42 T := arg.Value.Value.Type().Underlying()
43 switch T.(type) {
44 case *types.Interface:
45 // we don't know.
46 // TODO(dh): if the value is a phi node we can look at its edges
47 if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil {
48 // literal nil, e.g. sort.Sort(nil, ...)
49 arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c))
50 }
51 case *types.Slice:
52 // this is fine
53 default:
54 // this is not fine
55 arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T))
56 }
57 }
58