diff options
Diffstat (limited to 'internal/lsp/analysis/useany')
-rw-r--r-- | internal/lsp/analysis/useany/testdata/src/a/a.go | 25 | ||||
-rw-r--r-- | internal/lsp/analysis/useany/testdata/src/a/a.go.golden | 25 | ||||
-rw-r--r-- | internal/lsp/analysis/useany/useany.go | 102 | ||||
-rw-r--r-- | internal/lsp/analysis/useany/useany_test.go | 21 |
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") +} |