golang.org/x/tools@v0.21.0/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/analysis/passes/internal/analysisutil" 19 "golang.org/x/tools/go/ast/inspector" 20 "golang.org/x/tools/go/types/typeutil" 21 ) 22 23 const Doc = `check the argument type of sort.Slice 24 25 sort.Slice requires an argument of a slice type. Check that 26 the interface{} value passed to sort.Slice is actually a slice.` 27 28 var Analyzer = &analysis.Analyzer{ 29 Name: "sortslice", 30 Doc: Doc, 31 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/sortslice", 32 Requires: []*analysis.Analyzer{inspect.Analyzer}, 33 Run: run, 34 } 35 36 func run(pass *analysis.Pass) (interface{}, error) { 37 if !analysisutil.Imports(pass.Pkg, "sort") { 38 return nil, nil // doesn't directly import sort 39 } 40 41 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 42 43 nodeFilter := []ast.Node{ 44 (*ast.CallExpr)(nil), 45 } 46 47 inspect.Preorder(nodeFilter, func(n ast.Node) { 48 call := n.(*ast.CallExpr) 49 fn, _ := typeutil.Callee(pass.TypesInfo, call).(*types.Func) 50 if !analysisutil.IsFunctionNamed(fn, "sort", "Slice", "SliceStable", "SliceIsSorted") { 51 return 52 } 53 54 arg := call.Args[0] 55 typ := pass.TypesInfo.Types[arg].Type 56 57 if tuple, ok := typ.(*types.Tuple); ok { 58 typ = tuple.At(0).Type() // special case for Slice(f(...)) 59 } 60 61 switch typ.Underlying().(type) { 62 case *types.Slice, *types.Interface: 63 return 64 } 65 66 // Restore typ to the original type, we may unwrap the tuple above, 67 // typ might not be the type of arg. 68 typ = pass.TypesInfo.Types[arg].Type 69 70 var fixes []analysis.SuggestedFix 71 switch v := typ.Underlying().(type) { 72 case *types.Array: 73 var buf bytes.Buffer 74 format.Node(&buf, pass.Fset, &ast.SliceExpr{ 75 X: arg, 76 Slice3: false, 77 Lbrack: arg.End() + 1, 78 Rbrack: arg.End() + 3, 79 }) 80 fixes = append(fixes, analysis.SuggestedFix{ 81 Message: "Get a slice of the full array", 82 TextEdits: []analysis.TextEdit{{ 83 Pos: arg.Pos(), 84 End: arg.End(), 85 NewText: buf.Bytes(), 86 }}, 87 }) 88 case *types.Pointer: 89 _, ok := v.Elem().Underlying().(*types.Slice) 90 if !ok { 91 break 92 } 93 var buf bytes.Buffer 94 format.Node(&buf, pass.Fset, &ast.StarExpr{ 95 X: arg, 96 }) 97 fixes = append(fixes, analysis.SuggestedFix{ 98 Message: "Dereference the pointer to the slice", 99 TextEdits: []analysis.TextEdit{{ 100 Pos: arg.Pos(), 101 End: arg.End(), 102 NewText: buf.Bytes(), 103 }}, 104 }) 105 case *types.Signature: 106 if v.Params().Len() != 0 || v.Results().Len() != 1 { 107 break 108 } 109 if _, ok := v.Results().At(0).Type().Underlying().(*types.Slice); !ok { 110 break 111 } 112 var buf bytes.Buffer 113 format.Node(&buf, pass.Fset, &ast.CallExpr{ 114 Fun: arg, 115 }) 116 fixes = append(fixes, analysis.SuggestedFix{ 117 Message: "Call the function", 118 TextEdits: []analysis.TextEdit{{ 119 Pos: arg.Pos(), 120 End: arg.End(), 121 NewText: buf.Bytes(), 122 }}, 123 }) 124 } 125 126 pass.Report(analysis.Diagnostic{ 127 Pos: call.Pos(), 128 End: call.End(), 129 Message: fmt.Sprintf("%s's argument must be a slice; is called with %s", fn.FullName(), typ.String()), 130 SuggestedFixes: fixes, 131 }) 132 }) 133 return nil, nil 134 }