diff options
Diffstat (limited to 'gopls/internal/lsp/analysis/unusedparams/unusedparams.go')
-rw-r--r-- | gopls/internal/lsp/analysis/unusedparams/unusedparams.go | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/gopls/internal/lsp/analysis/unusedparams/unusedparams.go b/gopls/internal/lsp/analysis/unusedparams/unusedparams.go new file mode 100644 index 000000000..4c933c8fb --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams.go @@ -0,0 +1,152 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package unusedparams defines an analyzer that checks for unused +// parameters of functions. +package unusedparams + +import ( + "fmt" + "go/ast" + "go/types" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const Doc = `check for unused parameters of functions + +The unusedparams analyzer checks functions to see if there are +any parameters that are not being used. + +To reduce false positives it ignores: +- methods +- parameters that do not have a name or are underscored +- functions in test files +- functions with empty bodies or those with just a return stmt` + +var Analyzer = &analysis.Analyzer{ + Name: "unusedparams", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +type paramData struct { + field *ast.Field + ident *ast.Ident + typObj types.Object +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + var fieldList *ast.FieldList + var body *ast.BlockStmt + + // Get the fieldList and body from the function node. + switch f := n.(type) { + case *ast.FuncDecl: + fieldList, body = f.Type.Params, f.Body + // TODO(golang/go#36602): add better handling for methods, if we enable methods + // we will get false positives if a struct is potentially implementing + // an interface. + if f.Recv != nil { + return + } + // Ignore functions in _test.go files to reduce false positives. + if file := pass.Fset.File(n.Pos()); file != nil && strings.HasSuffix(file.Name(), "_test.go") { + return + } + case *ast.FuncLit: + fieldList, body = f.Type.Params, f.Body + } + // If there are no arguments or the function is empty, then return. + if fieldList.NumFields() == 0 || body == nil || len(body.List) == 0 { + return + } + + switch expr := body.List[0].(type) { + case *ast.ReturnStmt: + // Ignore functions that only contain a return statement to reduce false positives. + return + case *ast.ExprStmt: + callExpr, ok := expr.X.(*ast.CallExpr) + if !ok || len(body.List) > 1 { + break + } + // Ignore functions that only contain a panic statement to reduce false positives. + if fun, ok := callExpr.Fun.(*ast.Ident); ok && fun.Name == "panic" { + return + } + } + + // Get the useful data from each field. + params := make(map[string]*paramData) + unused := make(map[*paramData]bool) + for _, f := range fieldList.List { + for _, i := range f.Names { + if i.Name == "_" { + continue + } + params[i.Name] = ¶mData{ + field: f, + ident: i, + typObj: pass.TypesInfo.ObjectOf(i), + } + unused[params[i.Name]] = true + } + } + + // Traverse through the body of the function and + // check to see which parameters are unused. + ast.Inspect(body, func(node ast.Node) bool { + n, ok := node.(*ast.Ident) + if !ok { + return true + } + param, ok := params[n.Name] + if !ok { + return false + } + if nObj := pass.TypesInfo.ObjectOf(n); nObj != param.typObj { + return false + } + delete(unused, param) + return false + }) + + // Create the reports for the unused parameters. + for u := range unused { + start, end := u.field.Pos(), u.field.End() + if len(u.field.Names) > 1 { + start, end = u.ident.Pos(), u.ident.End() + } + // TODO(golang/go#36602): Add suggested fixes to automatically + // remove the unused parameter from every use of this + // function. + pass.Report(analysis.Diagnostic{ + Pos: start, + End: end, + Message: fmt.Sprintf("potentially unused parameter: '%s'", u.ident.Name), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: `Replace with "_"`, + TextEdits: []analysis.TextEdit{{ + Pos: u.ident.Pos(), + End: u.ident.End(), + NewText: []byte("_"), + }}, + }}, + }) + } + }) + return nil, nil +} |