github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/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 "github.com/powerman/golang-tools/go/analysis" 16 "github.com/powerman/golang-tools/go/analysis/passes/inspect" 17 "github.com/powerman/golang-tools/go/ast/inspector" 18 "github.com/powerman/golang-tools/internal/typeparams" 19 ) 20 21 const Doc = `check for constraints that could be simplified to "any"` 22 23 var Analyzer = &analysis.Analyzer{ 24 Name: "useany", 25 Doc: Doc, 26 Requires: []*analysis.Analyzer{inspect.Analyzer}, 27 Run: run, 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 if universeAny == nil { 35 // Go <= 1.17. Nothing to check. 36 return nil, nil 37 } 38 39 nodeFilter := []ast.Node{ 40 (*ast.TypeSpec)(nil), 41 (*ast.FuncType)(nil), 42 } 43 44 inspect.Preorder(nodeFilter, func(node ast.Node) { 45 var tparams *ast.FieldList 46 switch node := node.(type) { 47 case *ast.TypeSpec: 48 tparams = typeparams.ForTypeSpec(node) 49 case *ast.FuncType: 50 tparams = typeparams.ForFuncType(node) 51 default: 52 panic(fmt.Sprintf("unexpected node type %T", node)) 53 } 54 if tparams.NumFields() == 0 { 55 return 56 } 57 58 for _, field := range tparams.List { 59 typ := pass.TypesInfo.Types[field.Type].Type 60 if typ == nil { 61 continue // something is wrong, but not our concern 62 } 63 iface, ok := typ.Underlying().(*types.Interface) 64 if !ok { 65 continue // invalid constraint 66 } 67 68 // If the constraint is the empty interface, offer a fix to use 'any' 69 // instead. 70 if iface.Empty() { 71 id, _ := field.Type.(*ast.Ident) 72 if id != nil && pass.TypesInfo.Uses[id] == universeAny { 73 continue 74 } 75 76 diag := analysis.Diagnostic{ 77 Pos: field.Type.Pos(), 78 End: field.Type.End(), 79 Message: `could use "any" for this empty interface`, 80 } 81 82 // Only suggest a fix to 'any' if we actually resolve the predeclared 83 // any in this scope. 84 if scope := pass.TypesInfo.Scopes[node]; scope != nil { 85 if _, any := scope.LookupParent("any", token.NoPos); any == universeAny { 86 diag.SuggestedFixes = []analysis.SuggestedFix{{ 87 Message: `use "any"`, 88 TextEdits: []analysis.TextEdit{{ 89 Pos: field.Type.Pos(), 90 End: field.Type.End(), 91 NewText: []byte("any"), 92 }}, 93 }} 94 } 95 } 96 97 pass.Report(diag) 98 } 99 } 100 }) 101 return nil, nil 102 }