github.com/v2fly/tools@v0.100.0/internal/lsp/analysis/unusedparams/unusedparams.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 unusedparams defines an analyzer that checks for unused 6 // parameters of functions. 7 package unusedparams 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/types" 13 "strings" 14 15 "github.com/v2fly/tools/go/analysis" 16 "github.com/v2fly/tools/go/analysis/passes/inspect" 17 "github.com/v2fly/tools/go/ast/inspector" 18 ) 19 20 const Doc = `check for unused parameters of functions 21 22 The unusedparams analyzer checks functions to see if there are 23 any parameters that are not being used. 24 25 To reduce false positives it ignores: 26 - methods 27 - parameters that do not have a name or are underscored 28 - functions in test files 29 - functions with empty bodies or those with just a return stmt` 30 31 var Analyzer = &analysis.Analyzer{ 32 Name: "unusedparams", 33 Doc: Doc, 34 Requires: []*analysis.Analyzer{inspect.Analyzer}, 35 Run: run, 36 } 37 38 type paramData struct { 39 field *ast.Field 40 ident *ast.Ident 41 typObj types.Object 42 } 43 44 func run(pass *analysis.Pass) (interface{}, error) { 45 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 46 nodeFilter := []ast.Node{ 47 (*ast.FuncDecl)(nil), 48 (*ast.FuncLit)(nil), 49 } 50 51 inspect.Preorder(nodeFilter, func(n ast.Node) { 52 var fieldList *ast.FieldList 53 var body *ast.BlockStmt 54 55 // Get the fieldList and body from the function node. 56 switch f := n.(type) { 57 case *ast.FuncDecl: 58 fieldList, body = f.Type.Params, f.Body 59 // TODO(golang/go#36602): add better handling for methods, if we enable methods 60 // we will get false positives if a struct is potentially implementing 61 // an interface. 62 if f.Recv != nil { 63 return 64 } 65 // Ignore functions in _test.go files to reduce false positives. 66 if file := pass.Fset.File(n.Pos()); file != nil && strings.HasSuffix(file.Name(), "_test.go") { 67 return 68 } 69 case *ast.FuncLit: 70 fieldList, body = f.Type.Params, f.Body 71 } 72 // If there are no arguments or the function is empty, then return. 73 if fieldList.NumFields() == 0 || body == nil || len(body.List) == 0 { 74 return 75 } 76 77 switch expr := body.List[0].(type) { 78 case *ast.ReturnStmt: 79 // Ignore functions that only contain a return statement to reduce false positives. 80 return 81 case *ast.ExprStmt: 82 callExpr, ok := expr.X.(*ast.CallExpr) 83 if !ok || len(body.List) > 1 { 84 break 85 } 86 // Ignore functions that only contain a panic statement to reduce false positives. 87 if fun, ok := callExpr.Fun.(*ast.Ident); ok && fun.Name == "panic" { 88 return 89 } 90 } 91 92 // Get the useful data from each field. 93 params := make(map[string]*paramData) 94 unused := make(map[*paramData]bool) 95 for _, f := range fieldList.List { 96 for _, i := range f.Names { 97 if i.Name == "_" { 98 continue 99 } 100 params[i.Name] = ¶mData{ 101 field: f, 102 ident: i, 103 typObj: pass.TypesInfo.ObjectOf(i), 104 } 105 unused[params[i.Name]] = true 106 } 107 } 108 109 // Traverse through the body of the function and 110 // check to see which parameters are unused. 111 ast.Inspect(body, func(node ast.Node) bool { 112 n, ok := node.(*ast.Ident) 113 if !ok { 114 return true 115 } 116 param, ok := params[n.Name] 117 if !ok { 118 return false 119 } 120 if nObj := pass.TypesInfo.ObjectOf(n); nObj != param.typObj { 121 return false 122 } 123 delete(unused, param) 124 return false 125 }) 126 127 // Create the reports for the unused parameters. 128 for u := range unused { 129 start, end := u.field.Pos(), u.field.End() 130 if len(u.field.Names) > 1 { 131 start, end = u.ident.Pos(), u.ident.End() 132 } 133 // TODO(golang/go#36602): Add suggested fixes to automatically 134 // remove the unused parameter. To start, just remove it from the 135 // function declaration. Later, remove it from every use of this 136 // function. 137 pass.Report(analysis.Diagnostic{ 138 Pos: start, 139 End: end, 140 Message: fmt.Sprintf("potentially unused parameter: '%s'", u.ident.Name), 141 }) 142 } 143 }) 144 return nil, nil 145 }