diff options
Diffstat (limited to 'gopls/internal/lsp/analysis/unusedparams')
6 files changed, 394 insertions, 0 deletions
diff --git a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go new file mode 100644 index 000000000..23e4122c4 --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go @@ -0,0 +1,55 @@ +// Copyright 2022 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 a + +import ( + "bytes" + "fmt" + "net/http" +) + +type parent interface { + n(f bool) +} + +type yuh struct { + a int +} + +func (y *yuh) n(f bool) { + for i := 0; i < 10; i++ { + fmt.Println(i) + } +} + +func a(i1 int, i2 int, i3 int) int { // want "potentially unused parameter: 'i2'" + i3 += i1 + _ = func(z int) int { // want "potentially unused parameter: 'z'" + _ = 1 + return 1 + } + return i3 +} + +func b(c bytes.Buffer) { // want "potentially unused parameter: 'c'" + _ = 1 +} + +func z(h http.ResponseWriter, _ *http.Request) { // want "potentially unused parameter: 'h'" + fmt.Println("Before") +} + +func l(h http.Handler) http.Handler { + return http.HandlerFunc(z) +} + +func mult(a, b int) int { // want "potentially unused parameter: 'b'" + a += 1 + return a +} + +func y(a int) { + panic("yo") +} diff --git a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden new file mode 100644 index 000000000..e28a6bdea --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden @@ -0,0 +1,55 @@ +// Copyright 2022 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 a + +import ( + "bytes" + "fmt" + "net/http" +) + +type parent interface { + n(f bool) +} + +type yuh struct { + a int +} + +func (y *yuh) n(f bool) { + for i := 0; i < 10; i++ { + fmt.Println(i) + } +} + +func a(i1 int, _ int, i3 int) int { // want "potentially unused parameter: 'i2'" + i3 += i1 + _ = func(_ int) int { // want "potentially unused parameter: 'z'" + _ = 1 + return 1 + } + return i3 +} + +func b(_ bytes.Buffer) { // want "potentially unused parameter: 'c'" + _ = 1 +} + +func z(_ http.ResponseWriter, _ *http.Request) { // want "potentially unused parameter: 'h'" + fmt.Println("Before") +} + +func l(h http.Handler) http.Handler { + return http.HandlerFunc(z) +} + +func mult(a, _ int) int { // want "potentially unused parameter: 'b'" + a += 1 + return a +} + +func y(a int) { + panic("yo") +} diff --git a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go new file mode 100644 index 000000000..93af2681b --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go @@ -0,0 +1,55 @@ +// Copyright 2022 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 typeparams + +import ( + "bytes" + "fmt" + "net/http" +) + +type parent[T any] interface { + n(f T) +} + +type yuh[T any] struct { + a T +} + +func (y *yuh[int]) n(f bool) { + for i := 0; i < 10; i++ { + fmt.Println(i) + } +} + +func a[T comparable](i1 int, i2 T, i3 int) int { // want "potentially unused parameter: 'i2'" + i3 += i1 + _ = func(z int) int { // want "potentially unused parameter: 'z'" + _ = 1 + return 1 + } + return i3 +} + +func b[T any](c bytes.Buffer) { // want "potentially unused parameter: 'c'" + _ = 1 +} + +func z[T http.ResponseWriter](h T, _ *http.Request) { // want "potentially unused parameter: 'h'" + fmt.Println("Before") +} + +func l(h http.Handler) http.Handler { + return http.HandlerFunc(z[http.ResponseWriter]) +} + +func mult(a, b int) int { // want "potentially unused parameter: 'b'" + a += 1 + return a +} + +func y[T any](a T) { + panic("yo") +} diff --git a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden new file mode 100644 index 000000000..c86bf289a --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden @@ -0,0 +1,55 @@ +// Copyright 2022 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 typeparams + +import ( + "bytes" + "fmt" + "net/http" +) + +type parent[T any] interface { + n(f T) +} + +type yuh[T any] struct { + a T +} + +func (y *yuh[int]) n(f bool) { + for i := 0; i < 10; i++ { + fmt.Println(i) + } +} + +func a[T comparable](i1 int, _ T, i3 int) int { // want "potentially unused parameter: 'i2'" + i3 += i1 + _ = func(_ int) int { // want "potentially unused parameter: 'z'" + _ = 1 + return 1 + } + return i3 +} + +func b[T any](_ bytes.Buffer) { // want "potentially unused parameter: 'c'" + _ = 1 +} + +func z[T http.ResponseWriter](_ T, _ *http.Request) { // want "potentially unused parameter: 'h'" + fmt.Println("Before") +} + +func l(h http.Handler) http.Handler { + return http.HandlerFunc(z[http.ResponseWriter]) +} + +func mult(a, _ int) int { // want "potentially unused parameter: 'b'" + a += 1 + return a +} + +func y[T any](a T) { + panic("yo") +} 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 +} diff --git a/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go b/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go new file mode 100644 index 000000000..fdd43b821 --- /dev/null +++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go @@ -0,0 +1,22 @@ +// 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_test + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" + "golang.org/x/tools/gopls/internal/lsp/analysis/unusedparams" + "golang.org/x/tools/internal/typeparams" +) + +func Test(t *testing.T) { + testdata := analysistest.TestData() + tests := []string{"a"} + if typeparams.Enabled { + tests = append(tests, "typeparams") + } + analysistest.RunWithSuggestedFixes(t, testdata, unusedparams.Analyzer, tests...) +} |