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  }