mirror of
https://github.com/golang/go
synced 2024-11-13 15:30:22 -07:00
[dev.typeparams] go/types: move struct checking into separate file
This is a port of CL 321589 to go/types. Specifically, the same checker methods were moved. Change-Id: If07d96faa77d2f9409d8895f970149c42cbfe440 Reviewed-on: https://go-review.googlesource.com/c/go/+/324753 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
ffc74ad5d3
commit
93a886a165
154
src/go/types/struct.go
Normal file
154
src/go/types/struct.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// 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 ""
|
||||||
|
}
|
@ -12,7 +12,6 @@ import (
|
|||||||
"go/constant"
|
"go/constant"
|
||||||
"go/internal/typeparams"
|
"go/internal/typeparams"
|
||||||
"go/token"
|
"go/token"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -747,146 +746,3 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, type0 ast
|
|||||||
|
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user