+++ /dev/null
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package sortslice defines an Analyzer that checks for calls
-// to sort.Slice that do not use a slice type as first argument.
-package sortslice
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/format"
- "go/types"
-
- "golang.org/x/tools/go/analysis"
- "golang.org/x/tools/go/analysis/passes/inspect"
- "golang.org/x/tools/go/ast/inspector"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-const Doc = `check the argument type of sort.Slice
-
-sort.Slice requires an argument of a slice type. Check that
-the interface{} value passed to sort.Slice is actually a slice.`
-
-var Analyzer = &analysis.Analyzer{
- Name: "sortslice",
- Doc: Doc,
- Requires: []*analysis.Analyzer{inspect.Analyzer},
- Run: run,
-}
-
-func run(pass *analysis.Pass) (interface{}, error) {
- inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
-
- nodeFilter := []ast.Node{
- (*ast.CallExpr)(nil),
- }
-
- inspect.Preorder(nodeFilter, func(n ast.Node) {
- call := n.(*ast.CallExpr)
- fn, _ := typeutil.Callee(pass.TypesInfo, call).(*types.Func)
- if fn == nil {
- return
- }
-
- if fn.FullName() != "sort.Slice" {
- return
- }
-
- arg := call.Args[0]
- typ := pass.TypesInfo.Types[arg].Type
- switch typ.Underlying().(type) {
- case *types.Slice, *types.Interface:
- return
- }
-
- var fixes []analysis.SuggestedFix
- switch v := typ.Underlying().(type) {
- case *types.Array:
- var buf bytes.Buffer
- format.Node(&buf, pass.Fset, &ast.SliceExpr{
- X: arg,
- Slice3: false,
- Lbrack: arg.End() + 1,
- Rbrack: arg.End() + 3,
- })
- fixes = append(fixes, analysis.SuggestedFix{
- Message: "Get a slice of the full array",
- TextEdits: []analysis.TextEdit{{
- Pos: arg.Pos(),
- End: arg.End(),
- NewText: buf.Bytes(),
- }},
- })
- case *types.Pointer:
- _, ok := v.Elem().Underlying().(*types.Slice)
- if !ok {
- break
- }
- var buf bytes.Buffer
- format.Node(&buf, pass.Fset, &ast.StarExpr{
- X: arg,
- })
- fixes = append(fixes, analysis.SuggestedFix{
- Message: "Dereference the pointer to the slice",
- TextEdits: []analysis.TextEdit{{
- Pos: arg.Pos(),
- End: arg.End(),
- NewText: buf.Bytes(),
- }},
- })
- case *types.Signature:
- if v.Params().Len() != 0 || v.Results().Len() != 1 {
- break
- }
- if _, ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok {
- break
- }
- var buf bytes.Buffer
- format.Node(&buf, pass.Fset, &ast.CallExpr{
- Fun: arg,
- })
- fixes = append(fixes, analysis.SuggestedFix{
- Message: "Call the function",
- TextEdits: []analysis.TextEdit{{
- Pos: arg.Pos(),
- End: arg.End(),
- NewText: buf.Bytes(),
- }},
- })
- }
-
- pass.Report(analysis.Diagnostic{
- Pos: call.Pos(),
- End: call.End(),
- Message: fmt.Sprintf("sort.Slice's argument must be a slice; is called with %s", typ.String()),
- SuggestedFixes: fixes,
- })
- })
- return nil, nil
-}