|
// Copyright 2021 The Go Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
//go:build go1.18 |
|
|
|
package main |
|
|
|
import ( |
|
"fmt" |
|
"go/ast" |
|
"go/importer" |
|
"go/parser" |
|
"go/token" |
|
"go/types" |
|
"log" |
|
) |
|
|
|
const hello = ` |
|
//!+input |
|
package main |
|
|
|
type Constraint interface { |
|
Value() any |
|
} |
|
|
|
type Pair[L, R any] struct { |
|
left L |
|
right R |
|
} |
|
|
|
func MakePair[L, R Constraint](l L, r R) Pair[L, R] { |
|
return Pair[L, R]{l, r} |
|
} |
|
//!-input |
|
` |
|
|
|
// !+print |
|
func PrintTypeParams(fset *token.FileSet, file *ast.File) error { |
|
conf := types.Config{Importer: importer.Default()} |
|
info := &types.Info{ |
|
Scopes: make(map[ast.Node]*types.Scope), |
|
Defs: make(map[*ast.Ident]types.Object), |
|
} |
|
_, err := conf.Check("hello", fset, []*ast.File{file}, info) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// For convenience, we can use ast.Inspect to find the nodes we want to |
|
// investigate. |
|
ast.Inspect(file, func(n ast.Node) bool { |
|
var name *ast.Ident // the name of the generic object, or nil |
|
var tparamFields *ast.FieldList // the list of type parameter fields |
|
var tparams *types.TypeParamList // the list of type parameter types |
|
var scopeNode ast.Node // the node associated with the declaration scope |
|
|
|
switch n := n.(type) { |
|
case *ast.TypeSpec: |
|
name = n.Name |
|
tparamFields = n.TypeParams |
|
tparams = info.Defs[name].Type().(*types.Named).TypeParams() |
|
scopeNode = n |
|
case *ast.FuncDecl: |
|
name = n.Name |
|
tparamFields = n.Type.TypeParams |
|
tparams = info.Defs[name].Type().(*types.Signature).TypeParams() |
|
scopeNode = n.Type |
|
default: |
|
// Not a type or function declaration. |
|
return true |
|
} |
|
|
|
// Option 1: find type parameters by looking at their declaring field list. |
|
if tparamFields != nil { |
|
fmt.Printf("%s has a type parameter field list with %d fields\n", name.Name, tparamFields.NumFields()) |
|
for _, field := range tparamFields.List { |
|
for _, name := range field.Names { |
|
tparam := info.Defs[name] |
|
fmt.Printf(" field %s defines an object %q\n", name.Name, tparam) |
|
} |
|
} |
|
} else { |
|
fmt.Printf("%s does not have a type parameter list\n", name.Name) |
|
} |
|
|
|
// Option 2: find type parameters via the TypeParams() method on the |
|
// generic type. |
|
if tparams.Len() > 0 { |
|
fmt.Printf("%s has %d type parameters:\n", name.Name, tparams.Len()) |
|
for i := 0; i < tparams.Len(); i++ { |
|
tparam := tparams.At(i) |
|
fmt.Printf(" %s has constraint %s\n", tparam, tparam.Constraint()) |
|
} |
|
} else { |
|
fmt.Printf("%s does not have type parameters\n", name.Name) |
|
} |
|
|
|
// Option 3: find type parameters by looking in the declaration scope. |
|
scope, ok := info.Scopes[scopeNode] |
|
if ok { |
|
fmt.Printf("%s has a scope with %d objects:\n", name.Name, scope.Len()) |
|
for _, name := range scope.Names() { |
|
fmt.Printf(" %s is a %T\n", name, scope.Lookup(name)) |
|
} |
|
} else { |
|
fmt.Printf("%s does not have a scope\n", name.Name) |
|
} |
|
|
|
return true |
|
}) |
|
return nil |
|
} |
|
|
|
//!-print |
|
|
|
/* |
|
//!+output |
|
> go run golang.org/x/tools/internal/typeparams/example/findtypeparams |
|
Constraint does not have a type parameter list |
|
Constraint does not have type parameters |
|
Constraint does not have a scope |
|
Pair has a type parameter field list with 2 fields |
|
field L defines an object "type parameter L any" |
|
field R defines an object "type parameter R any" |
|
Pair has 2 type parameters: |
|
L has constraint any |
|
R has constraint any |
|
Pair has a scope with 2 objects: |
|
L is a *types.TypeName |
|
R is a *types.TypeName |
|
MakePair has a type parameter field list with 2 fields |
|
field L defines an object "type parameter L hello.Constraint" |
|
field R defines an object "type parameter R hello.Constraint" |
|
MakePair has 2 type parameters: |
|
L has constraint hello.Constraint |
|
R has constraint hello.Constraint |
|
MakePair has a scope with 4 objects: |
|
L is a *types.TypeName |
|
R is a *types.TypeName |
|
l is a *types.Var |
|
r is a *types.Var |
|
//!-output |
|
*/ |
|
|
|
func main() { |
|
// Parse one file. |
|
fset := token.NewFileSet() |
|
f, err := parser.ParseFile(fset, "hello.go", hello, 0) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
if err := PrintTypeParams(fset, f); err != nil { |
|
log.Fatal(err) |
|
} |
|
} |