github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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 "golang.org/x/tools/go/analysis" 17 "golang.org/x/tools/go/analysis/passes/inspect" 18 "golang.org/x/tools/go/ast/inspector" 19 "golang.org/x/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 56 if tuple, ok := typ.(*types.Tuple); ok { 57 typ = tuple.At(0).Type() // special case for Slice(f(...)) 58 } 59 60 switch typ.Underlying().(type) { 61 case *types.Slice, *types.Interface: 62 return 63 } 64 65 // Restore typ to the original type, we may unwrap the tuple above, 66 // typ might not be the type of arg. 67 typ = pass.TypesInfo.Types[arg].Type 68 69 var fixes []analysis.SuggestedFix 70 switch v := typ.Underlying().(type) { 71 case *types.Array: 72 var buf bytes.Buffer 73 format.Node(&buf, pass.Fset, &ast.SliceExpr{ 74 X: arg, 75 Slice3: false, 76 Lbrack: arg.End() + 1, 77 Rbrack: arg.End() + 3, 78 }) 79 fixes = append(fixes, analysis.SuggestedFix{ 80 Message: "Get a slice of the full array", 81 TextEdits: []analysis.TextEdit{{ 82 Pos: arg.Pos(), 83 End: arg.End(), 84 NewText: buf.Bytes(), 85 }}, 86 }) 87 case *types.Pointer: 88 _, ok := v.Elem().Underlying().(*types.Slice) 89 if !ok { 90 break 91 } 92 var buf bytes.Buffer 93 format.Node(&buf, pass.Fset, &ast.StarExpr{ 94 X: arg, 95 }) 96 fixes = append(fixes, analysis.SuggestedFix{ 97 Message: "Dereference the pointer to the slice", 98 TextEdits: []analysis.TextEdit{{ 99 Pos: arg.Pos(), 100 End: arg.End(), 101 NewText: buf.Bytes(), 102 }}, 103 }) 104 case *types.Signature: 105 if v.Params().Len() != 0 || v.Results().Len() != 1 { 106 break 107 } 108 if _, ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok { 109 break 110 } 111 var buf bytes.Buffer 112 format.Node(&buf, pass.Fset, &ast.CallExpr{ 113 Fun: arg, 114 }) 115 fixes = append(fixes, analysis.SuggestedFix{ 116 Message: "Call the function", 117 TextEdits: []analysis.TextEdit{{ 118 Pos: arg.Pos(), 119 End: arg.End(), 120 NewText: buf.Bytes(), 121 }}, 122 }) 123 } 124 125 pass.Report(analysis.Diagnostic{ 126 Pos: call.Pos(), 127 End: call.End(), 128 Message: fmt.Sprintf("%s's argument must be a slice; is called with %s", fnName, typ.String()), 129 SuggestedFixes: fixes, 130 }) 131 }) 132 return nil, nil 133 }