-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/lsp/analysis: add a useany analyzer
Add an analyzer that checks for empty interfaces in constraint position, that could instead use the new predeclared "any" type. Change-Id: I6c11f74c479c2cba64b3b12e61d70d157f94393b Reviewed-on: https://go-review.googlesource.com/c/tools/+/351549 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]>
- Loading branch information
Showing
9 changed files
with
205 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters