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.
5 // Package unusedparams defines an analyzer that checks for unused
6 // parameters of functions.
15 "golang.org/x/tools/go/analysis"
16 "golang.org/x/tools/go/analysis/passes/inspect"
17 "golang.org/x/tools/go/ast/inspector"
20 const Doc = `check for unused parameters of functions
22 The unusedparams analyzer checks functions to see if there are
23 any parameters that are not being used.
25 To reduce false positives it ignores:
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`
31 var Analyzer = &analysis.Analyzer{
34 Requires: []*analysis.Analyzer{inspect.Analyzer},
38 type paramData struct {
44 func run(pass *analysis.Pass) (interface{}, error) {
45 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
46 nodeFilter := []ast.Node{
51 inspect.Preorder(nodeFilter, func(n ast.Node) {
52 var fieldList *ast.FieldList
53 var body *ast.BlockStmt
55 // Get the fieldList and body from the function node.
56 switch f := n.(type) {
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
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") {
70 fieldList, body = f.Type.Params, f.Body
72 // If there are no arguments or the function is empty, then return.
73 if fieldList.NumFields() == 0 || len(body.List) == 0 {
77 switch expr := body.List[0].(type) {
79 // Ignore functions that only contain a return statement to reduce false positives.
82 callExpr, ok := expr.X.(*ast.CallExpr)
83 if !ok || len(body.List) > 1 {
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" {
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 {
100 params[i.Name] = ¶mData{
103 typObj: pass.TypesInfo.ObjectOf(i),
105 unused[params[i.Name]] = true
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)
116 param, ok := params[n.Name]
120 if nObj := pass.TypesInfo.ObjectOf(n); nObj != param.typObj {
123 delete(unused, param)
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()
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
137 pass.Report(analysis.Diagnostic{
140 Message: fmt.Sprintf("potentially unused parameter: '%s'", u.ident.Name),