github.com/v2fly/tools@v0.100.0/internal/lsp/analysis/simplifyslice/simplifyslice.go (about) 1 // Copyright 2020 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 simplifyslice defines an Analyzer that simplifies slice statements. 6 // https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go 7 // https://golang.org/cmd/gofmt/#hdr-The_simplify_command 8 package simplifyslice 9 10 import ( 11 "bytes" 12 "fmt" 13 "go/ast" 14 "go/printer" 15 16 "github.com/v2fly/tools/go/analysis" 17 "github.com/v2fly/tools/go/analysis/passes/inspect" 18 "github.com/v2fly/tools/go/ast/inspector" 19 ) 20 21 const Doc = `check for slice simplifications 22 23 A slice expression of the form: 24 s[a:len(s)] 25 will be simplified to: 26 s[a:] 27 28 This is one of the simplifications that "gofmt -s" applies.` 29 30 var Analyzer = &analysis.Analyzer{ 31 Name: "simplifyslice", 32 Doc: Doc, 33 Requires: []*analysis.Analyzer{inspect.Analyzer}, 34 Run: run, 35 } 36 37 // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] 38 // but we leave them as is since sometimes we want to be very explicit 39 // about the lower bound. 40 // An example where the 0 helps: 41 // x, y, z := b[0:2], b[2:4], b[4:6] 42 // An example where it does not: 43 // x, y := b[:n], b[n:] 44 45 func run(pass *analysis.Pass) (interface{}, error) { 46 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 47 nodeFilter := []ast.Node{ 48 (*ast.SliceExpr)(nil), 49 } 50 inspect.Preorder(nodeFilter, func(n ast.Node) { 51 expr := n.(*ast.SliceExpr) 52 // - 3-index slices always require the 2nd and 3rd index 53 if expr.Max != nil { 54 return 55 } 56 s, ok := expr.X.(*ast.Ident) 57 // the array/slice object is a single, resolved identifier 58 if !ok || s.Obj == nil { 59 return 60 } 61 call, ok := expr.High.(*ast.CallExpr) 62 // the high expression is a function call with a single argument 63 if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() { 64 return 65 } 66 fun, ok := call.Fun.(*ast.Ident) 67 // the function called is "len" and it is not locally defined; and 68 // because we don't have dot imports, it must be the predefined len() 69 if !ok || fun.Name != "len" || fun.Obj != nil { 70 return 71 } 72 arg, ok := call.Args[0].(*ast.Ident) 73 // the len argument is the array/slice object 74 if !ok || arg.Obj != s.Obj { 75 return 76 } 77 var b bytes.Buffer 78 printer.Fprint(&b, pass.Fset, expr.High) 79 pass.Report(analysis.Diagnostic{ 80 Pos: expr.High.Pos(), 81 End: expr.High.End(), 82 Message: fmt.Sprintf("unneeded: %s", b.String()), 83 SuggestedFixes: []analysis.SuggestedFix{{ 84 Message: fmt.Sprintf("Remove '%s'", b.String()), 85 TextEdits: []analysis.TextEdit{{ 86 Pos: expr.High.Pos(), 87 End: expr.High.End(), 88 NewText: []byte{}, 89 }}, 90 }}, 91 }) 92 }) 93 return nil, nil 94 }