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  }