[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:
Rob Findley 2021-06-03 11:10:08 -04:00 committed by Robert Findley
parent ffc74ad5d3
commit 93a886a165
2 changed files with 154 additions and 144 deletions

154
src/go/types/struct.go Normal file
View 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 ""
}

View file

@ -12,7 +12,6 @@ import (
"go/constant"
"go/internal/typeparams"
"go/token"
"strconv"
"strings"
)
@ -747,146 +746,3 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, type0 ast
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
}