Files
odidere/vendor/honnef.co/go/tools/simple/s1032/s1032.go
2026-06-06 01:17:29 +00:00

129 lines
3.2 KiB
Go

package s1032
import (
"go/ast"
"go/token"
"sort"
"honnef.co/go/tools/analysis/code"
"honnef.co/go/tools/analysis/facts/generated"
"honnef.co/go/tools/analysis/lint"
"honnef.co/go/tools/analysis/report"
"honnef.co/go/tools/knowledge"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
)
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
Analyzer: &analysis.Analyzer{
Name: "S1032",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
},
Doc: &lint.RawDocumentation{
Title: `Use \'sort.Ints(x)\', \'sort.Float64s(x)\', and \'sort.Strings(x)\'`,
Text: `The \'sort.Ints\', \'sort.Float64s\' and \'sort.Strings\' functions are easier to
read than \'sort.Sort(sort.IntSlice(x))\', \'sort.Sort(sort.Float64Slice(x))\'
and \'sort.Sort(sort.StringSlice(x))\'.`,
Before: `sort.Sort(sort.StringSlice(x))`,
After: `sort.Strings(x)`,
Since: "2019.1",
MergeIf: lint.MergeIfAny,
},
})
var Analyzer = SCAnalyzer.Analyzer
func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool {
call := node.(*ast.CallExpr)
typeconv, ok := call.Args[0].(*ast.CallExpr)
if !ok {
return true
}
sel, ok := typeconv.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
name := code.SelectorName(pass, sel)
switch name {
case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice":
default:
return true
}
return false
}
func run(pass *analysis.Pass) (any, error) {
type Error struct {
node ast.Node
msg string
}
var allErrors []Error
fn := func(node ast.Node) {
var body *ast.BlockStmt
switch node := node.(type) {
case *ast.FuncLit:
body = node.Body
case *ast.FuncDecl:
body = node.Body
default:
lint.ExhaustiveTypeSwitch(node)
}
if body == nil {
return
}
var errors []Error
permissible := false
fnSorts := func(node ast.Node) bool {
if permissible {
return false
}
if !code.IsCallTo(pass, node, "sort.Sort") {
return true
}
if isPermissibleSort(pass, node) {
permissible = true
return false
}
call := node.(*ast.CallExpr)
// isPermissibleSort guarantees that this type assertion will succeed
typeconv := call.Args[knowledge.Arg("sort.Sort.data")].(*ast.CallExpr)
sel := typeconv.Fun.(*ast.SelectorExpr)
name := code.SelectorName(pass, sel)
switch name {
case "sort.IntSlice":
errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"})
case "sort.Float64Slice":
errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"})
case "sort.StringSlice":
errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"})
}
return true
}
ast.Inspect(body, fnSorts)
if permissible {
return
}
allErrors = append(allErrors, errors...)
}
code.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil))
sort.Slice(allErrors, func(i, j int) bool {
return allErrors[i].node.Pos() < allErrors[j].node.Pos()
})
var prev token.Pos
for _, err := range allErrors {
if err.node.Pos() == prev {
continue
}
prev = err.node.Pos()
report.Report(pass, err.node, err.msg, report.FilterGenerated())
}
return nil, nil
}