golang.org/x/tools/gopls@v0.15.3/internal/analysis/useany/useany.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package useany defines an Analyzer that checks for usage of interface{} in 6 // constraints, rather than the predeclared any. 7 package useany 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/token" 13 "go/types" 14 15 "golang.org/x/tools/go/analysis" 16 "golang.org/x/tools/go/analysis/passes/inspect" 17 "golang.org/x/tools/go/ast/inspector" 18 ) 19 20 const Doc = `check for constraints that could be simplified to "any"` 21 22 var Analyzer = &analysis.Analyzer{ 23 Name: "useany", 24 Doc: Doc, 25 Requires: []*analysis.Analyzer{inspect.Analyzer}, 26 Run: run, 27 URL: "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/useany", 28 } 29 30 func run(pass *analysis.Pass) (interface{}, error) { 31 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 32 33 universeAny := types.Universe.Lookup("any") 34 35 nodeFilter := []ast.Node{ 36 (*ast.TypeSpec)(nil), 37 (*ast.FuncType)(nil), 38 } 39 40 inspect.Preorder(nodeFilter, func(node ast.Node) { 41 var tparams *ast.FieldList 42 switch node := node.(type) { 43 case *ast.TypeSpec: 44 tparams = node.TypeParams 45 case *ast.FuncType: 46 tparams = node.TypeParams 47 default: 48 panic(fmt.Sprintf("unexpected node type %T", node)) 49 } 50 if tparams.NumFields() == 0 { 51 return 52 } 53 54 for _, field := range tparams.List { 55 typ := pass.TypesInfo.Types[field.Type].Type 56 if typ == nil { 57 continue // something is wrong, but not our concern 58 } 59 iface, ok := typ.Underlying().(*types.Interface) 60 if !ok { 61 continue // invalid constraint 62 } 63 64 // If the constraint is the empty interface, offer a fix to use 'any' 65 // instead. 66 if iface.Empty() { 67 id, _ := field.Type.(*ast.Ident) 68 if id != nil && pass.TypesInfo.Uses[id] == universeAny { 69 continue 70 } 71 72 diag := analysis.Diagnostic{ 73 Pos: field.Type.Pos(), 74 End: field.Type.End(), 75 Message: `could use "any" for this empty interface`, 76 } 77 78 // Only suggest a fix to 'any' if we actually resolve the predeclared 79 // any in this scope. 80 if scope := pass.TypesInfo.Scopes[node]; scope != nil { 81 if _, any := scope.LookupParent("any", token.NoPos); any == universeAny { 82 diag.SuggestedFixes = []analysis.SuggestedFix{{ 83 Message: `use "any"`, 84 TextEdits: []analysis.TextEdit{{ 85 Pos: field.Type.Pos(), 86 End: field.Type.End(), 87 NewText: []byte("any"), 88 }}, 89 }} 90 } 91 } 92 93 pass.Report(diag) 94 } 95 } 96 }) 97 return nil, nil 98 }