Skip to content

Commit

Permalink
go/types: add struct field with invalid type if field has errors
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
griesemer committed May 29, 2018
1 parent 79fbe92 commit b28e33d
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
41 changes: 41 additions & 0 deletions src/go/types/issues_test.go
Expand Up @@ -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
})
}
}
4 changes: 2 additions & 2 deletions src/go/types/testdata/decls3.src
Expand Up @@ -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
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/go/types/typexpr.go
Expand 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)
Expand All @@ -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)
Expand All @@ -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
}
}
Expand Down

0 comments on commit b28e33d

Please sign in to comment.