github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/typeparams/example/findtypeparams/main.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 //go:build go1.18 6 // +build go1.18 7 8 package main 9 10 import ( 11 "fmt" 12 "go/ast" 13 "go/importer" 14 "go/parser" 15 "go/token" 16 "go/types" 17 "log" 18 ) 19 20 const hello = ` 21 //!+input 22 package main 23 24 type Constraint interface { 25 Value() interface{} 26 } 27 28 type Pair[L, R any] struct { 29 left L 30 right R 31 } 32 33 func MakePair[L, R Constraint](l L, r R) Pair[L, R] { 34 return Pair[L, R]{l, r} 35 } 36 //!-input 37 ` 38 39 //!+print 40 func PrintTypeParams(fset *token.FileSet, file *ast.File) error { 41 conf := types.Config{Importer: importer.Default()} 42 info := &types.Info{ 43 Scopes: make(map[ast.Node]*types.Scope), 44 Defs: make(map[*ast.Ident]types.Object), 45 } 46 _, err := conf.Check("hello", fset, []*ast.File{file}, info) 47 if err != nil { 48 return err 49 } 50 51 // For convenience, we can use ast.Inspect to find the nodes we want to 52 // investigate. 53 ast.Inspect(file, func(n ast.Node) bool { 54 var name *ast.Ident // the name of the generic object, or nil 55 var tparamSyntax *ast.FieldList // the list of type parameter fields 56 var tparamTypes *types.TypeParamList // the list of type parameter types 57 var scopeNode ast.Node // the node associated with the declaration scope 58 59 switch n := n.(type) { 60 case *ast.TypeSpec: 61 name = n.Name 62 tparamSyntax = n.TypeParams 63 tparamTypes = info.Defs[name].Type().(*types.Named).TypeParams() 64 name = n.Name 65 scopeNode = n 66 case *ast.FuncDecl: 67 name = n.Name 68 tparamSyntax = n.Type.TypeParams 69 tparamTypes = info.Defs[name].Type().(*types.Signature).TypeParams() 70 scopeNode = n.Type 71 } 72 73 if name == nil { 74 return true // not a generic object 75 } 76 77 // Option 1: find type parameters by looking at their declaring field list. 78 if tparamSyntax != nil { 79 fmt.Printf("%s has a type parameter field list with %d fields\n", name.Name, tparamSyntax.NumFields()) 80 for _, field := range tparamSyntax.List { 81 for _, name := range field.Names { 82 tparam := info.Defs[name] 83 fmt.Printf(" field %s defines an object %q\n", name.Name, tparam) 84 } 85 } 86 } else { 87 fmt.Printf("%s does not have a type parameter list\n", name.Name) 88 } 89 90 // Option 2: find type parameters via the TypeParams() method on the 91 // generic type. 92 fmt.Printf("%s has %d type parameters:\n", name.Name, tparamTypes.Len()) 93 for i := 0; i < tparamTypes.Len(); i++ { 94 tparam := tparamTypes.At(i) 95 fmt.Printf(" %s has constraint %s\n", tparam, tparam.Constraint()) 96 } 97 98 // Option 3: find type parameters by looking in the declaration scope. 99 scope, ok := info.Scopes[scopeNode] 100 if ok { 101 fmt.Printf("%s has a scope with %d objects:\n", name.Name, scope.Len()) 102 for _, name := range scope.Names() { 103 fmt.Printf(" %s is a %T\n", name, scope.Lookup(name)) 104 } 105 } else { 106 fmt.Printf("%s does not have a scope\n", name.Name) 107 } 108 109 return true 110 }) 111 return nil 112 } 113 114 //!-print 115 116 /* 117 //!+output 118 > go run github.com/powerman/golang-tools/internal/typeparams/example/findtypeparams 119 Constraint does not have a type parameter list 120 Constraint has 0 type parameters: 121 Constraint does not have a scope 122 Pair has a type parameter field list with 2 fields 123 field L defines an object "type parameter L any" 124 field R defines an object "type parameter R any" 125 Pair has 2 type parameters: 126 L has constraint any 127 R has constraint any 128 Pair has a scope with 2 objects: 129 L is a *types.TypeName 130 R is a *types.TypeName 131 MakePair has a type parameter field list with 2 fields 132 field L defines an object "type parameter L hello.Constraint" 133 field R defines an object "type parameter R hello.Constraint" 134 MakePair has 2 type parameters: 135 L has constraint hello.Constraint 136 R has constraint hello.Constraint 137 MakePair has a scope with 4 objects: 138 L is a *types.TypeName 139 R is a *types.TypeName 140 l is a *types.Var 141 r is a *types.Var 142 //!-output 143 */ 144 145 func main() { 146 // Parse one file. 147 fset := token.NewFileSet() 148 f, err := parser.ParseFile(fset, "hello.go", hello, 0) 149 if err != nil { 150 log.Fatal(err) // parse error 151 } 152 if err := PrintTypeParams(fset, f); err != nil { 153 log.Fatal(err) // type error 154 } 155 }