// 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 typesimport (.)// ----------------------------------------------------------------------------// API// A Struct represents a struct type.typeStructstruct { fields []*Var// fields != nil indicates the struct is set up (possibly with len(fields) == 0) tags []string// field tags; nil if there are no tags}// NewStruct returns a new struct with the given fields and corresponding field tags.// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be// only as long as required to hold the tag with the largest index i. Consequently,// if no field has a tag, tags may be nil.func ( []*Var, []string) *Struct {varobjsetfor , := range {if .name != "_" && .insert() != nil {panic("multiple fields with the same name") } }iflen() > len() {panic("more tags than fields") } := &Struct{fields: , tags: } .markComplete()return}// NumFields returns the number of fields in the struct (including blank and embedded fields).func ( *Struct) () int { returnlen(.fields) }// Field returns the i'th field for 0 <= i < NumFields().func ( *Struct) ( int) *Var { return .fields[] }// Tag returns the i'th field tag for 0 <= i < NumFields().func ( *Struct) ( int) string {if < len(.tags) {return .tags[] }return""}func ( *Struct) () Type { return }func ( *Struct) () string { returnTypeString(, nil) }// ----------------------------------------------------------------------------// Implementationfunc ( *Struct) () {if .fields == nil { .fields = make([]*Var, 0) }}func ( *Checker) ( *Struct, *ast.StructType) { := .Fieldsif == nil { .markComplete()return }// struct fields and tagsvar []*Varvar []string// for double-declaration checksvarobjset// current field typ and tagvarTypevarstring := func( *ast.Ident, bool) {if != "" && == nil { = make([]string, len()) }if != nil { = append(, ) } := .Pos() := .Name := NewField(, .pkg, , , )// spec: "Within a struct, non-blank field names must be unique."if == "_" || .declareInSet(&, , ) { = append(, ) .recordDef(, ) } }// addInvalid adds an embedded field of invalid type to the struct for // fields with errors; this keeps the number of struct fields in sync // with the source as long as the fields are _ or have different names // (go.dev/issue/25627). := func( *ast.Ident) { = Typ[Invalid] = "" (, true) }for , := range .List { = .varType(.Type) = .tag(.Tag)iflen(.Names) > 0 {// named fieldsfor , := range .Names { (, false) } } else {// embedded field // spec: "An embedded type must be specified as a type name T or as a // pointer to a non-interface type name *T, and T itself may not be a // pointer type." := .Type.Pos() // position of type, for errors := embeddedFieldIdent(.Type)if == nil { .errorf(.Type, InvalidSyntaxTree, "embedded field type %s has no name", .Type) = ast.NewIdent("_") .NamePos = ()continue } (, true) // struct{p.T} field has position of T// Because we have a name, typ must be of the form T or *T, where T is the name // of a (named or alias) type, and t (= deref(typ)) must be the type of T. // We must delay this check to the end because we don't want to instantiate // (via under(t)) a possibly incomplete type.// for use in the closure below := := .Type .later(func() { , := deref()switch u := under().(type) {case *Basic:if !isValid() {// error was reported beforereturn }// unsafe.Pointer is treated like a regular pointerif .kind == UnsafePointer { .error(, InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer") }case *Pointer: .error(, InvalidPtrEmbed, "embedded field type cannot be a pointer")case *Interface:ifisTypeParam() {// The error code here is inconsistent with other error codes for // invalid embedding, because this restriction may be relaxed in the // future, and so it did not warrant a new error code. .error(, MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter")break }if { .error(, InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface") } } }).describef(, "check embedded type %s", ) } } .fields = .tags = .markComplete()}func embeddedFieldIdent( ast.Expr) *ast.Ident {switch e := .(type) {case *ast.Ident:returncase *ast.StarExpr:// *T is valid, but **T is notif , := .X.(*ast.StarExpr); ! {return (.X) }case *ast.SelectorExpr:return .Selcase *ast.IndexExpr:return (.X)case *ast.IndexListExpr:return (.X) }returnnil// invalid embedded field}func ( *Checker) ( *objset, token.Pos, Object) bool {if := .insert(); != nil { := .newError(DuplicateDecl) .addf(atPos(), "%s redeclared", .Name()) .addAltDecl() .report()returnfalse }returntrue}func ( *Checker) ( *ast.BasicLit) string {if != nil {if .Kind == token.STRING {if , := strconv.Unquote(.Value); == nil {return } } .errorf(, InvalidSyntaxTree, "incorrect tag syntax: %q", .Value) }return""}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.