--- /dev/null
+// 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 types
+
+import (
+ "go/ast"
+ "go/token"
+ "strconv"
+)
+
+func (check *Checker) structType(styp *Struct, e *ast.StructType) {
+ list := e.Fields
+ if list == nil {
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ add := func(ident *ast.Ident, embedded bool, pos token.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Name
+ fld := NewField(pos, check.pkg, name, typ, embedded)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ }
+
+ // 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
+ // (issue #25627).
+ addInvalid := func(ident *ast.Ident, pos token.Pos) {
+ typ = Typ[Invalid]
+ tag = ""
+ add(ident, true, pos)
+ }
+
+ for _, f := range list.List {
+ typ = check.varType(f.Type)
+ tag = check.tag(f.Tag)
+ if len(f.Names) > 0 {
+ // named fields
+ for _, name := range f.Names {
+ add(name, false, name.Pos())
+ }
+ } 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."
+ pos := f.Type.Pos()
+ name := embeddedFieldIdent(f.Type)
+ if name == nil {
+ // TODO(rFindley): using invalidAST here causes test failures (all
+ // errors should have codes). Clean this up.
+ check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type)
+ name = ast.NewIdent("_")
+ name.NamePos = pos
+ addInvalid(name, pos)
+ continue
+ }
+ add(name, true, pos)
+
+ // 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
+ embeddedTyp := typ
+ embeddedPos := f.Type
+
+ check.later(func() {
+ t, isPtr := deref(embeddedTyp)
+ switch t := optype(t).(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ return
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if t.kind == UnsafePointer {
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
+ }
+ case *Pointer:
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
+ case *Interface:
+ if isPtr {
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
+ }
+ }
+ })
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+}
+
+func embeddedFieldIdent(e ast.Expr) *ast.Ident {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e
+ case *ast.StarExpr:
+ // *T is valid, but **T is not
+ if _, ok := e.X.(*ast.StarExpr); !ok {
+ return embeddedFieldIdent(e.X)
+ }
+ case *ast.SelectorExpr:
+ return e.Sel
+ case *ast.IndexExpr:
+ return embeddedFieldIdent(e.X)
+ }
+ return nil // invalid embedded field
+}
+
+func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
+ check.reportAltDecl(alt)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
"go/constant"
"go/internal/typeparams"
"go/token"
- "strconv"
"strings"
)
return
}
-
-func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
- if alt := oset.insert(obj); alt != nil {
- check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
- check.reportAltDecl(alt)
- return false
- }
- return true
-}
-
-func (check *Checker) tag(t *ast.BasicLit) string {
- if t != nil {
- if t.Kind == token.STRING {
- if val, err := strconv.Unquote(t.Value); err == nil {
- return val
- }
- }
- check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
- }
- return ""
-}
-
-func (check *Checker) structType(styp *Struct, e *ast.StructType) {
- list := e.Fields
- if list == nil {
- return
- }
-
- // struct fields and tags
- var fields []*Var
- var tags []string
-
- // for double-declaration checks
- var fset objset
-
- // current field typ and tag
- var typ Type
- var tag string
- add := func(ident *ast.Ident, embedded bool, pos token.Pos) {
- if tag != "" && tags == nil {
- tags = make([]string, len(fields))
- }
- if tags != nil {
- tags = append(tags, tag)
- }
-
- name := ident.Name
- fld := NewField(pos, check.pkg, name, typ, embedded)
- // spec: "Within a struct, non-blank field names must be unique."
- if name == "_" || check.declareInSet(&fset, pos, fld) {
- fields = append(fields, fld)
- check.recordDef(ident, fld)
- }
- }
-
- // 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
- // (issue #25627).
- addInvalid := func(ident *ast.Ident, pos token.Pos) {
- typ = Typ[Invalid]
- tag = ""
- add(ident, true, pos)
- }
-
- for _, f := range list.List {
- typ = check.varType(f.Type)
- tag = check.tag(f.Tag)
- if len(f.Names) > 0 {
- // named fields
- for _, name := range f.Names {
- add(name, false, name.Pos())
- }
- } 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."
- pos := f.Type.Pos()
- name := embeddedFieldIdent(f.Type)
- if name == nil {
- // TODO(rFindley): using invalidAST here causes test failures (all
- // errors should have codes). Clean this up.
- check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type)
- name = ast.NewIdent("_")
- name.NamePos = pos
- addInvalid(name, pos)
- continue
- }
- add(name, true, pos)
-
- // 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
- embeddedTyp := typ
- embeddedPos := f.Type
-
- check.later(func() {
- t, isPtr := deref(embeddedTyp)
- switch t := optype(t).(type) {
- case *Basic:
- if t == Typ[Invalid] {
- // error was reported before
- return
- }
- // unsafe.Pointer is treated like a regular pointer
- if t.kind == UnsafePointer {
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
- }
- case *Pointer:
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
- case *Interface:
- if isPtr {
- check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
- }
- }
- })
- }
- }
-
- styp.fields = fields
- styp.tags = tags
-}
-
-func embeddedFieldIdent(e ast.Expr) *ast.Ident {
- switch e := e.(type) {
- case *ast.Ident:
- return e
- case *ast.StarExpr:
- // *T is valid, but **T is not
- if _, ok := e.X.(*ast.StarExpr); !ok {
- return embeddedFieldIdent(e.X)
- }
- case *ast.SelectorExpr:
- return e.Sel
- case *ast.IndexExpr:
- return embeddedFieldIdent(e.X)
- }
- return nil // invalid embedded field
-}