github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/passes/sortslice/analyzer.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package sortslice defines an Analyzer that checks for calls 6 // to sort.Slice that do not use a slice type as first argument. 7 package sortslice 8 9 import ( 10 "bytes" 11 "fmt" 12 "go/ast" 13 "go/format" 14 "go/types" 15 16 "github.com/powerman/golang-tools/go/analysis" 17 "github.com/powerman/golang-tools/go/analysis/passes/inspect" 18 "github.com/powerman/golang-tools/go/ast/inspector" 19 "github.com/powerman/golang-tools/go/types/typeutil" 20 ) 21 22 const Doc = `check the argument type of sort.Slice 23 24 sort.Slice requires an argument of a slice type. Check that 25 the interface{} value passed to sort.Slice is actually a slice.` 26 27 var Analyzer = &analysis.Analyzer{ 28 Name: "sortslice", 29 Doc: Doc, 30 Requires: []*analysis.Analyzer{inspect.Analyzer}, 31 Run: run, 32 } 33 34 func run(pass *analysis.Pass) (interface{}, error) { 35 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 36 37 nodeFilter := []ast.Node{ 38 (*ast.CallExpr)(nil), 39 } 40 41 inspect.Preorder(nodeFilter, func(n ast.Node) { 42 call := n.(*ast.CallExpr) 43 fn, _ := typeutil.Callee(pass.TypesInfo, call).(*types.Func) 44 if fn == nil { 45 return 46 } 47 48 fnName := fn.FullName() 49 if fnName != "sort.Slice" && fnName != "sort.SliceStable" && fnName != "sort.SliceIsSorted" { 50 return 51 } 52 53 arg := call.Args[0] 54 typ := pass.TypesInfo.Types[arg].Type 55 switch typ.Underlying().(type) { 56 case *types.Slice, *types.Interface: 57 return 58 } 59 60 var fixes []analysis.SuggestedFix 61 switch v := typ.Underlying().(type) { 62 case *types.Array: 63 var buf bytes.Buffer 64 format.Node(&buf, pass.Fset, &ast.SliceExpr{ 65 X: arg, 66 Slice3: false, 67 Lbrack: arg.End() + 1, 68 Rbrack: arg.End() + 3, 69 }) 70 fixes = append(fixes, analysis.SuggestedFix{ 71 Message: "Get a slice of the full array", 72 TextEdits: []analysis.TextEdit{{ 73 Pos: arg.Pos(), 74 End: arg.End(), 75 NewText: buf.Bytes(), 76 }}, 77 }) 78 case *types.Pointer: 79 _, ok := v.Elem().Underlying().(*types.Slice) 80 if !ok { 81 break 82 } 83 var buf bytes.Buffer 84 format.Node(&buf, pass.Fset, &ast.StarExpr{ 85 X: arg, 86 }) 87 fixes = append(fixes, analysis.SuggestedFix{ 88 Message: "Dereference the pointer to the slice", 89 TextEdits: []analysis.TextEdit{{ 90 Pos: arg.Pos(), 91 End: arg.End(), 92 NewText: buf.Bytes(), 93 }}, 94 }) 95 case *types.Signature: 96 if v.Params().Len() != 0 || v.Results().Len() != 1 { 97 break 98 } 99 if _, ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok { 100 break 101 } 102 var buf bytes.Buffer 103 format.Node(&buf, pass.Fset, &ast.CallExpr{ 104 Fun: arg, 105 }) 106 fixes = append(fixes, analysis.SuggestedFix{ 107 Message: "Call the function", 108 TextEdits: []analysis.TextEdit{{ 109 Pos: arg.Pos(), 110 End: arg.End(), 111 NewText: buf.Bytes(), 112 }}, 113 }) 114 } 115 116 pass.Report(analysis.Diagnostic{ 117 Pos: call.Pos(), 118 End: call.End(), 119 Message: fmt.Sprintf("%s's argument must be a slice; is called with %s", fnName, typ.String()), 120 SuggestedFixes: fixes, 121 }) 122 }) 123 return nil, nil 124 }