Commit b28e33d3 authored by Robert Griesemer's avatar Robert Griesemer

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/115115Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 79fbe92b
......@@ -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
})
}
}
......@@ -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
}
}
......
......@@ -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
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment