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
+ })
+ }
+}
// 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
}
}
}
}
+ // 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)
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)
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
}
}