go/types: add struct field with invalid type if field has errors

This ensures that all struct fields are present and thus the struct
has the original number of fields even if some fields have type
errors. (This only applies as long as the field names themselves
don't conflict.)

Fixes #25627.

Change-Id: I2414b1f432ce139b3cd2776ff0d46d8dcf38b650
Reviewed-on: https://go-review.googlesource.com/115115
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2018-05-29 13:13:24 -07:00
parent 79fbe92b7e
commit b28e33d371
3 changed files with 60 additions and 2 deletions

View File

@ -314,3 +314,44 @@ func TestIssue22525(t *testing.T) {
t.Errorf("got: %swant: %s", got, want)
}
}
func TestIssue25627(t *testing.T) {
const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T `
// The src strings (without prefix) are constructed such that the number of semicolons
// plus one corresponds to the number of fields expected in the respective struct.
for _, src := range []string{
`struct { x Missing }`,
`struct { Missing }`,
`struct { *Missing }`,
`struct { unsafe.Pointer }`,
`struct { P }`,
`struct { *I }`,
`struct { a int; b Missing; *Missing }`,
} {
f, err := parser.ParseFile(fset, "", prefix+src, 0)
if err != nil {
t.Fatal(err)
}
cfg := Config{Importer: importer.Default(), Error: func(err error) {}}
info := &Info{Types: make(map[ast.Expr]TypeAndValue)}
_, err = cfg.Check(f.Name.Name, fset, []*ast.File{f}, info)
if err != nil {
if _, ok := err.(Error); !ok {
t.Fatal(err)
}
}
ast.Inspect(f, func(n ast.Node) bool {
if spec, _ := n.(*ast.TypeSpec); spec != nil {
if tv, ok := info.Types[spec.Type]; ok && spec.Name.Name == "T" {
want := strings.Count(src, ";") + 1
if got := tv.Type.(*Struct).NumFields(); got != want {
t.Errorf("%s: got %d fields; want %d", src, got, want)
}
}
}
return true
})
}
}

View File

@ -99,9 +99,9 @@ func _() {
// unsafe.Pointers are treated like regular pointers when embedded
type T2 struct {
unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer
*/* ERROR "cannot be unsafe.Pointer" */ unsafe.Pointer
*/* ERROR "cannot be unsafe.Pointer" */ /* ERROR "Pointer redeclared" */ unsafe.Pointer
UP /* ERROR "cannot be unsafe.Pointer" */
* /* ERROR "cannot be unsafe.Pointer" */ UP
* /* ERROR "cannot be unsafe.Pointer" */ /* ERROR "UP redeclared" */ UP
}
}

View File

@ -677,6 +677,16 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
}
}
// 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.typExpr(f.Type, nil, path)
tag = check.tag(f.Tag)
@ -693,6 +703,9 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
name := embeddedFieldIdent(f.Type)
if name == nil {
check.invalidAST(pos, "embedded field type %s has no name", f.Type)
name = ast.NewIdent("_")
name.NamePos = pos
addInvalid(name, pos)
continue
}
t, isPtr := deref(typ)
@ -702,22 +715,26 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
case *Basic:
if t == Typ[Invalid] {
// error was reported before
addInvalid(name, pos)
continue
}
// unsafe.Pointer is treated like a regular pointer
if t.kind == UnsafePointer {
check.errorf(pos, "embedded field type cannot be unsafe.Pointer")
addInvalid(name, pos)
continue
}
case *Pointer:
check.errorf(pos, "embedded field type cannot be a pointer")
addInvalid(name, pos)
continue
case *Interface:
if isPtr {
check.errorf(pos, "embedded field type cannot be a pointer to an interface")
addInvalid(name, pos)
continue
}
}