aboutsummaryrefslogtreecommitdiff
path: root/internal/lsp/analysis/useany
diff options
context:
space:
mode:
Diffstat (limited to 'internal/lsp/analysis/useany')
-rw-r--r--internal/lsp/analysis/useany/testdata/src/a/a.go25
-rw-r--r--internal/lsp/analysis/useany/testdata/src/a/a.go.golden25
-rw-r--r--internal/lsp/analysis/useany/useany.go102
-rw-r--r--internal/lsp/analysis/useany/useany_test.go21
4 files changed, 173 insertions, 0 deletions
diff --git a/internal/lsp/analysis/useany/testdata/src/a/a.go b/internal/lsp/analysis/useany/testdata/src/a/a.go
new file mode 100644
index 000000000..22d693150
--- /dev/null
+++ b/internal/lsp/analysis/useany/testdata/src/a/a.go
@@ -0,0 +1,25 @@
+// Copyright 2021 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.
+
+// This file contains tests for the useany checker.
+
+package a
+
+type Any interface{}
+
+func _[T interface{}]() {} // want "could use \"any\" for this empty interface"
+func _[X any, T interface{}]() {} // want "could use \"any\" for this empty interface"
+func _[any interface{}]() {} // want "could use \"any\" for this empty interface"
+func _[T Any]() {} // want "could use \"any\" for this empty interface"
+func _[T interface{ int | interface{} }]() {} // want "could use \"any\" for this empty interface"
+func _[T interface{ int | Any }]() {} // want "could use \"any\" for this empty interface"
+func _[T any]() {}
+
+type _[T interface{}] int // want "could use \"any\" for this empty interface"
+type _[X any, T interface{}] int // want "could use \"any\" for this empty interface"
+type _[any interface{}] int // want "could use \"any\" for this empty interface"
+type _[T Any] int // want "could use \"any\" for this empty interface"
+type _[T interface{ int | interface{} }] int // want "could use \"any\" for this empty interface"
+type _[T interface{ int | Any }] int // want "could use \"any\" for this empty interface"
+type _[T any] int
diff --git a/internal/lsp/analysis/useany/testdata/src/a/a.go.golden b/internal/lsp/analysis/useany/testdata/src/a/a.go.golden
new file mode 100644
index 000000000..efd8fd640
--- /dev/null
+++ b/internal/lsp/analysis/useany/testdata/src/a/a.go.golden
@@ -0,0 +1,25 @@
+// Copyright 2021 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.
+
+// This file contains tests for the useany checker.
+
+package a
+
+type Any interface{}
+
+func _[T any]() {} // want "could use \"any\" for this empty interface"
+func _[X any, T any]() {} // want "could use \"any\" for this empty interface"
+func _[any interface{}]() {} // want "could use \"any\" for this empty interface"
+func _[T any]() {} // want "could use \"any\" for this empty interface"
+func _[T any]() {} // want "could use \"any\" for this empty interface"
+func _[T any]() {} // want "could use \"any\" for this empty interface"
+func _[T any]() {}
+
+type _[T any] int // want "could use \"any\" for this empty interface"
+type _[X any, T any] int // want "could use \"any\" for this empty interface"
+type _[any interface{}] int // want "could use \"any\" for this empty interface"
+type _[T any] int // want "could use \"any\" for this empty interface"
+type _[T any] int // want "could use \"any\" for this empty interface"
+type _[T any] int // want "could use \"any\" for this empty interface"
+type _[T any] int
diff --git a/internal/lsp/analysis/useany/useany.go b/internal/lsp/analysis/useany/useany.go
new file mode 100644
index 000000000..73e2f7633
--- /dev/null
+++ b/internal/lsp/analysis/useany/useany.go
@@ -0,0 +1,102 @@
+// Copyright 2021 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 useany defines an Analyzer that checks for usage of interface{} in
+// constraints, rather than the predeclared any.
+package useany
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
+)
+
+const Doc = `check for constraints that could be simplified to "any"`
+
+var Analyzer = &analysis.Analyzer{
+ Name: "useany",
+ Doc: Doc,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ Run: run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+ universeAny := types.Universe.Lookup("any")
+ if universeAny == nil {
+ // Go <= 1.17. Nothing to check.
+ return nil, nil
+ }
+
+ nodeFilter := []ast.Node{
+ (*ast.TypeSpec)(nil),
+ (*ast.FuncType)(nil),
+ }
+
+ inspect.Preorder(nodeFilter, func(node ast.Node) {
+ var tparams *ast.FieldList
+ switch node := node.(type) {
+ case *ast.TypeSpec:
+ tparams = typeparams.ForTypeSpec(node)
+ case *ast.FuncType:
+ tparams = typeparams.ForFuncType(node)
+ default:
+ panic(fmt.Sprintf("unexpected node type %T", node))
+ }
+ if tparams.NumFields() == 0 {
+ return
+ }
+
+ for _, field := range tparams.List {
+ typ := pass.TypesInfo.Types[field.Type].Type
+ if typ == nil {
+ continue // something is wrong, but not our concern
+ }
+ iface, ok := typ.Underlying().(*types.Interface)
+ if !ok {
+ continue // invalid constraint
+ }
+
+ // If the constraint is the empty interface, offer a fix to use 'any'
+ // instead.
+ if iface.Empty() {
+ id, _ := field.Type.(*ast.Ident)
+ if id != nil && pass.TypesInfo.Uses[id] == universeAny {
+ continue
+ }
+
+ diag := analysis.Diagnostic{
+ Pos: field.Type.Pos(),
+ End: field.Type.End(),
+ Message: `could use "any" for this empty interface`,
+ }
+
+ // Only suggest a fix to 'any' if we actually resolve the predeclared
+ // any in this scope.
+ if scope := pass.TypesInfo.Scopes[node]; scope != nil {
+ if _, any := scope.LookupParent("any", token.NoPos); any == universeAny {
+ diag.SuggestedFixes = []analysis.SuggestedFix{{
+ Message: `use "any"`,
+ TextEdits: []analysis.TextEdit{{
+ Pos: field.Type.Pos(),
+ End: field.Type.End(),
+ NewText: []byte("any"),
+ }},
+ }}
+ }
+ }
+
+ pass.Report(diag)
+ }
+ }
+ })
+ return nil, nil
+}
diff --git a/internal/lsp/analysis/useany/useany_test.go b/internal/lsp/analysis/useany/useany_test.go
new file mode 100644
index 000000000..535d91526
--- /dev/null
+++ b/internal/lsp/analysis/useany/useany_test.go
@@ -0,0 +1,21 @@
+// Copyright 2021 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 useany_test
+
+import (
+ "testing"
+
+ "golang.org/x/tools/go/analysis/analysistest"
+ "golang.org/x/tools/internal/lsp/analysis/useany"
+ "golang.org/x/tools/internal/typeparams"
+)
+
+func Test(t *testing.T) {
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled")
+ }
+ testdata := analysistest.TestData()
+ analysistest.RunWithSuggestedFixes(t, testdata, useany.Analyzer, "a")
+}