mirror of
https://github.com/golang/go
synced 2024-11-24 12:40:12 -07:00
cmd/doc, go/doc: add basic support for generic code
Update cmd/doc and go/doc for the generics, by adding handling for type parameters and the new embedded interface elements. Specifically: - Format type parameters when summarizing type and function nodes. - Find the origin type name for instantiation expressions, so that methods are associated with generic type declarations. - Generalize the handling of embedding 'error' in interfaces to arbitrary predeclared types. - Keep embedded type literals. - Update filtering to descend into embedded type literals. Also add "any" to the list of predeclared types. Updates #49210 Change-Id: I6ea82869f19c3cdbc3c842f01581c8fc7e1c2ee7 Reviewed-on: https://go-review.googlesource.com/c/go/+/359778 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
901bf291bc
commit
00d6d2037e
@ -323,7 +323,8 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
|
|||||||
if n.Assign.IsValid() {
|
if n.Assign.IsValid() {
|
||||||
sep = " = "
|
sep = " = "
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
|
tparams := pkg.formatTypeParams(n.TypeParams, depth)
|
||||||
|
return fmt.Sprintf("type %s%s%s%s", n.Name.Name, tparams, sep, pkg.oneLineNodeDepth(n.Type, depth))
|
||||||
|
|
||||||
case *ast.FuncType:
|
case *ast.FuncType:
|
||||||
var params []string
|
var params []string
|
||||||
@ -342,15 +343,16 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tparam := pkg.formatTypeParams(n.TypeParams, depth)
|
||||||
param := joinStrings(params)
|
param := joinStrings(params)
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
return fmt.Sprintf("func(%s)", param)
|
return fmt.Sprintf("func%s(%s)", tparam, param)
|
||||||
}
|
}
|
||||||
result := joinStrings(results)
|
result := joinStrings(results)
|
||||||
if !needParens {
|
if !needParens {
|
||||||
return fmt.Sprintf("func(%s) %s", param, result)
|
return fmt.Sprintf("func%s(%s) %s", tparam, param, result)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("func(%s) (%s)", param, result)
|
return fmt.Sprintf("func%s(%s) (%s)", tparam, param, result)
|
||||||
|
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
if n.Fields == nil || len(n.Fields.List) == 0 {
|
if n.Fields == nil || len(n.Fields.List) == 0 {
|
||||||
@ -419,6 +421,17 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pkg *Package) formatTypeParams(list *ast.FieldList, depth int) string {
|
||||||
|
if list.NumFields() == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var tparams []string
|
||||||
|
for _, field := range list.List {
|
||||||
|
tparams = append(tparams, pkg.oneLineField(field, depth))
|
||||||
|
}
|
||||||
|
return "[" + joinStrings(tparams) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
// oneLineField returns a one-line summary of the field.
|
// oneLineField returns a one-line summary of the field.
|
||||||
func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
|
func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
|
||||||
var names []string
|
var names []string
|
||||||
|
@ -79,18 +79,15 @@ func hasExportedName(list []*ast.Ident) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeErrorField removes anonymous fields named "error" from an interface.
|
// removeAnonymousField removes anonymous fields named name from an interface.
|
||||||
// This is called when "error" has been determined to be a local name,
|
func removeAnonymousField(name string, ityp *ast.InterfaceType) {
|
||||||
// not the predeclared type.
|
|
||||||
//
|
|
||||||
func removeErrorField(ityp *ast.InterfaceType) {
|
|
||||||
list := ityp.Methods.List // we know that ityp.Methods != nil
|
list := ityp.Methods.List // we know that ityp.Methods != nil
|
||||||
j := 0
|
j := 0
|
||||||
for _, field := range list {
|
for _, field := range list {
|
||||||
keepField := true
|
keepField := true
|
||||||
if n := len(field.Names); n == 0 {
|
if n := len(field.Names); n == 0 {
|
||||||
// anonymous field
|
// anonymous field
|
||||||
if fname, _ := baseTypeName(field.Type); fname == "error" {
|
if fname, _ := baseTypeName(field.Type); fname == name {
|
||||||
keepField = false
|
keepField = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,16 +116,25 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp
|
|||||||
for _, field := range list {
|
for _, field := range list {
|
||||||
keepField := false
|
keepField := false
|
||||||
if n := len(field.Names); n == 0 {
|
if n := len(field.Names); n == 0 {
|
||||||
// anonymous field
|
// anonymous field or embedded type or union element
|
||||||
fname := r.recordAnonymousField(parent, field.Type)
|
fname := r.recordAnonymousField(parent, field.Type)
|
||||||
if token.IsExported(fname) {
|
if fname != "" {
|
||||||
keepField = true
|
if token.IsExported(fname) {
|
||||||
} else if ityp != nil && fname == "error" {
|
keepField = true
|
||||||
// possibly the predeclared error interface; keep
|
} else if ityp != nil && predeclaredTypes[fname] {
|
||||||
// it for now but remember this interface so that
|
// possibly an embedded predeclared type; keep it for now but
|
||||||
// it can be fixed if error is also defined locally
|
// remember this interface so that it can be fixed if name is also
|
||||||
keepField = true
|
// defined locally
|
||||||
r.remember(ityp)
|
keepField = true
|
||||||
|
r.remember(fname, ityp)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we're operating on an interface, assume that this is an embedded
|
||||||
|
// type or union element.
|
||||||
|
//
|
||||||
|
// TODO(rfindley): consider traversing into approximation/unions
|
||||||
|
// elements to see if they are entirely unexported.
|
||||||
|
keepField = ityp != nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
field.Names = filterIdentList(field.Names)
|
field.Names = filterIdentList(field.Names)
|
||||||
@ -172,6 +178,17 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) {
|
|||||||
// nothing to do
|
// nothing to do
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
r.filterType(nil, t.X)
|
r.filterType(nil, t.X)
|
||||||
|
case *ast.StarExpr: // possibly an embedded type literal
|
||||||
|
r.filterType(nil, t.X)
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
if t.Op == token.TILDE { // approximation element
|
||||||
|
r.filterType(nil, t.X)
|
||||||
|
}
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
if t.Op == token.OR { // union
|
||||||
|
r.filterType(nil, t.X)
|
||||||
|
r.filterType(nil, t.Y)
|
||||||
|
}
|
||||||
case *ast.ArrayType:
|
case *ast.ArrayType:
|
||||||
r.filterType(nil, t.Elt)
|
r.filterType(nil, t.Elt)
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
@ -179,6 +196,7 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) {
|
|||||||
t.Incomplete = true
|
t.Incomplete = true
|
||||||
}
|
}
|
||||||
case *ast.FuncType:
|
case *ast.FuncType:
|
||||||
|
r.filterParamList(t.TypeParams)
|
||||||
r.filterParamList(t.Params)
|
r.filterParamList(t.Params)
|
||||||
r.filterParamList(t.Results)
|
r.filterParamList(t.Results)
|
||||||
case *ast.InterfaceType:
|
case *ast.InterfaceType:
|
||||||
@ -219,12 +237,16 @@ func (r *reader) filterSpec(spec ast.Spec) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ast.TypeSpec:
|
case *ast.TypeSpec:
|
||||||
|
// Don't filter type parameters here, by analogy with function parameters
|
||||||
|
// which are not filtered for top-level function declarations.
|
||||||
if name := s.Name.Name; token.IsExported(name) {
|
if name := s.Name.Name; token.IsExported(name) {
|
||||||
r.filterType(r.lookupType(s.Name.Name), s.Type)
|
r.filterType(r.lookupType(s.Name.Name), s.Type)
|
||||||
return true
|
return true
|
||||||
} else if name == "error" {
|
} else if IsPredeclared(name) {
|
||||||
// special case: remember that error is declared locally
|
if r.shadowedPredecl == nil {
|
||||||
r.errorDecl = true
|
r.shadowedPredecl = make(map[string]bool)
|
||||||
|
}
|
||||||
|
r.shadowedPredecl[name] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -34,6 +34,8 @@ func matchDecl(d *ast.GenDecl, f Filter) bool {
|
|||||||
if f(v.Name.Name) {
|
if f(v.Name.Name) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// We don't match ordinary parameters in filterFuncs, so by analogy don't
|
||||||
|
// match type parameters here.
|
||||||
switch t := v.Type.(type) {
|
switch t := v.Type.(type) {
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
if matchFields(t.Fields, f) {
|
if matchFields(t.Fields, f) {
|
||||||
|
@ -101,6 +101,10 @@ func baseTypeName(x ast.Expr) (name string, imported bool) {
|
|||||||
switch t := x.(type) {
|
switch t := x.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
return t.Name, false
|
return t.Name, false
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
return baseTypeName(t.X)
|
||||||
|
case *ast.IndexListExpr:
|
||||||
|
return baseTypeName(t.X)
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
if _, ok := t.X.(*ast.Ident); ok {
|
if _, ok := t.X.(*ast.Ident); ok {
|
||||||
// only possible for qualified type names;
|
// only possible for qualified type names;
|
||||||
@ -112,7 +116,7 @@ func baseTypeName(x ast.Expr) (name string, imported bool) {
|
|||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
return baseTypeName(t.X)
|
return baseTypeName(t.X)
|
||||||
}
|
}
|
||||||
return
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// An embeddedSet describes a set of embedded types.
|
// An embeddedSet describes a set of embedded types.
|
||||||
@ -163,9 +167,9 @@ type reader struct {
|
|||||||
types map[string]*namedType
|
types map[string]*namedType
|
||||||
funcs methodSet
|
funcs methodSet
|
||||||
|
|
||||||
// support for package-local error type declarations
|
// support for package-local shadowing of predeclared types
|
||||||
errorDecl bool // if set, type "error" was declared locally
|
shadowedPredecl map[string]bool
|
||||||
fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
|
fixmap map[string][]*ast.InterfaceType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) isVisible(name string) bool {
|
func (r *reader) isVisible(name string) bool {
|
||||||
@ -224,8 +228,11 @@ func (r *reader) readDoc(comment *ast.CommentGroup) {
|
|||||||
r.doc += "\n" + text
|
r.doc += "\n" + text
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) remember(typ *ast.InterfaceType) {
|
func (r *reader) remember(predecl string, typ *ast.InterfaceType) {
|
||||||
r.fixlist = append(r.fixlist, typ)
|
if r.fixmap == nil {
|
||||||
|
r.fixmap = make(map[string][]*ast.InterfaceType)
|
||||||
|
}
|
||||||
|
r.fixmap[predecl] = append(r.fixmap[predecl], typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
func specNames(specs []ast.Spec) []string {
|
func specNames(specs []ast.Spec) []string {
|
||||||
@ -679,10 +686,11 @@ func (r *reader) computeMethodSets() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if error was declared locally, don't treat it as exported field anymore
|
// For any predeclared names that are declared locally, don't treat them as
|
||||||
if r.errorDecl {
|
// exported fields anymore.
|
||||||
for _, ityp := range r.fixlist {
|
for predecl := range r.shadowedPredecl {
|
||||||
removeErrorField(ityp)
|
for _, ityp := range r.fixmap[predecl] {
|
||||||
|
removeAnonymousField(predecl, ityp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -869,6 +877,7 @@ func IsPredeclared(s string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var predeclaredTypes = map[string]bool{
|
var predeclaredTypes = map[string]bool{
|
||||||
|
"any": true,
|
||||||
"bool": true,
|
"bool": true,
|
||||||
"byte": true,
|
"byte": true,
|
||||||
"complex64": true,
|
"complex64": true,
|
||||||
|
70
src/go/doc/testdata/generics.0.golden
vendored
Normal file
70
src/go/doc/testdata/generics.0.golden
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Package generics contains the new syntax supporting generic ...
|
||||||
|
PACKAGE generics
|
||||||
|
|
||||||
|
IMPORTPATH
|
||||||
|
testdata/generics
|
||||||
|
|
||||||
|
FILENAMES
|
||||||
|
testdata/generics.go
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
// AnotherFunc has an implicit constraint interface. Neither type ...
|
||||||
|
func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
|
||||||
|
|
||||||
|
// Func has an instantiated constraint.
|
||||||
|
func Func[T Constraint[string, Type[int]]]()
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
// AFuncType demonstrates filtering of parameters and type ...
|
||||||
|
type AFuncType[T ~struct{ f int }] func(_ struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
})
|
||||||
|
|
||||||
|
// Constraint is a constraint interface with two type parameters.
|
||||||
|
type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
|
||||||
|
~int | ~byte | Type[string]
|
||||||
|
M() P
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEmbeddings demonstrates how we filter the new embedded ...
|
||||||
|
type NewEmbeddings interface {
|
||||||
|
string // should not be filtered
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
~struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
*struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
} | ~struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
// contains filtered or unexported methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameterized types should be shown.
|
||||||
|
type Type[P any] struct {
|
||||||
|
Field P
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables with an instantiated type should be shown.
|
||||||
|
var X Type[int]
|
||||||
|
|
||||||
|
// Constructors for parameterized types should be shown.
|
||||||
|
func Constructor[lowerCase any]() Type[lowerCase]
|
||||||
|
|
||||||
|
// MethodA uses a different name for its receiver type parameter.
|
||||||
|
func (t Type[A]) MethodA(p A)
|
||||||
|
|
||||||
|
// MethodB has a blank receiver type parameter.
|
||||||
|
func (t Type[_]) MethodB()
|
||||||
|
|
||||||
|
// MethodC has a lower-case receiver type parameter.
|
||||||
|
func (t Type[c]) MethodC()
|
||||||
|
|
60
src/go/doc/testdata/generics.1.golden
vendored
Normal file
60
src/go/doc/testdata/generics.1.golden
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Package generics contains the new syntax supporting generic ...
|
||||||
|
PACKAGE generics
|
||||||
|
|
||||||
|
IMPORTPATH
|
||||||
|
testdata/generics
|
||||||
|
|
||||||
|
FILENAMES
|
||||||
|
testdata/generics.go
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
// AnotherFunc has an implicit constraint interface. Neither type ...
|
||||||
|
func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
|
||||||
|
|
||||||
|
// Func has an instantiated constraint.
|
||||||
|
func Func[T Constraint[string, Type[int]]]()
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
// AFuncType demonstrates filtering of parameters and type ...
|
||||||
|
type AFuncType[T ~struct{ f int }] func(_ struct{ f int })
|
||||||
|
|
||||||
|
// Constraint is a constraint interface with two type parameters.
|
||||||
|
type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
|
||||||
|
~int | ~byte | Type[string]
|
||||||
|
M() P
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEmbeddings demonstrates how we filter the new embedded ...
|
||||||
|
type NewEmbeddings interface {
|
||||||
|
string // should not be filtered
|
||||||
|
int16
|
||||||
|
struct{ f int }
|
||||||
|
~struct{ f int }
|
||||||
|
*struct{ f int }
|
||||||
|
struct{ f int } | ~struct{ f int }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameterized types should be shown.
|
||||||
|
type Type[P any] struct {
|
||||||
|
Field P
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables with an instantiated type should be shown.
|
||||||
|
var X Type[int]
|
||||||
|
|
||||||
|
// Constructors for parameterized types should be shown.
|
||||||
|
func Constructor[lowerCase any]() Type[lowerCase]
|
||||||
|
|
||||||
|
// MethodA uses a different name for its receiver type parameter.
|
||||||
|
func (t Type[A]) MethodA(p A)
|
||||||
|
|
||||||
|
// MethodB has a blank receiver type parameter.
|
||||||
|
func (t Type[_]) MethodB()
|
||||||
|
|
||||||
|
// MethodC has a lower-case receiver type parameter.
|
||||||
|
func (t Type[c]) MethodC()
|
||||||
|
|
||||||
|
// int16 shadows the predeclared type int16.
|
||||||
|
type int16 int
|
||||||
|
|
70
src/go/doc/testdata/generics.2.golden
vendored
Normal file
70
src/go/doc/testdata/generics.2.golden
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Package generics contains the new syntax supporting generic ...
|
||||||
|
PACKAGE generics
|
||||||
|
|
||||||
|
IMPORTPATH
|
||||||
|
testdata/generics
|
||||||
|
|
||||||
|
FILENAMES
|
||||||
|
testdata/generics.go
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
// AnotherFunc has an implicit constraint interface. Neither type ...
|
||||||
|
func AnotherFunc[T ~struct{ f int }](_ struct{ f int })
|
||||||
|
|
||||||
|
// Func has an instantiated constraint.
|
||||||
|
func Func[T Constraint[string, Type[int]]]()
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
// AFuncType demonstrates filtering of parameters and type ...
|
||||||
|
type AFuncType[T ~struct{ f int }] func(_ struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
})
|
||||||
|
|
||||||
|
// Constraint is a constraint interface with two type parameters.
|
||||||
|
type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
|
||||||
|
~int | ~byte | Type[string]
|
||||||
|
M() P
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEmbeddings demonstrates how we filter the new embedded ...
|
||||||
|
type NewEmbeddings interface {
|
||||||
|
string // should not be filtered
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
~struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
*struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
} | ~struct {
|
||||||
|
// contains filtered or unexported fields
|
||||||
|
}
|
||||||
|
// contains filtered or unexported methods
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameterized types should be shown.
|
||||||
|
type Type[P any] struct {
|
||||||
|
Field P
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables with an instantiated type should be shown.
|
||||||
|
var X Type[int]
|
||||||
|
|
||||||
|
// Constructors for parameterized types should be shown.
|
||||||
|
func Constructor[lowerCase any]() Type[lowerCase]
|
||||||
|
|
||||||
|
// MethodA uses a different name for its receiver type parameter.
|
||||||
|
func (t Type[A]) MethodA(p A)
|
||||||
|
|
||||||
|
// MethodB has a blank receiver type parameter.
|
||||||
|
func (t Type[_]) MethodB()
|
||||||
|
|
||||||
|
// MethodC has a lower-case receiver type parameter.
|
||||||
|
func (t Type[c]) MethodC()
|
||||||
|
|
61
src/go/doc/testdata/generics.go
vendored
Normal file
61
src/go/doc/testdata/generics.go
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Package generics contains the new syntax supporting generic programming in
|
||||||
|
// Go.
|
||||||
|
package generics
|
||||||
|
|
||||||
|
// Variables with an instantiated type should be shown.
|
||||||
|
var X Type[int]
|
||||||
|
|
||||||
|
// Parameterized types should be shown.
|
||||||
|
type Type[P any] struct {
|
||||||
|
Field P
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructors for parameterized types should be shown.
|
||||||
|
func Constructor[lowerCase any]() Type[lowerCase] {
|
||||||
|
return Type[lowerCase]{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodA uses a different name for its receiver type parameter.
|
||||||
|
func (t Type[A]) MethodA(p A) {}
|
||||||
|
|
||||||
|
// MethodB has a blank receiver type parameter.
|
||||||
|
func (t Type[_]) MethodB() {}
|
||||||
|
|
||||||
|
// MethodC has a lower-case receiver type parameter.
|
||||||
|
func (t Type[c]) MethodC() {}
|
||||||
|
|
||||||
|
// Constraint is a constraint interface with two type parameters.
|
||||||
|
type Constraint[P, Q interface{ string | ~int | Type[int] }] interface {
|
||||||
|
~int | ~byte | Type[string]
|
||||||
|
M() P
|
||||||
|
}
|
||||||
|
|
||||||
|
// int16 shadows the predeclared type int16.
|
||||||
|
type int16 int
|
||||||
|
|
||||||
|
// NewEmbeddings demonstrates how we filter the new embedded elements.
|
||||||
|
type NewEmbeddings interface {
|
||||||
|
string // should not be filtered
|
||||||
|
int16
|
||||||
|
struct{ f int }
|
||||||
|
~struct{ f int }
|
||||||
|
*struct{ f int }
|
||||||
|
struct{ f int } | ~struct{ f int }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func has an instantiated constraint.
|
||||||
|
func Func[T Constraint[string, Type[int]]]() {}
|
||||||
|
|
||||||
|
// AnotherFunc has an implicit constraint interface.
|
||||||
|
//
|
||||||
|
// Neither type parameters nor regular parameters should be filtered.
|
||||||
|
func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) {}
|
||||||
|
|
||||||
|
// AFuncType demonstrates filtering of parameters and type parameters. Here we
|
||||||
|
// don't filter type parameters (to be consistent with function declarations),
|
||||||
|
// but DO filter the RHS.
|
||||||
|
type AFuncType[T ~struct{ f int }] func(_ struct{ f int })
|
Loading…
Reference in New Issue
Block a user